Beispiel #1
0
// If the names of all children have the form (PREFIX)(INDEX)(SUFFIX),
// return the common PREFIX and SUFFIX.
void DispValue::get_index_surroundings(string& prefix, string& suffix) const
{
    assert (nchildren() > 0);

    prefix = child(0)->full_name();
    suffix = child(0)->full_name();

    for (int i = 1; i < nchildren(); i++)
    {
	prefix = common_prefix(prefix, child(i)->full_name());
	suffix = common_suffix(suffix, child(i)->full_name());
    }
}
Beispiel #2
0
// Clear box caches for this and all children
void DispValue::clear_box_cache()
{
    clear_cached_box();

    for (int i = 0; i < nchildren(); i++)
	child(i)->clear_box_cache();
}
Beispiel #3
0
// Representation invariant
bool ListBox::OK() const
{
    assert (CompositeBox::OK());
    assert (nchildren() == 0 || nchildren() == 2);

    // _last may be inconsistent; it is recomputed before every access
    // assert (_last && _last->isEmpty());

    if (nchildren() == 2)
    {
	assert (head() && head()->OK());
	assert (tail() && tail()->OK());
    }

    return true;
}
Beispiel #4
0
int DispValue::nchildren_with_repeats() const
{
    int sum = 0;
    for (int i = 0; i < nchildren(); i++)
	sum += child(i)->repeats();
    return sum;
}
Beispiel #5
0
void DispValue::validate_box_cache()
{
    int i;
    for (i = 0; i < nchildren(); i++)
	child(i)->validate_box_cache();

    for (i = 0; i < nchildren(); i++)
    {
	if (child(i)->cached_box() == 0 ||
	    child(i)->_cached_box_change > _cached_box_change)
	{
	    clear_cached_box();
	    break;
	}
    }
}
// Check for equality
bool CompositeBox::matches (const Box &b, const Box *) const
{
    // Don't compare size and extend, as MatchBoxen have zero size
    if (strcmp(type(), b.type()))
	return false;

    const CompositeBox *c = (const CompositeBox *)&b;   // dirty trick
    if (nchildren() != c->nchildren())
	return false;

    for (int i = 0; i < nchildren(); i++)
	if (*((*this)[i]) != *((*c)[i]))
	    return false;

    return true;
}
// Count MatchBoxes
void CompositeBox::countMatchBoxes(int instances[]) const
{
    for (int i = 0; i < nchildren(); i++)
    {
	const Box *child = (*this)[i];
	child->countMatchBoxes(instances);
    }
}
TypeSpec
ASTindex::typecheck (TypeSpec expected)
{
    typecheck_children ();
    const char *indextype = "";
    TypeSpec t = lvalue()->typespec();
    if (t.is_structure()) {
        error ("Cannot use [] indexing on a struct");
        return TypeSpec();
    }
    if (t.is_closure()) {
        error ("Cannot use [] indexing on a closure");
        return TypeSpec();
    }
    if (index3()) {
        if (! t.is_array() && ! t.elementtype().is_matrix())
            error ("[][][] only valid for a matrix array");
        m_typespec = TypeDesc::FLOAT;
    } else if (t.is_array()) {
        indextype = "array";
        m_typespec = t.elementtype();
        if (index2()) {
            if (t.aggregate() == TypeDesc::SCALAR)
                error ("can't use [][] on a simple array");
            m_typespec = TypeDesc::FLOAT;
        }
    } else if (t.aggregate() == TypeDesc::VEC3) {
        indextype = "component";
        TypeDesc tnew = t.simpletype();
        tnew.aggregate = TypeDesc::SCALAR;
        tnew.vecsemantics = TypeDesc::NOXFORM;
        m_typespec = tnew;
        if (index2())
            error ("can't use [][] on a %s", type_c_str(t));
    } else if (t.aggregate() == TypeDesc::MATRIX44) {
        indextype = "component";
        TypeDesc tnew = t.simpletype();
        tnew.aggregate = TypeDesc::SCALAR;
        tnew.vecsemantics = TypeDesc::NOXFORM;
        m_typespec = tnew;
        if (! index2())
            error ("must use [][] on a matrix, not just []");
    } else {
        error ("can only use [] indexing for arrays or multi-component types");
        return TypeSpec();
    }

    // Make sure the indices (children 1+) are integers
    for (size_t c = 1;  c < nchildren();  ++c)
        if (! child(c)->typespec().is_int())
            error ("%s index must be an integer, not a %s", 
                   indextype, type_c_str(index()->typespec()));

    // If the thing we're indexing is an lvalue, so is the indexed element
    m_is_lvalue = lvalue()->is_lvalue();

    return m_typespec;
}
Beispiel #9
0
void
ASTvariable_ref::print (std::ostream &out, int indentlevel) const
{
    indent (out, indentlevel);
    out << "(" << nodetypename() << " (type: "
        << (m_sym ? m_sym->typespec().string() : "unknown") << ") " 
        << (m_sym ? m_sym->mangled() : m_name.string()) << ")\n";
    DASSERT (nchildren() == 0);
}
Beispiel #10
0
// Return height of entire tree
int DispValue::height() const
{
    int d = 0;

    for (int i = 0; i < nchildren(); i++)
	d = max(d, child(i)->height());

    return d + 1;
}
// Propagate font
void CompositeBox::newFont(const string& font)
{
    for (int i = 0; i < nchildren(); i++)
    {
	Box* child = (*this)[i];
	child->newFont(font);
    }
    resize();
}
// Recompute size
Box *CompositeBox::resize()
{
    for (int i = 0; i < nchildren(); i++)
    {
	Box* child = (*this)[i];
	child->resize();
    }
    return this;
}
Beispiel #13
0
// Count expanded nodes in tree
int DispValue::expandedAll() const
{
    int count = 0;
    if (expanded())
	count++;
    for (int i = 0; i < nchildren(); i++)
	count += child(i)->expandedAll();

    return count;
}
// Create string from Box
string CompositeBox::str() const
{
    string s("");
    for (int i = 0; i < nchildren(); i++)
    {
	const Box* child = (*this)[i];
	s += child->str();
    }
    return s;
}
Beispiel #15
0
// Count collapsed nodes in tree
int DispValue::collapsedAll() const
{
    int count = 0;
    if (collapsed())
	count++;
    for (int i = 0; i < nchildren(); i++)
	count += child(i)->collapsedAll();

    return count;
}
Beispiel #16
0
// Return true iff this or some descendant changed
bool DispValue::descendant_changed() const
{
    if (changed)
	return true;

    for (int i = 0; i < nchildren(); i++)
	if (child(i)->descendant_changed())
	    return true;

    return false;
}
Beispiel #17
0
// Expand.  Like expand(), but expand entire subtree
void DispValue::expandAll(int depth)
{
    if (depth == 0)
	return;

    _expand();

    for (int i = 0; i < nchildren(); i++)
    {
	child(i)->expandAll(depth - 1);
    }
}
Beispiel #18
0
// Collapse.  Like collapse(), but collapse entire subtree
void DispValue::collapseAll(int depth)
{
    if (depth == 0)
	return;

    _collapse();

    for (int i = 0; i < nchildren(); i++)
    {
	child(i)->collapseAll(depth - 1);
    }
}
// Find TagBox for point P
const TagBox *CompositeBox::findTag(const BoxPoint& p) const
{
    if (p != BoxPoint(-1, -1))
	for (int i = 0; i < nchildren(); i++)
	{
	    const Box *child = (*this)[i];
	    const TagBox *t = child->findTag(p);
	    if (t != 0)
		return t;   // found
	}
    return 0; // not found
}
// Dump
void CompositeBox::dumpComposite(std::ostream& s, 
				 const char *sep, const char *head, const char *tail) const
{
    s << head;
    for (int i = 0; i < nchildren(); i++)
    {
	const Box* child = (*this)[i];
	if (i > 0)
	    s << sep;
	s << *child;
    }
    s << tail;
}
Beispiel #21
0
//
// Constructor: reads taxonomy file and builds taxonomy as a DAG
//
Taxonomy::Taxonomy(LINT num_items,	// total number of items
		   LINT num_roots,	// number of roots
		   FLOAT fanout,	// average fanout
		   FLOAT depth_ratio	// average ratio of ....
		   )
  : nitems(num_items), nroots(num_roots), depth(depth_ratio)
{
  LINT i, j;
  LINT next_child;
  PoissonDist nchildren(fanout-1);	// string length
 
  // allocate memory
  par = new LINT [nitems];
  child_start = new LINT [nitems];
  child_end = new LINT [nitems];

  next_child = nroots;

  // initialize parents (or lack thereof) for roots
  for (i = 0; i < nroots; i++)
    par[i] = -1;

  // set up all the interior nodes
  for (i = 0, j = next_child; i < nitems && next_child < nitems; i++)
    {
      child_start[i] = next_child;
      next_child += nchildren() + 1;
      if (next_child > nitems) 
	next_child = nitems;
      child_end[i] = next_child;
      for (; j < next_child; j++)
	par[j] = i;
    }

  // initialize children (or lack thereof) for all the leaves
  for (; i < nitems; i++)
    child_start[i] = 
    child_end[i] = -1;
}
Beispiel #22
0
bool DispValue::can_plot2d() const
{
    if (type() == Array)
    {
	for (int i = 0; i < nchildren(); i++)
	{
	    if (!child(i)->can_plot1d())
		return false;
	}

	return true;
    }

    if (nchildren() > 0)
    {
	// If we have a list of indexed names, then we can plot in 2d.
	int i;
	string prefix, suffix;
	get_index_surroundings(prefix, suffix);
	
	for (i = 0; i < nchildren(); i++)
	{
	    string idx = child(i)->index(prefix, suffix);
	    if (!idx.matches(rxdouble) && !idx.matches(rxint))
		return false;
	}

	for (i = 0; i < nchildren(); i++)
	{
	    if (!child(i)->can_plot1d())
		return false;
	}

	return true;
    }

    return false;
}
Beispiel #23
0
// Destructor helper
void DispValue::clear()
{
    for (int i = 0; i < nchildren(); i++)
	child(i)->unlink();

    static const DispValueArray empty(0);
    _children = empty;

    if (plotter() != 0)
    {
	plotter()->terminate();
	_plotter = 0;
    }

    clear_cached_box();
}
Beispiel #24
0
// Return height of expanded tree
int DispValue::heightExpanded() const
{
    if (collapsed())
	return 0;

    int d = 0;

    for (int i = 0; i < nchildren(); i++)
    {
	if (child(i)->collapsed())
	    return 1;

	d = max(d, child(i)->heightExpanded());
    }

    return d + 1;
}
Beispiel #25
0
int DispValue::can_plot() const
{
    if (can_plot3d())
	return 3;

    if (can_plot2d())
	return 2;

    if (can_plot1d())
	return 1;

    // Search for plottable array children
    int ndim = 0;
    for (int i = 0; i < nchildren(); i++)
	ndim = max(ndim, child(i)->can_plot());

    return ndim;
}
Beispiel #26
0
bool DispValue::can_plot3d() const
{
    if (type() != Array)
	return false;

    int grandchildren = -1;
    for (int i = 0; i < nchildren(); i++)
    {
	if (!child(i)->can_plot2d())
	    return false;

	if (i == 0)
	    grandchildren = child(i)->nchildren_with_repeats();
	else if (child(i)->nchildren_with_repeats() != grandchildren)
	    return false;	// Differing number of grandchildren
    }

    return true;
}
Beispiel #27
0
DispValue *DispValue::_update(DispValue *source, 
			      bool& was_changed, bool& was_initialized)
{
    if (source == this)
    {
	// We're updated from ourselves -- ignore it all.  
	// This happens when a cluster is updated from the values of
	// the clustered dislays.
	if (descendant_changed())
	    was_changed = true;

	return this;
    }

    if (changed)
    {
	// Clear `changed' flag
	changed = false;
	was_changed = true;
    }

    if (source->enabled() != enabled())
    {
	myenabled = source->enabled();
	was_changed = true;

	// We don't set CHANGED to true since enabled/disabled changes
	// are merely a change in the view, not a change in the data.
    }

    if (source->full_name() == full_name() && source->type() == type())
    {
	switch (type())
	{
	case Simple:
	case Text:
	case Pointer:
	    // Atomic values
	    if (_value != source->value())
	    {
		_value = source->value();
		changed = was_changed = true;
	    }
	    return this;

	case Array:
	    // Array.  Check for 1st element, too.
	    if (_have_index_base != source->_have_index_base &&
		(_have_index_base && _index_base != source->_index_base))
		break;

	    // FALL THROUGH
	case Reference:
	case Sequence:
	    // Numbered children.  If size changed, we assume
	    // the whole has been changed.
	    if (nchildren() == source->nchildren())
	    {
		for (int i = 0; i < nchildren(); i++)
		{
		    // Update each child
		    _children[i] = child(i)->update(source->child(i),
						    was_changed,
						    was_initialized);
		}
		return this;
	    }
	    break;

	case List:
	case Struct:
	{
	    // Named children.  Check whether names are the same.
	    bool same_members = (nchildren() == source->nchildren());

	    for (int i = 0; same_members && i < nchildren(); i++)
	    {
		if (child(i)->full_name() != source->child(i)->full_name())
		    same_members = false;
	    }

	    if (same_members)
	    {
		// Update each child
		for (int i = 0; i < nchildren(); i++)
		{
		    _children[i] = child(i)->update(source->child(i),
						    was_changed,
						    was_initialized);
		}
		return this;
	    }

	    // Members have changed.
	    // Be sure to mark only those members that actually have changed
	    // (i.e. don't mark the entire struct and don't mark new members)
	    // We do so by creating a new list of children.  `Old' children
	    // that still are reported get updated; `new' children are added.
	    DispValueArray new_children;
	    DispValueArray processed_children;
	    for (int j = 0; j < source->nchildren(); j++)
	    {
		DispValue *c = 0;
		for (int i = 0; c == 0 && i < nchildren(); i++)
		{
		    bool processed = false;
		    for (int k = 0; k < processed_children.size(); k++)
		    {
			if (child(i) == processed_children[k])
			    processed = true;
		    }
		    if (processed)
			continue;

		    if (child(i)->full_name() == source->child(j)->full_name())
		    {
			c = child(i)->update(source->child(j),
					     was_changed,
					     was_initialized);
			processed_children += child(i);
		    }
		}

		if (c == 0)
		{
		    // Child not found -- use source child instead
		    c = source->child(j)->link();
		}

		new_children += c;
	    }
	    _children = new_children;
	    was_changed = was_initialized = true;
	    return this;
	}

	case UnknownType:
	    assert(0);
	    abort();
	}
    }

    // Type, name or structure have changed -- use SOURCE instead of original
    DispValue *ret = source->link();
    ret->changed = was_changed = was_initialized = true;

    // Copy the basic settings
    ret->myexpanded = expanded();
    ret->dereference(dereferenced());
    ret->set_orientation(orientation());
    ret->set_member_names(member_names());

    // Have new DispValue take over the plotter
    if (ret->plotter() == 0)
    {
	ret->_plotter = plotter();
	_plotter = 0;
    }

    unlink();
    return ret;
}
Beispiel #28
0
// Return true iff SOURCE and this are structurally equal.
// If SOURCE_DESCENDANT (a descendant of SOURCE) is set, 
// return its equivalent descendant of this in DESCENDANT.
bool DispValue::structurally_equal(const DispValue *source,
				   const DispValue *source_descendant,
				   const DispValue *&descendant) const
{
    if (source == source_descendant)
	descendant = this;

    if (type() != source->type())
	return false;		// Differing type

    switch (type())
    {
	case Simple:
	case Text:
	case Pointer:
	    return true;	// Structurally equal
		
	case Array:
	{
	    if (nchildren() != source->nchildren())
		return false;	// Differing size

	    if (_have_index_base != source->_have_index_base)
		return false;	// Differing base

	    if (_have_index_base && _index_base != source->_index_base)
		return false;	// Differing base

	    for (int i = 0; i < nchildren(); i++)
	    {
		DispValue *child = _children[i];
		DispValue *source_child = source->child(i);
		bool eq = child->structurally_equal(source_child, 
						    source_descendant,
						    descendant);

		if (!eq)
		    return false;
	    }
	    return true;	// All children structurally equal
	}

	case List:
	case Struct:
	case Sequence:
	case Reference:
	{
	    if (nchildren() != source->nchildren())
		return false;

	    for (int i = 0; i < nchildren(); i++)
	    {
		DispValue *child = _children[i];
		DispValue *source_child = source->child(i);
		bool eq = child->structurally_equal(source_child, 
						    source_descendant,
						    descendant);

		if (!eq)
		    return false;
	    }
	    return true;	// All children structurally equal
	}

	case UnknownType:
	    assert(0);
	    abort();
    }

    return false;		// Not found
}
Beispiel #29
0
// Initialization
void DispValue::init(DispValue *parent, int depth, string& value,
		     DispValueType given_type)
{
#if LOG_CREATE_VALUES
    std::clog << "Building value from " << quote(value) << "\n";
#endif

    // Be sure the value is not changed in memory
    value.consuming(true);

    const char *initial_value = value.chars();

    static const DispValueArray empty(0);
    _children = empty;

    if (background(value.length()))
    {
	clear();

	mytype = Simple;
	_value = "(Aborted)";
	value  = "Aborted\n";
	return;
    }

    mytype = given_type;
    if (mytype == UnknownType && 
	(parent == 0 || parent->type() == List) && print_name.empty())
	mytype = Text;
    if (mytype == UnknownType && parent == 0 && is_user_command(print_name))
	mytype = List;
    if (mytype == UnknownType)
	mytype = determine_type(value);

    bool ignore_repeats = (parent != 0 && parent->type() == Array);

    char perl_type = '\0';

    switch (mytype)
    {

    case Simple:
    {
	_value = read_simple_value(value, depth, ignore_repeats);
#if LOG_CREATE_VALUES
	std::clog << mytype << ": " << quote(_value) << "\n";
#endif
	perl_type = '$';
	break;
    }

    case Text:
    {
	// Read in a line of text
	if (value.contains('\n'))
	    _value = value.through('\n');
	else
	    _value = value;
	value = value.after('\n');
#if LOG_CREATE_VALUES
	std::clog << mytype << ": " << quote(_value) << "\n";
#endif
	perl_type = '$';
	break;
    }

    case Pointer:
    {
	_value = read_pointer_value(value, ignore_repeats);
	_dereferenced = false;

#if LOG_CREATE_VALUES
	std::clog << mytype << ": " << quote(_value) << "\n";
#endif
	// Hide vtable pointers.
	if (_value.contains("virtual table") || _value.contains("vtable"))
	    myexpanded = false;
	perl_type = '$';

	// In Perl, pointers may be followed by indented `pointed to'
	// info.  Skip this.
	if (gdb->type() == PERL)
	{
	    while (value.contains("\n  ", 0))
	    {
		value = value.after("\n  ");
		value = value.from("\n");
	    }
	}		
	break;
    }

    case Array:
    {
	string base = normalize_base(myfull_name);

	_orientation = app_data.array_orientation;

#if LOG_CREATE_VALUES
	std::clog << mytype << ": " << "\n";
#endif

	read_array_begin(value, myaddr);

	// Check for `vtable entries' prefix.
	string vtable_entries = read_vtable_entries(value);
	if (!vtable_entries.empty())
	{
	    _children += parse_child(depth, vtable_entries, myfull_name);
	}

	// Read the array elements.  Assume that the type is the
	// same across all elements.
	DispValueType member_type = UnknownType;
	if (!_have_index_base)
	{
	    _index_base = index_base(base, depth);
	    _have_index_base = true;
	}
	int array_index = _index_base;

	// The array has at least one element.  Otherwise, GDB
	// would treat it as a pointer.
	do {
	    const char *repeated_value = value.chars();
	    string member_name = 
		gdb->index_expr("", itostring(array_index++));
	    DispValue *dv = parse_child(depth, value,
					add_member_name(base, member_name), 
					member_name, member_type);
	    member_type = dv->type();
	    _children += dv;

	    int repeats = read_repeats(value);

	    if (expand_repeated_values)
	    {
		// Create one value per repeat
		while (--repeats > 0)
		{
		    member_name = 
			gdb->index_expr("", itostring(array_index++));
		    string val = repeated_value;
		    DispValue *repeated_dv = 
			parse_child(depth, val, 
				    add_member_name(base, member_name),
				    member_name, member_type);
		    _children += repeated_dv;
		}
	    }
	    else
	    {
		// Show repetition in member
		if (repeats > 1)
		{
		    array_index--;

#if 0
		    // We use the GDB `artificial array' notation here,
		    // since repeat recognition is supported in GDB only.
		    member_name += "@" + itostring(repeats);

		    dv->full_name() = add_member_name(base, member_name);
		    dv->name()      = member_name;
#endif
		    dv->repeats()   = repeats;

		    array_index += repeats;
		}
	    }

	    if (background(value.length()))
	    {
		init(parent, depth, value);
		return;
	    }
	} while (read_array_next(value));
	read_array_end(value);

	// Expand only if at top-level.
	myexpanded = (depth == 0 || nchildren() <= 1);

#if LOG_CREATE_VALUES
	std::clog << mytype << " has " << nchildren() << " members\n";
#endif
	perl_type = '@';
	break;
    }

    case List:
	// Some DBXes issue the local variables via a frame line, just
	// like `set_date(d = 0x10003060, day_of_week = Sat, day = 24,
	// month = 12, year = 1994)'.  Make this more readable.
	munch_dump_line(value);

	// FALL THROUGH
    case Struct:
    {
	_orientation  = app_data.struct_orientation;
	_member_names = app_data.show_member_names;

	bool found_struct_begin   = false;
	bool read_multiple_values = false;
	
#if LOG_CREATE_VALUES
	std::clog << mytype << " " << quote(myfull_name) << "\n";
#endif
	string member_prefix = myfull_name;
	string member_suffix = "";
	if (mytype == List)
	{
	    member_prefix = "";
	    read_multiple_values = true;
	}
	else
	{
	    // In C and Java, `*' binds tighter than `.'
	    if (member_prefix.contains('*', 0))
	    {
		if (gdb->program_language() == LANGUAGE_C)
		{
		    // Use the C `->' operator instead
		    member_prefix.del("*");
		    if (member_prefix.contains('(', 0) &&
			member_prefix.contains(')', -1))
			member_prefix = unquote(member_prefix);

#if RUNTIME_REGEX
		    static regex rxchain("[-a-zA-Z0-9::_>.`]+");
#endif
		    if (member_prefix.matches(rxchain))
		    {
			// Simple chain of identifiers - prepend `->'
			member_prefix += "->";
		    }
		    else
		    {
			member_prefix.prepend("(");
			member_prefix += ")->";
		    }
		}
		else
		{
		    member_prefix.prepend("(");
		    member_prefix += ").";
		}
	    }
	    else if (gdb->program_language() == LANGUAGE_PERL)
	    {
		// In Perl, members of A are accessed as A{'MEMBER_NAME'}
		member_prefix = normalize_base(member_prefix) + "{'";
		member_suffix = "'}";
	    }
	    else if (gdb->program_language() == LANGUAGE_PHP)
	    {
		// In PHP, members of $A are accessed as $A['MEMBER_NAME']
		member_prefix = normalize_base(member_prefix) + "['";
		member_suffix = "']";
	    }
	    else if (gdb->program_language() == LANGUAGE_FORTRAN)
	    {
		// In Fortran, members of A are accessed as A%B
		member_prefix = normalize_base(member_prefix) + "%";
	    }
	    else
	    {
		// In all other languages, members are accessed as A.B
		member_prefix = normalize_base(member_prefix) + ".";
	    }

	    // In case we do not find a struct beginning, read only one value
	    found_struct_begin = read_struct_begin(value, myaddr);
	    read_multiple_values = found_struct_begin;
	}

	// Prepend base class in case of multiple inheritance
	// FIXME: This should be passed as an argument
	static string baseclass_prefix;
	member_prefix += baseclass_prefix;
	int base_classes = 0;

	bool more_values = true;
	while (more_values)
	{
	    // In a List, we may have `member' names like `(a + b)'.
	    // Don't be picky about this.
	    bool picky = (mytype == Struct);
	    string member_name = read_member_name(value, picky);

	    if (member_name.empty())
	    {
		// Some struct stuff that is not a member
		DispValue *dv = parse_child(depth, value, myfull_name, "");

		if (dv->type() == Struct)
		{
		    // What's this - a struct within a struct?  Just
		    // adopt the members.
		    // (This happens when we finally found the struct
		    // after having read all the AIX DBX base classes.)
		    for (int i = 0; i < dv->nchildren(); i++)
		    {
			DispValue *dv2 = dv->child(i)->link();
			_children += dv2;
		    }
		    dv->unlink();
		}
		else
		{
		    _children += dv;
		}

		more_values = read_multiple_values && read_struct_next(value);
	    }
	    else if (is_BaseClass_name(member_name))
	    {
		// Base class member
		string saved_baseclass_prefix = baseclass_prefix;
		base_classes++;

		if (base_classes > 1)
		{
		    // Multiple inheritance.  Be sure to
		    // reference further members unambiguously.
		    //
		    // Note: we don't do that for the first base class,
		    // because this might turn ambiguous again.
		    //
		    // Example:
		    //
		    //    Base
		    //    |   |
		    //    I1 I2
		    //     \ /
		    //      C
		    //
		    // Members of I1::Base are not prefixed, members
		    // of I2::Base get `I2::' as base class prefix.
		    // If we did this already for the first base class,
		    // members of both I1 and I2 would get `Base::' as
		    // base class prefix.

		    switch (gdb->program_language())
		    {
		    case LANGUAGE_C: // C++
			baseclass_prefix = unquote(member_name) + "::";
			break;

		    default:
			// Do nothing (yet)
			break;
		    }
		}

		DispValue *dv = 
		    parse_child(depth, value, myfull_name, member_name);
		_children += dv;

		baseclass_prefix = saved_baseclass_prefix;

		more_values = read_multiple_values && read_struct_next(value);

		// Skip a possible `members of CLASS:' prefix
		read_members_prefix(value);

		// AIX DBX does not place a separator between base
		// classes and the other members, so we always
		// continue reading after having found a base
		// class.  After all, the own class members are
		// still missing.
		if (mytype == Struct && !found_struct_begin)
		    more_values = true;
	    }
	    else
	    {
		// Ordinary member
		string full_name = "";

		if (member_name == " ")
		{
		    // Anonymous union
		    full_name = myfull_name;
		}
		
		if (member_name.contains('.'))
		{
		    if (gdb->has_quotes())
		    {
			// The member name contains `.' => quote it.  This
			// happens with vtable pointers on Linux (`_vptr.').
			full_name = member_prefix + quote(member_name, '\'') + 
			    member_suffix;
		    }
		    else
		    {
			// JDB (and others?) prepend the class name 
			// to inherited members.  Omit this.
			full_name = 
			    member_prefix + member_name.after('.', -1) + 
			    member_suffix;
		    }
		}
		
		if (full_name.empty())
		{
		    // Ordinary member
		    full_name = member_prefix + member_name + member_suffix;
		}

		DispValue *child = 
		    parse_child(depth, value, full_name, member_name);

		if (child->type() == Text)
		{
		    // Found a text as child - child value must be empty
		    string empty = "";
		    _children += 
			parse_child(depth, empty, full_name, member_name);

		    string v = child->value();
		    strip_space(v);
		    if (!v.empty())
			_children += child;
		}
		else
		{
		    _children += child;
		}

		more_values = read_multiple_values && read_struct_next(value);
	    }

	    if (background(value.length()))
	    {
		init(parent, depth, value);
		return;
	    }
	}

	if (mytype == List && !value.empty())
	{
	    // Add remaining value as text
	    _children += parse_child(depth, value, "");
	}

	if (found_struct_begin)
	{
	    // Skip the remainder
	    read_struct_end(value);
	}

	// Expand only if at top-level.
	myexpanded = (depth == 0 || nchildren() <= 1);

#if LOG_CREATE_VALUES
	std::clog << mytype << " "
		  << quote(myfull_name)
		  << " has " << nchildren() << " members\n";
#endif

	perl_type = '%';
	break;
    }

    case Reference:
    {
	myexpanded = true;

	int sep = value.index('@');
	sep = value.index(':', sep);

	string ref = value.before(sep);
	value = value.after(sep);

	string addr = gdb->address_expr(myfull_name);

	_children += parse_child(depth, ref, addr, myfull_name, Pointer);
	_children += parse_child(depth, value, myfull_name);

	if (background(value.length()))
	{
	    init(parent, depth, value);
	    return;
	}

	perl_type = '$';	// No such thing in Perl...
	break;
    }

    case Sequence:
    case UnknownType:
	assert(0);
	abort();
    }

    // Handle trailing stuff (`sequences')
    if (parent == 0 || parent->type() != Sequence)
    {
	bool need_clear = true;
	while (sequence_pending(value, parent))
	{
	    if (need_clear)
	    {
#if LOG_CREATE_VALUES
		std::clog << "Sequence detected at " << quote(value) << "\n";
#endif

		clear();
		value = initial_value;

		mytype = Sequence;

#if LOG_CREATE_VALUES
		std::clog << mytype << " " << quote(myfull_name) << "\n";
#endif

		need_clear = false;
	    }
	    
	    const char *old_value = value.chars();

	    DispValue *dv = parse_child(depth, value, myfull_name);

	    if (value == old_value)
	    {
		// Nothing consumed - stop here
		dv->unlink();
		break;
	    }
	    else if (dv->type() == Simple && dv->value().empty())
	    {
		// Empty value - ignore
		dv->unlink();
	    }
	    else
	    {
		_children += dv;
	    }
	}

#if LOG_CREATE_VALUES
	if (!need_clear)
	{
	    std::clog << mytype << " "
		      << quote(myfull_name)
		      << " has " << nchildren() << " members\n";
	}
#endif
    }

    if (gdb->program_language() == LANGUAGE_PERL && is_perl_prefix(perl_type))
    {
	// Set new type
	if (!myfull_name.empty() && is_perl_prefix(myfull_name[0]))
	    myfull_name[0] = perl_type;
    }

    background(value.length());
    changed = true;
}