This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[commit] Merge the C++ name parser from drow-cplus-branch


This patch should have no effect on the behavior of GDB; all it does is
change the implementation of a couple of helper functions in
cp-support.c.  It uses a really big hammer, however, and provides an
interesting new interface:

  char *cp_canonicalize_string (const char *);

This function takes anything that looks like a 'name' in C++ (and a
couple of other things; roughly speaking, any types, identifiers,
or declarators) and converts them into a canonical form.  This
could handle all sorts of niggly user interface problems.  For
instance, these are all canonicalized in the same way:

  long foo<const long*>()
  signed long int foo <signed long int const *> (void)
  int long foo<int long const*>  ( void )

(as long foo<long const*>(), if you're curious.)

The formatting is common to the GNU v3 demangler in libiberty, which is
not set in stone; we can adjust if necessary.

We can't canonicalize user input before searching for it in the symbol
table, yet, because we don't canonicalize in the symbol readers.  It
was too expensive the last time I tried; the parser itself is quite
fast, but there's a lot of names to parse.  However, with the parser in
place, I'm confident we can find uses for it, and probably a solution
to the symbol table canonicalization.  That will fix some old issues in
templates.exp.

The specific problem which reminded me of this parser is the oddity
that GNU v3 mangles template functions including the return type; this
parser could be used to strip the return type off when adding the
function to the symbol table.

If you want to play around with the parser, run 'make test-cp-names' in
gdb/, and give it names as quoted strings on the command line.

I've tested the parser extensively, and I don't know of any open
problems with it.  If you can find any, I'll ... um ... I dunno, maybe
buy you a drink.  Please don't laugh at my abuse of bison.

Checked in.

-- 
Daniel Jacobowitz
CodeSourcery, LLC

2005-03-10  Daniel Jacobowitz  <dan@codesourcery.com>

	* Makefile.in (SFILES): Add cp-names.y.
	(libiberty_h, safe_ctype_h): New.
	(YYFILES): Add cp-names.c.
	(YYOBJ): Add cp-names.o.
	(test-cp-names.o, test-cp-names$(EXEEXT), cp-names.o): New rules.
	(clean): Remove test-cp-names$(EXEEXT).
	(local-maintainer-clean): Remove cp-names.c.
	* cp-names.y: New file.
	* cp-support.c (find_last_component): Delete.
	(d_left, d_right): Define.
	(cp_canonicalize_string, mangled_name_to_comp): New functions.
	(cp_class_name_from_physname, method_name_from_physname): Rewrite
	to use mangled_name_to_comp.
	* cp-support.h (cp_canonicalize_string, cp_demangled_name_to_comp)
	(cp_comp_to_string): New prototypes.
	* config/djgpp/fnchange.lst: Add cp-names.c.

Index: Makefile.in
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/Makefile.in,v
retrieving revision 1.704
diff -u -p -r1.704 Makefile.in
--- Makefile.in	8 Mar 2005 08:59:45 -0000	1.704
+++ Makefile.in	10 Mar 2005 04:16:50 -0000
@@ -519,6 +519,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
 	charset.c cli-out.c coffread.c coff-pe-read.c \
 	complaints.c completer.c corefile.c \
 	cp-abi.c cp-support.c cp-namespace.c cp-valprint.c \
+	cp-names.y \
 	dbxread.c demangle.c dictionary.c disasm.c doublest.c dummy-frame.c \
 	dwarfread.c dwarf2expr.c dwarf2loc.c dwarf2read.c dwarf2-frame.c \
 	elfread.c environ.c eval.c event-loop.c event-top.c expprint.c \
@@ -579,6 +580,7 @@ elf_arm_h =	$(INCLUDE_DIR)/elf/arm.h $(e
 elf_bfd_h =	$(BFD_SRC)/elf-bfd.h
 elf_frv_h =	$(INCLUDE_DIR)/elf/frv.h $(elf_reloc_macros_h)
 libaout_h =	$(BFD_SRC)/libaout.h
+libiberty_h =	$(INCLUDE_DIR)/libiberty.h
 libbfd_h =	$(BFD_SRC)/libbfd.h
 remote_sim_h =	$(INCLUDE_DIR)/gdb/remote-sim.h
 demangle_h =    $(INCLUDE_DIR)/demangle.h
@@ -596,6 +598,7 @@ gdb_sim_frv_h = $(INCLUDE_DIR)/gdb/sim-f
 gdb_sim_ppc_h =	$(INCLUDE_DIR)/gdb/sim-ppc.h
 gdb_sim_sh_h =	$(INCLUDE_DIR)/gdb/sim-sh.h
 splay_tree_h =  $(INCLUDE_DIR)/splay-tree.h
+safe_ctype_h =  $(INCLUDE_DIR)/safe-ctype.h
 hashtab_h =	$(INCLUDE_DIR)/hashtab.h
 
 #
@@ -944,11 +947,13 @@ SUBDIRS = @subdirs@
 
 # For now, shortcut the "configure GDB for fewer languages" stuff.
 YYFILES = c-exp.c \
+	cp-names.c \
 	objc-exp.c \
 	ada-exp.c \
 	jv-exp.c \
 	f-exp.c m2-exp.c p-exp.c
 YYOBJ = c-exp.o \
+	cp-names.o \
 	objc-exp.o \
 	ada-exp.o \
 	jv-exp.o \
@@ -1079,6 +1084,14 @@ uninstall-tui:
 		rm -f $(DESTDIR)$(bindir)/$$transformed_name$(EXEEXT) \
 		      $(DESTDIR)$(man1dir)/$$transformed_name.1
 
+# The C++ name parser can be built standalone for testing.
+test-cp-names.o: cp-names.c $(safe_ctype_h) $(libiberty_h) $(demangle_h)
+	$(CC) -c $(INTERNAL_CFLAGS) -DTEST_CPNAMES \
+		-o test-cp-names.o cp-names.c
+
+test-cp-names$(EXEEXT): test-cp-names.o $(LIBIBERTY)
+	$(CC) -o test-cp-names$(EXEEXT) test-cp-names.o $(LIBIBERTY)
+
 # We do this by grepping through sources.  If that turns out to be too slow,
 # maybe we could just require every .o file to have an initialization routine
 # of a given name (top.o -> _initialize_top, etc.).
@@ -1233,6 +1246,8 @@ clean mostlyclean: $(CONFIG_CLEAN)
 	rm -f init.c version.c
 	rm -f gdb$(EXEEXT) core make.log
 	rm -f gdb[0-9]$(EXEEXT)
+	rm -f test-cp-names$(EXEEXT)
+
 .PHONY: clean-tui
 clean-tui:
 	rm -f $(TUI)$(EXEEXT)
@@ -1260,6 +1275,7 @@ local-maintainer-clean:
 	@echo "This command is intended for maintainers to use;"
 	@echo "it deletes files that may require special tools to rebuild."
 	rm -f c-exp.c \
+		cp-names.c \
 		ada-lex.c ada-exp.c \
 	        objc-exp.c \
 		jv-exp.tab \
@@ -1492,7 +1508,6 @@ v850ice.o: $(srcdir)/v850ice.c
 		$(GDBTK_CFLAGS) \
 		$(srcdir)/v850ice.c
 
-
 # Message files.  Based on code in gcc/Makefile.in.
 
 # Rules for generating translated message descriptions.  Disabled by
@@ -1811,6 +1826,7 @@ core-regset.o: core-regset.c $(defs_h) $
 	$(inferior_h) $(target_h) $(gdb_string_h) $(gregset_h)
 cp-abi.o: cp-abi.c $(defs_h) $(value_h) $(cp_abi_h) $(command_h) $(gdbcmd_h) \
 	$(ui_out_h) $(gdb_string_h)
+cp-names.o: cp-names.c $(safe_ctype_h) $(libiberty_h) $(demangle_h)
 cp-namespace.o: cp-namespace.c $(defs_h) $(cp_support_h) $(gdb_obstack_h) \
 	$(symtab_h) $(symfile_h) $(gdb_assert_h) $(block_h) $(objfiles_h) \
 	$(gdbtypes_h) $(dictionary_h) $(command_h) $(frame_h)
Index: cp-names.y
===================================================================
RCS file: cp-names.y
diff -N cp-names.y
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cp-names.y	10 Mar 2005 04:31:19 -0000
@@ -0,0 +1,2131 @@
+/* YACC parser for C++ names, for GDB.
+
+   Copyright 2003, 2004, 2005
+   Free Software Foundation, Inc.
+
+   Parts of the lexer are based on c-exp.y from GDB.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Note that malloc's and realloc's in this file are transformed to
+   xmalloc and xrealloc respectively by the same sed command in the
+   makefile that remaps any other malloc/realloc inserted by the parser
+   generator.  Doing this with #defines and trying to control the interaction
+   with include files (<malloc.h> and <stdlib.h> for example) just became
+   too messy, particularly when such includes can be inserted at random
+   times by the parser generator.  */
+
+%{
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "safe-ctype.h"
+#include "libiberty.h"
+#include "demangle.h"
+
+/* Bison does not make it easy to create a parser without global
+   state, unfortunately.  Here are all the global variables used
+   in this parser.  */
+
+/* LEXPTR is the current pointer into our lex buffer.  PREV_LEXPTR
+   is the start of the last token lexed, only used for diagnostics.
+   ERROR_LEXPTR is the first place an error occurred.  GLOBAL_ERRMSG
+   is the first error message encountered.  */
+
+static const char *lexptr, *prev_lexptr, *error_lexptr, *global_errmsg;
+
+/* The components built by the parser are allocated ahead of time,
+   and cached in this structure.  */
+
+struct demangle_info {
+  int used;
+  struct demangle_component comps[1];
+};
+
+static struct demangle_info *demangle_info;
+#define d_grab() (&demangle_info->comps[demangle_info->used++])
+
+/* The parse tree created by the parser is stored here after a successful
+   parse.  */
+
+static struct demangle_component *global_result;
+
+/* Prototypes for helper functions used when constructing the parse
+   tree.  */
+
+static struct demangle_component *d_qualify (struct demangle_component *, int,
+					     int);
+
+static struct demangle_component *d_int_type (int);
+
+static struct demangle_component *d_unary (const char *,
+					   struct demangle_component *);
+static struct demangle_component *d_binary (const char *,
+					    struct demangle_component *,
+					    struct demangle_component *);
+
+/* Flags passed to d_qualify.  */
+
+#define QUAL_CONST 1
+#define QUAL_RESTRICT 2
+#define QUAL_VOLATILE 4
+
+/* Flags passed to d_int_type.  */
+
+#define INT_CHAR	(1 << 0)
+#define INT_SHORT	(1 << 1)
+#define INT_LONG	(1 << 2)
+#define INT_LLONG	(1 << 3)
+
+#define INT_SIGNED	(1 << 4)
+#define INT_UNSIGNED	(1 << 5)
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+   as well as gratuitiously global symbol names, so we can have multiple
+   yacc generated parsers in gdb.  Note that these are only the variables
+   produced by yacc.  If other parser generators (bison, byacc, etc) produce
+   additional global names that conflict at link time, then those parser
+   generators need to be fixed instead of adding those names to this list. */
+
+#define	yymaxdepth cpname_maxdepth
+#define	yyparse	cpname_parse
+#define	yylex	cpname_lex
+#define	yyerror	cpname_error
+#define	yylval	cpname_lval
+#define	yychar	cpname_char
+#define	yydebug	cpname_debug
+#define	yypact	cpname_pact	
+#define	yyr1	cpname_r1			
+#define	yyr2	cpname_r2			
+#define	yydef	cpname_def		
+#define	yychk	cpname_chk		
+#define	yypgo	cpname_pgo		
+#define	yyact	cpname_act		
+#define	yyexca	cpname_exca
+#define yyerrflag cpname_errflag
+#define yynerrs	cpname_nerrs
+#define	yyps	cpname_ps
+#define	yypv	cpname_pv
+#define	yys	cpname_s
+#define	yy_yys	cpname_yys
+#define	yystate	cpname_state
+#define	yytmp	cpname_tmp
+#define	yyv	cpname_v
+#define	yy_yyv	cpname_yyv
+#define	yyval	cpname_val
+#define	yylloc	cpname_lloc
+#define yyreds	cpname_reds		/* With YYDEBUG defined */
+#define yytoks	cpname_toks		/* With YYDEBUG defined */
+#define yyname	cpname_name		/* With YYDEBUG defined */
+#define yyrule	cpname_rule		/* With YYDEBUG defined */
+#define yylhs	cpname_yylhs
+#define yylen	cpname_yylen
+#define yydefred cpname_yydefred
+#define yydgoto	cpname_yydgoto
+#define yysindex cpname_yysindex
+#define yyrindex cpname_yyrindex
+#define yygindex cpname_yygindex
+#define yytable	 cpname_yytable
+#define yycheck	 cpname_yycheck
+
+static int yylex (void);
+static void yyerror (char *);
+
+/* Enable yydebug for the stand-alone parser.  */
+#ifdef TEST_CPNAMES
+# define YYDEBUG	1
+#endif
+
+/* Helper functions.  These wrap the demangler tree interface, handle
+   allocation from our global store, and return the allocated component.  */
+
+static struct demangle_component *
+fill_comp (enum demangle_component_type d_type, struct demangle_component *lhs,
+	   struct demangle_component *rhs)
+{
+  struct demangle_component *ret = d_grab ();
+  cplus_demangle_fill_component (ret, d_type, lhs, rhs);
+  return ret;
+}
+
+static struct demangle_component *
+make_empty (enum demangle_component_type d_type)
+{
+  struct demangle_component *ret = d_grab ();
+  ret->type = d_type;
+  return ret;
+}
+
+static struct demangle_component *
+make_operator (const char *name, int args)
+{
+  struct demangle_component *ret = d_grab ();
+  cplus_demangle_fill_operator (ret, name, args);
+  return ret;
+}
+
+static struct demangle_component *
+make_dtor (enum gnu_v3_dtor_kinds kind, struct demangle_component *name)
+{
+  struct demangle_component *ret = d_grab ();
+  cplus_demangle_fill_dtor (ret, kind, name);
+  return ret;
+}
+
+static struct demangle_component *
+make_builtin_type (const char *name)
+{
+  struct demangle_component *ret = d_grab ();
+  cplus_demangle_fill_builtin_type (ret, name);
+  return ret;
+}
+
+static struct demangle_component *
+make_name (const char *name, int len)
+{
+  struct demangle_component *ret = d_grab ();
+  cplus_demangle_fill_name (ret, name, len);
+  return ret;
+}
+
+#define d_left(dc) (dc)->u.s_binary.left
+#define d_right(dc) (dc)->u.s_binary.right
+
+%}
+
+%union
+  {
+    struct demangle_component *comp;
+    struct nested {
+      struct demangle_component *comp;
+      struct demangle_component **last;
+    } nested;
+    struct {
+      struct demangle_component *comp, *last;
+    } nested1;
+    struct {
+      struct demangle_component *comp, **last;
+      struct nested fn;
+      struct demangle_component *start;
+      int fold_flag;
+    } abstract;
+    int lval;
+    struct {
+      int val;
+      struct demangle_component *type;
+    } typed_val_int;
+    const char *opname;
+  }
+
+%type <comp> exp exp1 type start start_opt operator colon_name
+%type <comp> unqualified_name colon_ext_name
+%type <comp> template template_arg
+%type <comp> builtin_type
+%type <comp> typespec_2 array_indicator
+%type <comp> colon_ext_only ext_only_name
+
+%type <comp> demangler_special function conversion_op
+%type <nested> conversion_op_name
+
+%type <abstract> abstract_declarator direct_abstract_declarator
+%type <abstract> abstract_declarator_fn
+%type <nested> declarator direct_declarator function_arglist
+
+%type <nested> declarator_1 direct_declarator_1
+
+%type <nested> template_params function_args
+%type <nested> ptr_operator
+
+%type <nested1> nested_name
+
+%type <lval> qualifier qualifiers qualifiers_opt
+
+%type <lval> int_part int_seq
+
+%token <comp> INT
+%token <comp> FLOAT
+
+%token <comp> NAME
+%type <comp> name
+
+%token STRUCT CLASS UNION ENUM SIZEOF UNSIGNED COLONCOLON
+%token TEMPLATE
+%token ERROR
+%token NEW DELETE OPERATOR
+%token STATIC_CAST REINTERPRET_CAST DYNAMIC_CAST
+
+/* Special type cases, put in to allow the parser to distinguish different
+   legal basetypes.  */
+%token SIGNED_KEYWORD LONG SHORT INT_KEYWORD CONST_KEYWORD VOLATILE_KEYWORD DOUBLE_KEYWORD BOOL
+%token ELLIPSIS RESTRICT VOID FLOAT_KEYWORD CHAR WCHAR_T
+
+%token <opname> ASSIGN_MODIFY
+
+/* C++ */
+%token TRUEKEYWORD
+%token FALSEKEYWORD
+
+/* Non-C++ things we get from the demangler.  */
+%token <lval> DEMANGLER_SPECIAL
+%token CONSTRUCTION_VTABLE CONSTRUCTION_IN
+%token <typed_val_int> GLOBAL
+
+%{
+enum {
+  GLOBAL_CONSTRUCTORS = DEMANGLE_COMPONENT_LITERAL + 20,
+  GLOBAL_DESTRUCTORS = DEMANGLE_COMPONENT_LITERAL + 21
+};
+%}
+
+/* Precedence declarations.  */
+
+/* Give NAME lower precedence than COLONCOLON, so that nested_name will
+   associate greedily.  */
+%nonassoc NAME
+
+/* Give NEW and DELETE lower precedence than ']', because we can not
+   have an array of type operator new.  This causes NEW '[' to be
+   parsed as operator new[].  */
+%nonassoc NEW DELETE
+
+/* Give VOID higher precedence than NAME.  Then we can use %prec NAME
+   to prefer (VOID) to (function_args).  */
+%nonassoc VOID
+
+/* Give VOID lower precedence than ')' for similar reasons.  */
+%nonassoc ')'
+
+%left ','
+%right '=' ASSIGN_MODIFY
+%right '?'
+%left OROR
+%left ANDAND
+%left '|'
+%left '^'
+%left '&'
+%left EQUAL NOTEQUAL
+%left '<' '>' LEQ GEQ
+%left LSH RSH
+%left '@'
+%left '+' '-'
+%left '*' '/' '%'
+%right UNARY INCREMENT DECREMENT
+
+/* We don't need a precedence for '(' in this reduced grammar, and it
+   can mask some unpleasant bugs, so disable it for now.  */
+
+%right ARROW '.' '[' /* '(' */
+%left COLONCOLON
+
+
+%%
+
+result		:	start
+			{ global_result = $1; }
+		;
+
+start		:	type
+
+		|	demangler_special
+
+		|	function
+
+		;
+
+start_opt	:	/* */
+			{ $$ = NULL; }
+		|	COLONCOLON start
+			{ $$ = $2; }
+		;
+
+function
+		/* Function with a return type.  declarator_1 is used to prevent
+		   ambiguity with the next rule.  */
+		:	typespec_2 declarator_1
+			{ $$ = $2.comp;
+			  *$2.last = $1;
+			}
+
+		/* Function without a return type.  We need to use typespec_2
+		   to prevent conflicts from qualifiers_opt - harmless.  The
+		   start_opt is used to handle "function-local" variables and
+		   types.  */
+		|	typespec_2 function_arglist start_opt
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp);
+			  if ($3) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $3); }
+		|	colon_ext_only function_arglist start_opt
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp);
+			  if ($3) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $3); }
+
+		|	conversion_op_name start_opt
+			{ $$ = $1.comp;
+			  if ($2) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $2); }
+		|	conversion_op_name abstract_declarator_fn
+			{ if ($2.last)
+			    {
+			       /* First complete the abstract_declarator's type using
+				  the typespec from the conversion_op_name.  */
+			      *$2.last = *$1.last;
+			      /* Then complete the conversion_op_name with the type.  */
+			      *$1.last = $2.comp;
+			    }
+			  /* If we have an arglist, build a function type.  */
+			  if ($2.fn.comp)
+			    $$ = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1.comp, $2.fn.comp);
+			  else
+			    $$ = $1.comp;
+			  if ($2.start) $$ = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$, $2.start);
+			}
+		;
+
+demangler_special
+		:	DEMANGLER_SPECIAL start
+			{ $$ = make_empty ($1);
+			  d_left ($$) = $2;
+			  d_right ($$) = NULL; }
+		|	CONSTRUCTION_VTABLE start CONSTRUCTION_IN start
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_CONSTRUCTION_VTABLE, $2, $4); }
+		|	GLOBAL
+			{ $$ = make_empty ($1.val);
+			  d_left ($$) = $1.type;
+			  d_right ($$) = NULL; }
+		;
+
+operator	:	OPERATOR NEW
+			{ $$ = make_operator ("new", 1); }
+		|	OPERATOR DELETE
+			{ $$ = make_operator ("delete", 1); }
+		|	OPERATOR NEW '[' ']'
+			{ $$ = make_operator ("new[]", 1); }
+		|	OPERATOR DELETE '[' ']'
+			{ $$ = make_operator ("delete[]", 1); }
+		|	OPERATOR '+'
+			{ $$ = make_operator ("+", 2); }
+		|	OPERATOR '-'
+			{ $$ = make_operator ("-", 2); }
+		|	OPERATOR '*'
+			{ $$ = make_operator ("*", 2); }
+		|	OPERATOR '/'
+			{ $$ = make_operator ("/", 2); }
+		|	OPERATOR '%'
+			{ $$ = make_operator ("%", 2); }
+		|	OPERATOR '^'
+			{ $$ = make_operator ("^", 2); }
+		|	OPERATOR '&'
+			{ $$ = make_operator ("&", 2); }
+		|	OPERATOR '|'
+			{ $$ = make_operator ("|", 2); }
+		|	OPERATOR '~'
+			{ $$ = make_operator ("~", 1); }
+		|	OPERATOR '!'
+			{ $$ = make_operator ("!", 1); }
+		|	OPERATOR '='
+			{ $$ = make_operator ("=", 2); }
+		|	OPERATOR '<'
+			{ $$ = make_operator ("<", 2); }
+		|	OPERATOR '>'
+			{ $$ = make_operator (">", 2); }
+		|	OPERATOR ASSIGN_MODIFY
+			{ $$ = make_operator ($2, 2); }
+		|	OPERATOR LSH
+			{ $$ = make_operator ("<<", 2); }
+		|	OPERATOR RSH
+			{ $$ = make_operator (">>", 2); }
+		|	OPERATOR EQUAL
+			{ $$ = make_operator ("==", 2); }
+		|	OPERATOR NOTEQUAL
+			{ $$ = make_operator ("!=", 2); }
+		|	OPERATOR LEQ
+			{ $$ = make_operator ("<=", 2); }
+		|	OPERATOR GEQ
+			{ $$ = make_operator (">=", 2); }
+		|	OPERATOR ANDAND
+			{ $$ = make_operator ("&&", 2); }
+		|	OPERATOR OROR
+			{ $$ = make_operator ("||", 2); }
+		|	OPERATOR INCREMENT
+			{ $$ = make_operator ("++", 1); }
+		|	OPERATOR DECREMENT
+			{ $$ = make_operator ("--", 1); }
+		|	OPERATOR ','
+			{ $$ = make_operator (",", 2); }
+		|	OPERATOR ARROW '*'
+			{ $$ = make_operator ("->*", 2); }
+		|	OPERATOR ARROW
+			{ $$ = make_operator ("->", 2); }
+		|	OPERATOR '(' ')'
+			{ $$ = make_operator ("()", 0); }
+		|	OPERATOR '[' ']'
+			{ $$ = make_operator ("[]", 2); }
+		;
+
+		/* Conversion operators.  We don't try to handle some of
+		   the wackier demangler output for function pointers,
+		   since it's not clear that it's parseable.  */
+conversion_op
+		:	OPERATOR typespec_2
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_CAST, $2, NULL); }
+		;
+
+conversion_op_name
+		:	nested_name conversion_op
+			{ $$.comp = $1.comp;
+			  d_right ($1.last) = $2;
+			  $$.last = &d_left ($2);
+			}
+		|	conversion_op
+			{ $$.comp = $1;
+			  $$.last = &d_left ($1);
+			}
+		|	COLONCOLON nested_name conversion_op
+			{ $$.comp = $2.comp;
+			  d_right ($2.last) = $3;
+			  $$.last = &d_left ($3);
+			}
+		|	COLONCOLON conversion_op
+			{ $$.comp = $2;
+			  $$.last = &d_left ($2);
+			}
+		;
+
+/* DEMANGLE_COMPONENT_NAME */
+/* This accepts certain invalid placements of '~'.  */
+unqualified_name:	operator
+		|	operator '<' template_params '>'
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_TEMPLATE, $1, $3.comp); }
+		|	'~' NAME
+			{ $$ = make_dtor (gnu_v3_complete_object_dtor, $2); }
+		;
+
+/* This rule is used in name and nested_name, and expanded inline there
+   for efficiency.  */
+/*
+scope_id	:	NAME
+		|	template
+		;
+*/
+
+colon_name	:	name
+		|	COLONCOLON name
+			{ $$ = $2; }
+		;
+
+/* DEMANGLE_COMPONENT_QUAL_NAME */
+/* DEMANGLE_COMPONENT_CTOR / DEMANGLE_COMPONENT_DTOR ? */
+name		:	nested_name NAME %prec NAME
+			{ $$ = $1.comp; d_right ($1.last) = $2; }
+		|	NAME %prec NAME
+		|	nested_name template %prec NAME
+			{ $$ = $1.comp; d_right ($1.last) = $2; }
+		|	template %prec NAME
+		;
+
+colon_ext_name	:	colon_name
+		|	colon_ext_only
+		;
+
+colon_ext_only	:	ext_only_name
+		|	COLONCOLON ext_only_name
+			{ $$ = $2; }
+		;
+
+ext_only_name	:	nested_name unqualified_name
+			{ $$ = $1.comp; d_right ($1.last) = $2; }
+		|	unqualified_name
+		;
+
+nested_name	:	NAME COLONCOLON
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_QUAL_NAME);
+			  d_left ($$.comp) = $1;
+			  d_right ($$.comp) = NULL;
+			  $$.last = $$.comp;
+			}
+		|	nested_name NAME COLONCOLON
+			{ $$.comp = $1.comp;
+			  d_right ($1.last) = make_empty (DEMANGLE_COMPONENT_QUAL_NAME);
+			  $$.last = d_right ($1.last);
+			  d_left ($$.last) = $2;
+			  d_right ($$.last) = NULL;
+			}
+		|	template COLONCOLON
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_QUAL_NAME);
+			  d_left ($$.comp) = $1;
+			  d_right ($$.comp) = NULL;
+			  $$.last = $$.comp;
+			}
+		|	nested_name template COLONCOLON
+			{ $$.comp = $1.comp;
+			  d_right ($1.last) = make_empty (DEMANGLE_COMPONENT_QUAL_NAME);
+			  $$.last = d_right ($1.last);
+			  d_left ($$.last) = $2;
+			  d_right ($$.last) = NULL;
+			}
+		;
+
+/* DEMANGLE_COMPONENT_TEMPLATE */
+/* DEMANGLE_COMPONENT_TEMPLATE_ARGLIST */
+template	:	NAME '<' template_params '>'
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_TEMPLATE, $1, $3.comp); }
+		;
+
+template_params	:	template_arg
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, $1, NULL);
+			$$.last = &d_right ($$.comp); }
+		|	template_params ',' template_arg
+			{ $$.comp = $1.comp;
+			  *$1.last = fill_comp (DEMANGLE_COMPONENT_TEMPLATE_ARGLIST, $3, NULL);
+			  $$.last = &d_right (*$1.last);
+			}
+		;
+
+/* "type" is inlined into template_arg and function_args.  */
+
+/* Also an integral constant-expression of integral type, and a
+   pointer to member (?) */
+template_arg	:	typespec_2
+		|	typespec_2 abstract_declarator
+			{ $$ = $2.comp;
+			  *$2.last = $1;
+			}
+		|	'&' start
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator ("&", 1), $2); }
+		|	'&' '(' start ')'
+			{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator ("&", 1), $3); }
+		|	exp
+		;
+
+function_args	:	typespec_2
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $1, NULL);
+			  $$.last = &d_right ($$.comp);
+			}
+		|	typespec_2 abstract_declarator
+			{ *$2.last = $1;
+			  $$.comp = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $2.comp, NULL);
+			  $$.last = &d_right ($$.comp);
+			}
+		|	function_args ',' typespec_2
+			{ *$1.last = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $3, NULL);
+			  $$.comp = $1.comp;
+			  $$.last = &d_right (*$1.last);
+			}
+		|	function_args ',' typespec_2 abstract_declarator
+			{ *$4.last = $3;
+			  *$1.last = fill_comp (DEMANGLE_COMPONENT_ARGLIST, $4.comp, NULL);
+			  $$.comp = $1.comp;
+			  $$.last = &d_right (*$1.last);
+			}
+		|	function_args ',' ELLIPSIS
+			{ *$1.last
+			    = fill_comp (DEMANGLE_COMPONENT_ARGLIST,
+					   make_builtin_type ("..."),
+					   NULL);
+			  $$.comp = $1.comp;
+			  $$.last = &d_right (*$1.last);
+			}
+		;
+
+function_arglist:	'(' function_args ')' qualifiers_opt %prec NAME
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, NULL, $2.comp);
+			  $$.last = &d_left ($$.comp);
+			  $$.comp = d_qualify ($$.comp, $4, 1); }
+		|	'(' VOID ')' qualifiers_opt
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, NULL, NULL);
+			  $$.last = &d_left ($$.comp);
+			  $$.comp = d_qualify ($$.comp, $4, 1); }
+		|	'(' ')' qualifiers_opt
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_FUNCTION_TYPE, NULL, NULL);
+			  $$.last = &d_left ($$.comp);
+			  $$.comp = d_qualify ($$.comp, $3, 1); }
+		;
+
+/* Should do something about DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL */
+qualifiers_opt	:	/* epsilon */
+			{ $$ = 0; }
+		|	qualifiers
+		;
+
+qualifier	:	RESTRICT
+			{ $$ = QUAL_RESTRICT; }
+		|	VOLATILE_KEYWORD
+			{ $$ = QUAL_VOLATILE; }
+		|	CONST_KEYWORD
+			{ $$ = QUAL_CONST; }
+		;
+
+qualifiers	:	qualifier
+		|	qualifier qualifiers
+			{ $$ = $1 | $2; }
+		;
+
+/* This accepts all sorts of invalid constructions and produces
+   invalid output for them - an error would be better.  */
+
+int_part	:	INT_KEYWORD
+			{ $$ = 0; }
+		|	SIGNED_KEYWORD
+			{ $$ = INT_SIGNED; }
+		|	UNSIGNED
+			{ $$ = INT_UNSIGNED; }
+		|	CHAR
+			{ $$ = INT_CHAR; }
+		|	LONG
+			{ $$ = INT_LONG; }
+		|	SHORT
+			{ $$ = INT_SHORT; }
+		;
+
+int_seq		:	int_part
+		|	int_seq int_part
+			{ $$ = $1 | $2; if ($1 & $2 & INT_LONG) $$ = $1 | INT_LLONG; }
+		;
+
+builtin_type	:	int_seq
+			{ $$ = d_int_type ($1); }
+		|	FLOAT_KEYWORD
+			{ $$ = make_builtin_type ("float"); }
+		|	DOUBLE_KEYWORD
+			{ $$ = make_builtin_type ("double"); }
+		|	LONG DOUBLE_KEYWORD
+			{ $$ = make_builtin_type ("long double"); }
+		|	BOOL
+			{ $$ = make_builtin_type ("bool"); }
+		|	WCHAR_T
+			{ $$ = make_builtin_type ("wchar_t"); }
+		|	VOID
+			{ $$ = make_builtin_type ("void"); }
+		;
+
+ptr_operator	:	'*' qualifiers_opt
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_POINTER);
+			  $$.comp->u.s_binary.left = $$.comp->u.s_binary.right = NULL;
+			  $$.last = &d_left ($$.comp);
+			  $$.comp = d_qualify ($$.comp, $2, 0); }
+		/* g++ seems to allow qualifiers after the reference?  */
+		|	'&'
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_REFERENCE);
+			  $$.comp->u.s_binary.left = $$.comp->u.s_binary.right = NULL;
+			  $$.last = &d_left ($$.comp); }
+		|	nested_name '*' qualifiers_opt
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_PTRMEM_TYPE);
+			  $$.comp->u.s_binary.left = $1.comp;
+			  /* Convert the innermost DEMANGLE_COMPONENT_QUAL_NAME to a DEMANGLE_COMPONENT_NAME.  */
+			  *$1.last = *d_left ($1.last);
+			  $$.comp->u.s_binary.right = NULL;
+			  $$.last = &d_right ($$.comp);
+			  $$.comp = d_qualify ($$.comp, $3, 0); }
+		|	COLONCOLON nested_name '*' qualifiers_opt
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_PTRMEM_TYPE);
+			  $$.comp->u.s_binary.left = $2.comp;
+			  /* Convert the innermost DEMANGLE_COMPONENT_QUAL_NAME to a DEMANGLE_COMPONENT_NAME.  */
+			  *$2.last = *d_left ($2.last);
+			  $$.comp->u.s_binary.right = NULL;
+			  $$.last = &d_right ($$.comp);
+			  $$.comp = d_qualify ($$.comp, $4, 0); }
+		;
+
+array_indicator	:	'[' ']'
+			{ $$ = make_empty (DEMANGLE_COMPONENT_ARRAY_TYPE);
+			  d_left ($$) = NULL;
+			}
+		|	'[' INT ']'
+			{ $$ = make_empty (DEMANGLE_COMPONENT_ARRAY_TYPE);
+			  d_left ($$) = $2;
+			}
+		;
+
+/* Details of this approach inspired by the G++ < 3.4 parser.  */
+
+/* This rule is only used in typespec_2, and expanded inline there for
+   efficiency.  */
+/*
+typespec	:	builtin_type
+		|	colon_name
+		;
+*/
+
+typespec_2	:	builtin_type qualifiers
+			{ $$ = d_qualify ($1, $2, 0); }
+		|	builtin_type
+		|	qualifiers builtin_type qualifiers
+			{ $$ = d_qualify ($2, $1 | $3, 0); }
+		|	qualifiers builtin_type
+			{ $$ = d_qualify ($2, $1, 0); }
+
+		|	name qualifiers
+			{ $$ = d_qualify ($1, $2, 0); }
+		|	name
+		|	qualifiers name qualifiers
+			{ $$ = d_qualify ($2, $1 | $3, 0); }
+		|	qualifiers name
+			{ $$ = d_qualify ($2, $1, 0); }
+
+		|	COLONCOLON name qualifiers
+			{ $$ = d_qualify ($2, $3, 0); }
+		|	COLONCOLON name
+			{ $$ = $2; }
+		|	qualifiers COLONCOLON name qualifiers
+			{ $$ = d_qualify ($3, $1 | $4, 0); }
+		|	qualifiers COLONCOLON name
+			{ $$ = d_qualify ($3, $1, 0); }
+		;
+
+abstract_declarator
+		:	ptr_operator
+			{ $$.comp = $1.comp; $$.last = $1.last;
+			  $$.fn.comp = NULL; $$.fn.last = NULL; }
+		|	ptr_operator abstract_declarator
+			{ $$ = $2; $$.fn.comp = NULL; $$.fn.last = NULL;
+			  if ($2.fn.comp) { $$.last = $2.fn.last; *$2.last = $2.fn.comp; }
+			  *$$.last = $1.comp;
+			  $$.last = $1.last; }
+		|	direct_abstract_declarator
+			{ $$.fn.comp = NULL; $$.fn.last = NULL;
+			  if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; }
+			}
+		;
+
+direct_abstract_declarator
+		:	'(' abstract_declarator ')'
+			{ $$ = $2; $$.fn.comp = NULL; $$.fn.last = NULL; $$.fold_flag = 1;
+			  if ($2.fn.comp) { $$.last = $2.fn.last; *$2.last = $2.fn.comp; }
+			}
+		|	direct_abstract_declarator function_arglist
+			{ $$.fold_flag = 0;
+			  if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; }
+			  if ($1.fold_flag)
+			    {
+			      *$$.last = $2.comp;
+			      $$.last = $2.last;
+			    }
+			  else
+			    $$.fn = $2;
+			}
+		|	direct_abstract_declarator array_indicator
+			{ $$.fn.comp = NULL; $$.fn.last = NULL; $$.fold_flag = 0;
+			  if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; }
+			  *$1.last = $2;
+			  $$.last = &d_right ($2);
+			}
+		|	array_indicator
+			{ $$.fn.comp = NULL; $$.fn.last = NULL; $$.fold_flag = 0;
+			  $$.comp = $1;
+			  $$.last = &d_right ($1);
+			}
+		/* G++ has the following except for () and (type).  Then
+		   (type) is handled in regcast_or_absdcl and () is handled
+		   in fcast_or_absdcl.
+
+		   However, this is only useful for function types, and
+		   generates reduce/reduce conflicts with direct_declarator.
+		   We're interested in pointer-to-function types, and in
+		   functions, but not in function types - so leave this
+		   out.  */
+		/* |	function_arglist */
+		;
+
+abstract_declarator_fn
+		:	ptr_operator
+			{ $$.comp = $1.comp; $$.last = $1.last;
+			  $$.fn.comp = NULL; $$.fn.last = NULL; $$.start = NULL; }
+		|	ptr_operator abstract_declarator_fn
+			{ $$ = $2;
+			  if ($2.last)
+			    *$$.last = $1.comp;
+			  else
+			    $$.comp = $1.comp;
+			  $$.last = $1.last;
+			}
+		|	direct_abstract_declarator
+			{ $$.comp = $1.comp; $$.last = $1.last; $$.fn = $1.fn; $$.start = NULL; }
+		|	direct_abstract_declarator function_arglist COLONCOLON start
+			{ $$.start = $4;
+			  if ($1.fn.comp) { $$.last = $1.fn.last; *$1.last = $1.fn.comp; }
+			  if ($1.fold_flag)
+			    {
+			      *$$.last = $2.comp;
+			      $$.last = $2.last;
+			    }
+			  else
+			    $$.fn = $2;
+			}
+		|	function_arglist start_opt
+			{ $$.fn = $1;
+			  $$.start = $2;
+			  $$.comp = NULL; $$.last = NULL;
+			}
+		;
+
+type		:	typespec_2
+		|	typespec_2 abstract_declarator
+			{ $$ = $2.comp;
+			  *$2.last = $1;
+			}
+		;
+
+declarator	:	ptr_operator declarator
+			{ $$.comp = $2.comp;
+			  $$.last = $1.last;
+			  *$2.last = $1.comp; }
+		|	direct_declarator
+		;
+
+direct_declarator
+		:	'(' declarator ')'
+			{ $$ = $2; }
+		|	direct_declarator function_arglist
+			{ $$.comp = $1.comp;
+			  *$1.last = $2.comp;
+			  $$.last = $2.last;
+			}
+		|	direct_declarator array_indicator
+			{ $$.comp = $1.comp;
+			  *$1.last = $2;
+			  $$.last = &d_right ($2);
+			}
+		|	colon_ext_name
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_TYPED_NAME);
+			  d_left ($$.comp) = $1;
+			  $$.last = &d_right ($$.comp);
+			}
+		;
+
+/* These are similar to declarator and direct_declarator except that they
+   do not permit ( colon_ext_name ), which is ambiguous with a function
+   argument list.  They also don't permit a few other forms with redundant
+   parentheses around the colon_ext_name; any colon_ext_name in parentheses
+   must be followed by an argument list or an array indicator, or preceded
+   by a pointer.  */
+declarator_1	:	ptr_operator declarator_1
+			{ $$.comp = $2.comp;
+			  $$.last = $1.last;
+			  *$2.last = $1.comp; }
+		|	colon_ext_name
+			{ $$.comp = make_empty (DEMANGLE_COMPONENT_TYPED_NAME);
+			  d_left ($$.comp) = $1;
+			  $$.last = &d_right ($$.comp);
+			}
+		|	direct_declarator_1
+
+			/* Function local variable or type.  The typespec to
+			   our left is the type of the containing function. 
+			   This should be OK, because function local types
+			   can not be templates, so the return types of their
+			   members will not be mangled.  If they are hopefully
+			   they'll end up to the right of the ::.  */
+		|	colon_ext_name function_arglist COLONCOLON start
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp);
+			  $$.last = $2.last;
+			  $$.comp = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$.comp, $4);
+			}
+		|	direct_declarator_1 function_arglist COLONCOLON start
+			{ $$.comp = $1.comp;
+			  *$1.last = $2.comp;
+			  $$.last = $2.last;
+			  $$.comp = fill_comp (DEMANGLE_COMPONENT_LOCAL_NAME, $$.comp, $4);
+			}
+		;
+
+direct_declarator_1
+		:	'(' ptr_operator declarator ')'
+			{ $$.comp = $3.comp;
+			  $$.last = $2.last;
+			  *$3.last = $2.comp; }
+		|	direct_declarator_1 function_arglist
+			{ $$.comp = $1.comp;
+			  *$1.last = $2.comp;
+			  $$.last = $2.last;
+			}
+		|	direct_declarator_1 array_indicator
+			{ $$.comp = $1.comp;
+			  *$1.last = $2;
+			  $$.last = &d_right ($2);
+			}
+		|	colon_ext_name function_arglist
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2.comp);
+			  $$.last = $2.last;
+			}
+		|	colon_ext_name array_indicator
+			{ $$.comp = fill_comp (DEMANGLE_COMPONENT_TYPED_NAME, $1, $2);
+			  $$.last = &d_right ($2);
+			}
+		;
+
+exp	:	'(' exp1 ')'
+		{ $$ = $2; }
+	;
+
+/* Silly trick.  Only allow '>' when parenthesized, in order to
+   handle conflict with templates.  */
+exp1	:	exp
+	;
+
+exp1	:	exp '>' exp
+		{ $$ = d_binary (">", $1, $3); }
+	;
+
+/* References.  Not allowed everywhere in template parameters, only
+   at the top level, but treat them as expressions in case they are wrapped
+   in parentheses.  */
+exp1	:	'&' start
+		{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator ("&", 1), $2); }
+	;
+
+/* Expressions, not including the comma operator.  */
+exp	:	'-' exp    %prec UNARY
+		{ $$ = d_unary ("-", $2); }
+	;
+
+exp	:	'!' exp    %prec UNARY
+		{ $$ = d_unary ("!", $2); }
+	;
+
+exp	:	'~' exp    %prec UNARY
+		{ $$ = d_unary ("~", $2); }
+	;
+
+/* Casts.  First your normal C-style cast.  If exp is a LITERAL, just change
+   its type.  */
+
+exp	:	'(' type ')' exp  %prec UNARY
+		{ if ($4->type == DEMANGLE_COMPONENT_LITERAL
+		      || $4->type == DEMANGLE_COMPONENT_LITERAL_NEG)
+		    {
+		      $$ = $4;
+		      d_left ($4) = $2;
+		    }
+		  else
+		    $$ = fill_comp (DEMANGLE_COMPONENT_UNARY,
+				      fill_comp (DEMANGLE_COMPONENT_CAST, $2, NULL),
+				      $4);
+		}
+	;
+
+/* Mangling does not differentiate between these, so we don't need to
+   either.  */
+exp	:	STATIC_CAST '<' type '>' '(' exp1 ')' %prec UNARY
+		{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY,
+				    fill_comp (DEMANGLE_COMPONENT_CAST, $3, NULL),
+				    $6);
+		}
+	;
+
+exp	:	DYNAMIC_CAST '<' type '>' '(' exp1 ')' %prec UNARY
+		{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY,
+				    fill_comp (DEMANGLE_COMPONENT_CAST, $3, NULL),
+				    $6);
+		}
+	;
+
+exp	:	REINTERPRET_CAST '<' type '>' '(' exp1 ')' %prec UNARY
+		{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY,
+				    fill_comp (DEMANGLE_COMPONENT_CAST, $3, NULL),
+				    $6);
+		}
+	;
+
+/* Another form of C++-style cast.  "type ( exp1 )" is not allowed (it's too
+   ambiguous), but "name ( exp1 )" is.  Because we don't need to support
+   function types, we can handle this unambiguously (the use of typespec_2
+   prevents a silly, harmless conflict with qualifiers_opt).  This does not
+   appear in demangler output so it's not a great loss if we need to
+   disable it.  */
+exp	:	typespec_2 '(' exp1 ')' %prec UNARY
+		{ $$ = fill_comp (DEMANGLE_COMPONENT_UNARY,
+				    fill_comp (DEMANGLE_COMPONENT_CAST, $1, NULL),
+				    $3);
+		}
+	;
+
+/* TO INVESTIGATE: ._0 style anonymous names; anonymous namespaces */
+
+/* Binary operators in order of decreasing precedence.  */
+
+exp	:	exp '*' exp
+		{ $$ = d_binary ("*", $1, $3); }
+	;
+
+exp	:	exp '/' exp
+		{ $$ = d_binary ("/", $1, $3); }
+	;
+
+exp	:	exp '%' exp
+		{ $$ = d_binary ("%", $1, $3); }
+	;
+
+exp	:	exp '+' exp
+		{ $$ = d_binary ("+", $1, $3); }
+	;
+
+exp	:	exp '-' exp
+		{ $$ = d_binary ("-", $1, $3); }
+	;
+
+exp	:	exp LSH exp
+		{ $$ = d_binary ("<<", $1, $3); }
+	;
+
+exp	:	exp RSH exp
+		{ $$ = d_binary (">>", $1, $3); }
+	;
+
+exp	:	exp EQUAL exp
+		{ $$ = d_binary ("==", $1, $3); }
+	;
+
+exp	:	exp NOTEQUAL exp
+		{ $$ = d_binary ("!=", $1, $3); }
+	;
+
+exp	:	exp LEQ exp
+		{ $$ = d_binary ("<=", $1, $3); }
+	;
+
+exp	:	exp GEQ exp
+		{ $$ = d_binary (">=", $1, $3); }
+	;
+
+exp	:	exp '<' exp
+		{ $$ = d_binary ("<", $1, $3); }
+	;
+
+exp	:	exp '&' exp
+		{ $$ = d_binary ("&", $1, $3); }
+	;
+
+exp	:	exp '^' exp
+		{ $$ = d_binary ("^", $1, $3); }
+	;
+
+exp	:	exp '|' exp
+		{ $$ = d_binary ("|", $1, $3); }
+	;
+
+exp	:	exp ANDAND exp
+		{ $$ = d_binary ("&&", $1, $3); }
+	;
+
+exp	:	exp OROR exp
+		{ $$ = d_binary ("||", $1, $3); }
+	;
+
+/* Not 100% sure these are necessary, but they're harmless.  */
+exp	:	exp ARROW NAME
+		{ $$ = d_binary ("->", $1, $3); }
+	;
+
+exp	:	exp '.' NAME
+		{ $$ = d_binary (".", $1, $3); }
+	;
+
+exp	:	exp '?' exp ':' exp	%prec '?'
+		{ $$ = fill_comp (DEMANGLE_COMPONENT_TRINARY, make_operator ("?", 3),
+				    fill_comp (DEMANGLE_COMPONENT_TRINARY_ARG1, $1,
+						 fill_comp (DEMANGLE_COMPONENT_TRINARY_ARG2, $3, $5)));
+		}
+	;
+			  
+exp	:	INT
+	;
+
+/* Not generally allowed.  */
+exp	:	FLOAT
+	;
+
+exp	:	SIZEOF '(' type ')'	%prec UNARY
+		{ $$ = d_unary ("sizeof", $3); }
+	;
+
+/* C++.  */
+exp     :       TRUEKEYWORD    
+		{ struct demangle_component *i;
+		  i = make_name ("1", 1);
+		  $$ = fill_comp (DEMANGLE_COMPONENT_LITERAL,
+				    make_builtin_type ("bool"),
+				    i);
+		}
+	;
+
+exp     :       FALSEKEYWORD   
+		{ struct demangle_component *i;
+		  i = make_name ("0", 1);
+		  $$ = fill_comp (DEMANGLE_COMPONENT_LITERAL,
+				    make_builtin_type ("bool"),
+				    i);
+		}
+	;
+
+/* end of C++.  */
+
+%%
+
+/* Apply QUALIFIERS to LHS and return a qualified component.  IS_METHOD
+   is set if LHS is a method, in which case the qualifiers are logically
+   applied to "this".  We apply qualifiers in a consistent order; LHS
+   may already be qualified; duplicate qualifiers are not created.  */
+
+struct demangle_component *
+d_qualify (struct demangle_component *lhs, int qualifiers, int is_method)
+{
+  struct demangle_component **inner_p;
+  enum demangle_component_type type;
+
+  /* For now the order is CONST (innermost), VOLATILE, RESTRICT.  */
+
+#define HANDLE_QUAL(TYPE, MTYPE, QUAL)				\
+  if ((qualifiers & QUAL) && (type != TYPE) && (type != MTYPE))	\
+    {								\
+      *inner_p = fill_comp (is_method ? MTYPE : TYPE,	\
+			      *inner_p, NULL);			\
+      inner_p = &d_left (*inner_p);				\
+      type = (*inner_p)->type;					\
+    }								\
+  else if (type == TYPE || type == MTYPE)			\
+    {								\
+      inner_p = &d_left (*inner_p);				\
+      type = (*inner_p)->type;					\
+    }
+
+  inner_p = &lhs;
+
+  type = (*inner_p)->type;
+
+  HANDLE_QUAL (DEMANGLE_COMPONENT_RESTRICT, DEMANGLE_COMPONENT_RESTRICT_THIS, QUAL_RESTRICT);
+  HANDLE_QUAL (DEMANGLE_COMPONENT_VOLATILE, DEMANGLE_COMPONENT_VOLATILE_THIS, QUAL_VOLATILE);
+  HANDLE_QUAL (DEMANGLE_COMPONENT_CONST, DEMANGLE_COMPONENT_CONST_THIS, QUAL_CONST);
+
+  return lhs;
+}
+
+/* Return a builtin type corresponding to FLAGS.  */
+
+static struct demangle_component *
+d_int_type (int flags)
+{
+  const char *name;
+
+  switch (flags)
+    {
+    case INT_SIGNED | INT_CHAR:
+      name = "signed char";
+      break;
+    case INT_CHAR:
+      name = "char";
+      break;
+    case INT_UNSIGNED | INT_CHAR:
+      name = "unsigned char";
+      break;
+    case 0:
+    case INT_SIGNED:
+      name = "int";
+      break;
+    case INT_UNSIGNED:
+      name = "unsigned int";
+      break;
+    case INT_LONG:
+    case INT_SIGNED | INT_LONG:
+      name = "long";
+      break;
+    case INT_UNSIGNED | INT_LONG:
+      name = "unsigned long";
+      break;
+    case INT_SHORT:
+    case INT_SIGNED | INT_SHORT:
+      name = "short";
+      break;
+    case INT_UNSIGNED | INT_SHORT:
+      name = "unsigned short";
+      break;
+    case INT_LLONG | INT_LONG:
+    case INT_SIGNED | INT_LLONG | INT_LONG:
+      name = "long long";
+      break;
+    case INT_UNSIGNED | INT_LLONG | INT_LONG:
+      name = "unsigned long long";
+      break;
+    default:
+      return NULL;
+    }
+
+  return make_builtin_type (name);
+}
+
+/* Wrapper to create a unary operation.  */
+
+static struct demangle_component *
+d_unary (const char *name, struct demangle_component *lhs)
+{
+  return fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator (name, 1), lhs);
+}
+
+/* Wrapper to create a binary operation.  */
+
+static struct demangle_component *
+d_binary (const char *name, struct demangle_component *lhs, struct demangle_component *rhs)
+{
+  return fill_comp (DEMANGLE_COMPONENT_BINARY, make_operator (name, 2),
+		      fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs));
+}
+
+/* Find the end of a symbol name starting at LEXPTR.  */
+
+static const char *
+symbol_end (const char *lexptr)
+{
+  const char *p = lexptr;
+
+  while (*p && (ISALNUM (*p) || *p == '_' || *p == '$' || *p == '.'))
+    p++;
+
+  return p;
+}
+
+/* Take care of parsing a number (anything that starts with a digit).
+   The number starts at P and contains LEN characters.  Store the result in
+   YYLVAL.  */
+
+static int
+parse_number (const char *p, int len, int parsed_float)
+{
+  int unsigned_p = 0;
+
+  /* Number of "L" suffixes encountered.  */
+  int long_p = 0;
+
+  struct demangle_component *signed_type;
+  struct demangle_component *unsigned_type;
+  struct demangle_component *type, *name;
+  enum demangle_component_type literal_type;
+
+  if (p[0] == '-')
+    {
+      literal_type = DEMANGLE_COMPONENT_LITERAL_NEG;
+      p++;
+      len--;
+    }
+  else
+    literal_type = DEMANGLE_COMPONENT_LITERAL;
+
+  if (parsed_float)
+    {
+      /* It's a float since it contains a point or an exponent.  */
+      char c;
+
+      /* The GDB lexer checks the result of scanf at this point.  Not doing
+         this leaves our error checking slightly weaker but only for invalid
+         data.  */
+
+      /* See if it has `f' or `l' suffix (float or long double).  */
+
+      c = TOLOWER (p[len - 1]);
+
+      if (c == 'f')
+      	{
+      	  len--;
+      	  type = make_builtin_type ("float");
+      	}
+      else if (c == 'l')
+	{
+	  len--;
+	  type = make_builtin_type ("long double");
+	}
+      else if (ISDIGIT (c) || c == '.')
+	type = make_builtin_type ("double");
+      else
+	return ERROR;
+
+      name = make_name (p, len);
+      yylval.comp = fill_comp (literal_type, type, name);
+
+      return FLOAT;
+    }
+
+  /* This treats 0x1 and 1 as different literals.  We also do not
+     automatically generate unsigned types.  */
+
+  long_p = 0;
+  unsigned_p = 0;
+  while (len > 0)
+    {
+      if (p[len - 1] == 'l' || p[len - 1] == 'L')
+	{
+	  len--;
+	  long_p++;
+	  continue;
+	}
+      if (p[len - 1] == 'u' || p[len - 1] == 'U')
+	{
+	  len--;
+	  unsigned_p++;
+	  continue;
+	}
+      break;
+    }
+
+  if (long_p == 0)
+    {
+      unsigned_type = make_builtin_type ("unsigned int");
+      signed_type = make_builtin_type ("int");
+    }
+  else if (long_p == 1)
+    {
+      unsigned_type = make_builtin_type ("unsigned long");
+      signed_type = make_builtin_type ("long");
+    }
+  else
+    {
+      unsigned_type = make_builtin_type ("unsigned long long");
+      signed_type = make_builtin_type ("long long");
+    }
+
+   if (unsigned_p)
+     type = unsigned_type;
+   else
+     type = signed_type;
+
+   name = make_name (p, len);
+   yylval.comp = fill_comp (literal_type, type, name);
+
+   return INT;
+}
+
+static char backslashable[] = "abefnrtv";
+static char represented[] = "\a\b\e\f\n\r\t\v";
+
+/* Translate the backslash the way we would in the host character set.  */
+static int
+c_parse_backslash (int host_char, int *target_char)
+{
+  const char *ix;
+  ix = strchr (backslashable, host_char);
+  if (! ix)
+    return 0;
+  else
+    *target_char = represented[ix - backslashable];
+  return 1;
+}
+
+/* Parse a C escape sequence.  STRING_PTR points to a variable
+   containing a pointer to the string to parse.  That pointer
+   should point to the character after the \.  That pointer
+   is updated past the characters we use.  The value of the
+   escape sequence is returned.
+
+   A negative value means the sequence \ newline was seen,
+   which is supposed to be equivalent to nothing at all.
+
+   If \ is followed by a null character, we return a negative
+   value and leave the string pointer pointing at the null character.
+
+   If \ is followed by 000, we return 0 and leave the string pointer
+   after the zeros.  A value of 0 does not mean end of string.  */
+
+static int
+parse_escape (const char **string_ptr)
+{
+  int target_char;
+  int c = *(*string_ptr)++;
+  if (c_parse_backslash (c, &target_char))
+    return target_char;
+  else
+    switch (c)
+      {
+      case '\n':
+	return -2;
+      case 0:
+	(*string_ptr)--;
+	return 0;
+      case '^':
+	{
+	  c = *(*string_ptr)++;
+
+	  if (c == '?')
+	    return 0177;
+	  else if (c == '\\')
+	    target_char = parse_escape (string_ptr);
+	  else
+	    target_char = c;
+
+	  /* Now target_char is something like `c', and we want to find
+	     its control-character equivalent.  */
+	  target_char = target_char & 037;
+
+	  return target_char;
+	}
+
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+	{
+	  int i = c - '0';
+	  int count = 0;
+	  while (++count < 3)
+	    {
+	      c = (**string_ptr);
+	      if (c >= '0' && c <= '7')
+		{
+		  (*string_ptr)++;
+		  i *= 8;
+		  i += c - '0';
+		}
+	      else
+		{
+		  break;
+		}
+	    }
+	  return i;
+	}
+      default:
+	return target_char;
+      }
+}
+
+#define HANDLE_SPECIAL(string, comp)				\
+  if (strncmp (tokstart, string, sizeof (string) - 1) == 0)	\
+    {								\
+      lexptr = tokstart + sizeof (string) - 1;			\
+      yylval.lval = comp;					\
+      return DEMANGLER_SPECIAL;					\
+    }
+
+#define HANDLE_TOKEN2(string, token)			\
+  if (lexptr[1] == string[1])				\
+    {							\
+      lexptr += 2;					\
+      yylval.opname = string;				\
+      return token;					\
+    }      
+
+#define HANDLE_TOKEN3(string, token)			\
+  if (lexptr[1] == string[1] && lexptr[2] == string[2])	\
+    {							\
+      lexptr += 3;					\
+      yylval.opname = string;				\
+      return token;					\
+    }      
+
+/* Read one token, getting characters through LEXPTR.  */
+
+static int
+yylex (void)
+{
+  int c;
+  int namelen;
+  const char *tokstart, *tokptr;
+
+ retry:
+  prev_lexptr = lexptr;
+  tokstart = lexptr;
+
+  switch (c = *tokstart)
+    {
+    case 0:
+      return 0;
+
+    case ' ':
+    case '\t':
+    case '\n':
+      lexptr++;
+      goto retry;
+
+    case '\'':
+      /* We either have a character constant ('0' or '\177' for example)
+	 or we have a quoted symbol reference ('foo(int,int)' in C++
+	 for example). */
+      lexptr++;
+      c = *lexptr++;
+      if (c == '\\')
+	c = parse_escape (&lexptr);
+      else if (c == '\'')
+	{
+	  yyerror ("empty character constant");
+	  return ERROR;
+	}
+
+      c = *lexptr++;
+      if (c != '\'')
+	{
+	  yyerror ("invalid character constant");
+	  return ERROR;
+	}
+
+      /* FIXME: We should refer to a canonical form of the character,
+	 presumably the same one that appears in manglings - the decimal
+	 representation.  But if that isn't in our input then we have to
+	 allocate memory for it somewhere.  */
+      yylval.comp = fill_comp (DEMANGLE_COMPONENT_LITERAL,
+				 make_builtin_type ("char"),
+				 make_name (tokstart, lexptr - tokstart));
+
+      return INT;
+
+    case '(':
+      if (strncmp (tokstart, "(anonymous namespace)", 21) == 0)
+	{
+	  lexptr += 21;
+	  yylval.comp = make_name ("(anonymous namespace)",
+				     sizeof "(anonymous namespace)" - 1);
+	  return NAME;
+	}
+	/* FALL THROUGH */
+
+    case ')':
+    case ',':
+      lexptr++;
+      return c;
+
+    case '.':
+      if (lexptr[1] == '.' && lexptr[2] == '.')
+	{
+	  lexptr += 3;
+	  return ELLIPSIS;
+	}
+
+      /* Might be a floating point number.  */
+      if (lexptr[1] < '0' || lexptr[1] > '9')
+	goto symbol;		/* Nope, must be a symbol. */
+
+      goto try_number;
+
+    case '-':
+      HANDLE_TOKEN2 ("-=", ASSIGN_MODIFY);
+      HANDLE_TOKEN2 ("--", DECREMENT);
+      HANDLE_TOKEN2 ("->", ARROW);
+
+      /* For construction vtables.  This is kind of hokey.  */
+      if (strncmp (tokstart, "-in-", 4) == 0)
+	{
+	  lexptr += 4;
+	  return CONSTRUCTION_IN;
+	}
+
+      if (lexptr[1] < '0' || lexptr[1] > '9')
+	{
+	  lexptr++;
+	  return '-';
+	}
+      /* FALL THRU into number case.  */
+
+    try_number:
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      {
+	/* It's a number.  */
+	int got_dot = 0, got_e = 0, toktype;
+	const char *p = tokstart;
+	int hex = 0;
+
+	if (c == '-')
+	  p++;
+
+	if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+	  {
+	    p += 2;
+	    hex = 1;
+	  }
+	else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D'))
+	  {
+	    p += 2;
+	    hex = 0;
+	  }
+
+	for (;; ++p)
+	  {
+	    /* This test includes !hex because 'e' is a valid hex digit
+	       and thus does not indicate a floating point number when
+	       the radix is hex.  */
+	    if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+	      got_dot = got_e = 1;
+	    /* This test does not include !hex, because a '.' always indicates
+	       a decimal floating point number regardless of the radix.
+
+	       NOTE drow/2005-03-09: This comment is not accurate in C99;
+	       however, it's not clear that all the floating point support
+	       in this file is doing any good here.  */
+	    else if (!got_dot && *p == '.')
+	      got_dot = 1;
+	    else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+		     && (*p == '-' || *p == '+'))
+	      /* This is the sign of the exponent, not the end of the
+		 number.  */
+	      continue;
+	    /* We will take any letters or digits.  parse_number will
+	       complain if past the radix, or if L or U are not final.  */
+	    else if (! ISALNUM (*p))
+	      break;
+	  }
+	toktype = parse_number (tokstart, p - tokstart, got_dot|got_e);
+        if (toktype == ERROR)
+	  {
+	    char *err_copy = (char *) alloca (p - tokstart + 1);
+
+	    memcpy (err_copy, tokstart, p - tokstart);
+	    err_copy[p - tokstart] = 0;
+	    yyerror ("invalid number");
+	    return ERROR;
+	  }
+	lexptr = p;
+	return toktype;
+      }
+
+    case '+':
+      HANDLE_TOKEN2 ("+=", ASSIGN_MODIFY);
+      HANDLE_TOKEN2 ("++", INCREMENT);
+      lexptr++;
+      return c;
+    case '*':
+      HANDLE_TOKEN2 ("*=", ASSIGN_MODIFY);
+      lexptr++;
+      return c;
+    case '/':
+      HANDLE_TOKEN2 ("/=", ASSIGN_MODIFY);
+      lexptr++;
+      return c;
+    case '%':
+      HANDLE_TOKEN2 ("%=", ASSIGN_MODIFY);
+      lexptr++;
+      return c;
+    case '|':
+      HANDLE_TOKEN2 ("|=", ASSIGN_MODIFY);
+      HANDLE_TOKEN2 ("||", OROR);
+      lexptr++;
+      return c;
+    case '&':
+      HANDLE_TOKEN2 ("&=", ASSIGN_MODIFY);
+      HANDLE_TOKEN2 ("&&", ANDAND);
+      lexptr++;
+      return c;
+    case '^':
+      HANDLE_TOKEN2 ("^=", ASSIGN_MODIFY);
+      lexptr++;
+      return c;
+    case '!':
+      HANDLE_TOKEN2 ("!=", NOTEQUAL);
+      lexptr++;
+      return c;
+    case '<':
+      HANDLE_TOKEN3 ("<<=", ASSIGN_MODIFY);
+      HANDLE_TOKEN2 ("<=", LEQ);
+      HANDLE_TOKEN2 ("<<", LSH);
+      lexptr++;
+      return c;
+    case '>':
+      HANDLE_TOKEN3 (">>=", ASSIGN_MODIFY);
+      HANDLE_TOKEN2 (">=", GEQ);
+      HANDLE_TOKEN2 (">>", RSH);
+      lexptr++;
+      return c;
+    case '=':
+      HANDLE_TOKEN2 ("==", EQUAL);
+      lexptr++;
+      return c;
+    case ':':
+      HANDLE_TOKEN2 ("::", COLONCOLON);
+      lexptr++;
+      return c;
+
+    case '[':
+    case ']':
+    case '?':
+    case '@':
+    case '~':
+    case '{':
+    case '}':
+    symbol:
+      lexptr++;
+      return c;
+
+    case '"':
+      /* These can't occur in C++ names.  */
+      yyerror ("unexpected string literal");
+      return ERROR;
+    }
+
+  if (!(c == '_' || c == '$' || ISALPHA (c)))
+    {
+      /* We must have come across a bad character (e.g. ';').  */
+      yyerror ("invalid character");
+      return ERROR;
+    }
+
+  /* It's a name.  See how long it is.  */
+  namelen = 0;
+  do
+    c = tokstart[++namelen];
+  while (ISALNUM (c) || c == '_' || c == '$');
+
+  lexptr += namelen;
+
+  /* Catch specific keywords.  Notice that some of the keywords contain
+     spaces, and are sorted by the length of the first word.  They must
+     all include a trailing space in the string comparison.  */
+  switch (namelen)
+    {
+    case 16:
+      if (strncmp (tokstart, "reinterpret_cast", 16) == 0)
+        return REINTERPRET_CAST;
+      break;
+    case 12:
+      if (strncmp (tokstart, "construction vtable for ", 24) == 0)
+	{
+	  lexptr = tokstart + 24;
+	  return CONSTRUCTION_VTABLE;
+	}
+      if (strncmp (tokstart, "dynamic_cast", 12) == 0)
+        return DYNAMIC_CAST;
+      break;
+    case 11:
+      if (strncmp (tokstart, "static_cast", 11) == 0)
+        return STATIC_CAST;
+      break;
+    case 9:
+      HANDLE_SPECIAL ("covariant return thunk to ", DEMANGLE_COMPONENT_COVARIANT_THUNK);
+      HANDLE_SPECIAL ("reference temporary for ", DEMANGLE_COMPONENT_REFTEMP);
+      break;
+    case 8:
+      HANDLE_SPECIAL ("typeinfo for ", DEMANGLE_COMPONENT_TYPEINFO);
+      HANDLE_SPECIAL ("typeinfo fn for ", DEMANGLE_COMPONENT_TYPEINFO_FN);
+      HANDLE_SPECIAL ("typeinfo name for ", DEMANGLE_COMPONENT_TYPEINFO_NAME);
+      if (strncmp (tokstart, "operator", 8) == 0)
+	return OPERATOR;
+      if (strncmp (tokstart, "restrict", 8) == 0)
+	return RESTRICT;
+      if (strncmp (tokstart, "unsigned", 8) == 0)
+	return UNSIGNED;
+      if (strncmp (tokstart, "template", 8) == 0)
+	return TEMPLATE;
+      if (strncmp (tokstart, "volatile", 8) == 0)
+	return VOLATILE_KEYWORD;
+      break;
+    case 7:
+      HANDLE_SPECIAL ("virtual thunk to ", DEMANGLE_COMPONENT_VIRTUAL_THUNK);
+      if (strncmp (tokstart, "wchar_t", 7) == 0)
+	return WCHAR_T;
+      break;
+    case 6:
+      if (strncmp (tokstart, "global constructors keyed to ", 29) == 0)
+	{
+	  const char *p;
+	  lexptr = tokstart + 29;
+	  yylval.typed_val_int.val = GLOBAL_CONSTRUCTORS;
+	  /* Find the end of the symbol.  */
+	  p = symbol_end (lexptr);
+	  yylval.typed_val_int.type = make_name (lexptr, p - lexptr);
+	  lexptr = p;
+	  return GLOBAL;
+	}
+      if (strncmp (tokstart, "global destructors keyed to ", 28) == 0)
+	{
+	  const char *p;
+	  lexptr = tokstart + 28;
+	  yylval.typed_val_int.val = GLOBAL_DESTRUCTORS;
+	  /* Find the end of the symbol.  */
+	  p = symbol_end (lexptr);
+	  yylval.typed_val_int.type = make_name (lexptr, p - lexptr);
+	  lexptr = p;
+	  return GLOBAL;
+	}
+
+      HANDLE_SPECIAL ("vtable for ", DEMANGLE_COMPONENT_VTABLE);
+      if (strncmp (tokstart, "delete", 6) == 0)
+	return DELETE;
+      if (strncmp (tokstart, "struct", 6) == 0)
+	return STRUCT;
+      if (strncmp (tokstart, "signed", 6) == 0)
+	return SIGNED_KEYWORD;
+      if (strncmp (tokstart, "sizeof", 6) == 0)
+	return SIZEOF;
+      if (strncmp (tokstart, "double", 6) == 0)
+	return DOUBLE_KEYWORD;
+      break;
+    case 5:
+      HANDLE_SPECIAL ("guard variable for ", DEMANGLE_COMPONENT_GUARD);
+      if (strncmp (tokstart, "false", 5) == 0)
+	return FALSEKEYWORD;
+      if (strncmp (tokstart, "class", 5) == 0)
+	return CLASS;
+      if (strncmp (tokstart, "union", 5) == 0)
+	return UNION;
+      if (strncmp (tokstart, "float", 5) == 0)
+	return FLOAT_KEYWORD;
+      if (strncmp (tokstart, "short", 5) == 0)
+	return SHORT;
+      if (strncmp (tokstart, "const", 5) == 0)
+	return CONST_KEYWORD;
+      break;
+    case 4:
+      if (strncmp (tokstart, "void", 4) == 0)
+	return VOID;
+      if (strncmp (tokstart, "bool", 4) == 0)
+	return BOOL;
+      if (strncmp (tokstart, "char", 4) == 0)
+	return CHAR;
+      if (strncmp (tokstart, "enum", 4) == 0)
+	return ENUM;
+      if (strncmp (tokstart, "long", 4) == 0)
+	return LONG;
+      if (strncmp (tokstart, "true", 4) == 0)
+	return TRUEKEYWORD;
+      break;
+    case 3:
+      HANDLE_SPECIAL ("VTT for ", DEMANGLE_COMPONENT_VTT);
+      HANDLE_SPECIAL ("non-virtual thunk to ", DEMANGLE_COMPONENT_THUNK);
+      if (strncmp (tokstart, "new", 3) == 0)
+	return NEW;
+      if (strncmp (tokstart, "int", 3) == 0)
+	return INT_KEYWORD;
+      break;
+    default:
+      break;
+    }
+
+  yylval.comp = make_name (tokstart, namelen);
+  return NAME;
+}
+
+static void
+yyerror (char *msg)
+{
+  if (global_errmsg)
+    return;
+
+  error_lexptr = prev_lexptr;
+  global_errmsg = msg ? msg : "parse error";
+}
+
+/* Allocate all the components we'll need to build a tree.  We generally
+   allocate too many components, but the extra memory usage doesn't hurt
+   because the trees are temporary.  If we start keeping the trees for
+   a longer lifetime we'll need to be cleverer.  */
+static struct demangle_info *
+allocate_info (int comps)
+{
+  struct demangle_info *ret;
+
+  ret = malloc (sizeof (struct demangle_info)
+		+ sizeof (struct demangle_component) * (comps - 1));
+  ret->used = 0;
+  return ret;
+}
+
+/* Convert RESULT to a string.  The return value is allocated
+   using xmalloc.  ESTIMATED_LEN is used only as a guide to the
+   length of the result.  This functions handles a few cases that
+   cplus_demangle_print does not, specifically the global destructor
+   and constructor labels.  */
+
+char *
+cp_comp_to_string (struct demangle_component *result, int estimated_len)
+{
+  char *str, *prefix = NULL, *buf;
+  size_t err = 0;
+
+  if (result->type == GLOBAL_DESTRUCTORS)
+    {
+      result = d_left (result);
+      prefix = "global destructors keyed to ";
+    }
+  else if (result->type == GLOBAL_CONSTRUCTORS)
+    {
+      result = d_left (result);
+      prefix = "global constructors keyed to ";
+    }
+
+  str = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, result, estimated_len, &err);
+  if (str == NULL)
+    return NULL;
+
+  if (prefix == NULL)
+    return str;
+
+  buf = malloc (strlen (str) + strlen (prefix) + 1);
+  strcpy (buf, prefix);
+  strcat (buf, str);
+  free (str);
+  return (buf);
+}
+
+/* Convert a demangled name to a demangle_component tree.  *MEMORY is set to the
+   block of used memory that should be freed when finished with the
+   tree.  On error, NULL is returned, and an error message will be
+   set in *ERRMSG (which does not need to be freed).  */
+
+struct demangle_component *
+cp_demangled_name_to_comp (const char *demangled_name, void **memory,
+			   const char **errmsg)
+{
+  static char errbuf[60];
+  struct demangle_component *result;
+
+  int len = strlen (demangled_name);
+
+  len = len + len / 8;
+  prev_lexptr = lexptr = demangled_name;
+  error_lexptr = NULL;
+  global_errmsg = NULL;
+
+  demangle_info = allocate_info (len);
+
+  if (yyparse ())
+    {
+      if (global_errmsg && errmsg)
+	{
+	  snprintf (errbuf, sizeof (errbuf) - 2, "%s, near `%s",
+		    global_errmsg, error_lexptr);
+	  strcat (errbuf, "'");
+	  *errmsg = errbuf;
+	}
+      free (demangle_info);
+      return NULL;
+    }
+
+  *memory = demangle_info;
+  result = global_result;
+  global_result = NULL;
+
+  return result;
+}
+
+#ifdef TEST_CPNAMES
+
+static void
+cp_print (struct demangle_component *result)
+{
+  char *str;
+  size_t err = 0;
+
+  if (result->type == GLOBAL_DESTRUCTORS)
+    {
+      result = d_left (result);
+      fputs ("global destructors keyed to ", stdout);
+    }
+  else if (result->type == GLOBAL_CONSTRUCTORS)
+    {
+      result = d_left (result);
+      fputs ("global constructors keyed to ", stdout);
+    }
+
+  str = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, result, 64, &err);
+  if (str == NULL)
+    return;
+
+  fputs (str, stdout);
+
+  free (str);
+}
+
+static char
+trim_chars (char *lexptr, char **extra_chars)
+{
+  char *p = (char *) symbol_end (lexptr);
+  char c = 0;
+
+  if (*p)
+    {
+      c = *p;
+      *p = 0;
+      *extra_chars = p + 1;
+    }
+
+  return c;
+}
+
+int
+main (int argc, char **argv)
+{
+  char *str2, *extra_chars, c;
+  char buf[65536];
+  int arg;
+  const char *errmsg;
+  void *memory;
+  struct demangle_component *result;
+
+  arg = 1;
+  if (argv[arg] && strcmp (argv[arg], "--debug") == 0)
+    {
+      yydebug = 1;
+      arg++;
+    }
+
+  if (argv[arg] == NULL)
+    while (fgets (buf, 65536, stdin) != NULL)
+      {
+	int len;
+	buf[strlen (buf) - 1] = 0;
+	/* Use DMGL_VERBOSE to get expanded standard substitutions.  */
+	c = trim_chars (buf, &extra_chars);
+	str2 = cplus_demangle (buf, DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE);
+	if (str2 == NULL)
+	  {
+	    /* printf ("Demangling error\n"); */
+	    if (c)
+	      printf ("%s%c%s\n", buf, c, extra_chars);
+	    else
+	      printf ("%s\n", buf);
+	    continue;
+	  }
+	result = cp_demangled_name_to_comp (str2, &memory, &errmsg);
+	if (result == NULL)
+	  {
+	    fputs (errmsg, stderr);
+	    fputc ('\n', stderr);
+	    continue;
+	  }
+
+	cp_print (result);
+	free (memory);
+
+	free (str2);
+	if (c)
+	  {
+	    putchar (c);
+	    fputs (extra_chars, stdout);
+	  }
+	putchar ('\n');
+      }
+  else
+    {
+      result = cp_demangled_name_to_comp (argv[arg], &memory, &errmsg);
+      if (result == NULL)
+	{
+	  fputs (errmsg, stderr);
+	  fputc ('\n', stderr);
+	  return 0;
+	}
+      cp_print (result);
+      putchar ('\n');
+      free (memory);
+    }
+  return 0;
+}
+
+#endif
Index: cp-support.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/cp-support.c,v
retrieving revision 1.18
diff -u -p -r1.18 cp-support.c
--- cp-support.c	15 Feb 2005 15:49:08 -0000	1.18
+++ cp-support.c	10 Mar 2005 03:56:04 -0000
@@ -1,5 +1,5 @@
 /* Helper routines for C++ support in GDB.
-   Copyright 2002, 2003 Free Software Foundation, Inc.
+   Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
    Contributed by MontaVista Software.
 
@@ -35,9 +35,10 @@
 #include "complaints.h"
 #include "gdbtypes.h"
 
-/* Functions related to demangled name parsing.  */
+#define d_left(dc) (dc)->u.s_binary.left
+#define d_right(dc) (dc)->u.s_binary.right
 
-static const char *find_last_component (const char *name);
+/* Functions related to demangled name parsing.  */
 
 static unsigned int cp_find_first_component_aux (const char *name,
 						 int permissive);
@@ -71,78 +72,76 @@ struct cmd_list_element *maint_cplus_cmd
 static void maint_cplus_command (char *arg, int from_tty);
 static void first_component_command (char *arg, int from_tty);
 
-/* Here are some random pieces of trivia to keep in mind while trying
-   to take apart demangled names:
-
-   - Names can contain function arguments or templates, so the process
-     has to be, to some extent recursive: maybe keep track of your
-     depth based on encountering <> and ().
-
-   - Parentheses don't just have to happen at the end of a name: they
-     can occur even if the name in question isn't a function, because
-     a template argument might be a type that's a function.
-
-   - Conversely, even if you're trying to deal with a function, its
-     demangled name might not end with ')': it could be a const or
-     volatile class method, in which case it ends with "const" or
-     "volatile".
-
-   - Parentheses are also used in anonymous namespaces: a variable
-     'foo' in an anonymous namespace gets demangled as "(anonymous
-     namespace)::foo".
+/* Return the canonicalized form of STRING, or NULL if STRING can not be
+   parsed.  The return value is allocated via xmalloc.
 
-   - And operator names can contain parentheses or angle brackets.  */
+   drow/2005-03-07: Should we also return NULL for things that trivially do
+   not require any change?  e.g. simple identifiers.  This could be more
+   efficient.  */
 
-/* FIXME: carlton/2003-03-13: We have several functions here with
-   overlapping functionality; can we combine them?  Also, do they
-   handle all the above considerations correctly?  */
+char *
+cp_canonicalize_string (const char *string)
+{
+  void *storage;
+  struct demangle_component *ret_comp;
+  char *ret;
+  int len = strlen (string);
 
-/* Find the last component of the demangled C++ name NAME.  NAME
-   must be a method name including arguments, in order to correctly
-   locate the last component.
+  len = len + len / 8;
 
-   This function return a pointer to the first colon before the
-   last component, or NULL if the name had only one component.  */
+  ret_comp = cp_demangled_name_to_comp (string, &storage, NULL);
+  if (ret_comp == NULL)
+    return NULL;
 
-static const char *
-find_last_component (const char *name)
-{
-  const char *p;
-  int depth;
+  ret = cp_comp_to_string (ret_comp, len);
 
-  /* Functions can have local classes, so we need to find the
-     beginning of the last argument list, not the end of the first
-     one.  */
-  p = name + strlen (name) - 1;
-  while (p > name && *p != ')')
-    p--;
+  xfree (storage);
 
-  if (p == name)
-    return NULL;
+  return ret;
+}
 
-  /* P now points at the `)' at the end of the argument list.  Walk
-     back to the beginning.  */
-  p--;
-  depth = 1;
-  while (p > name && depth > 0)
+/* Convert a mangled name to a demangle_component tree.  *MEMORY is set to the
+   block of used memory that should be freed when finished with the tree. 
+   DEMANGLED_P is set to the char * that should be freed when finished with
+   the tree, or NULL if none was needed.  OPTIONS will be passed to the
+   demangler.  */
+
+static struct demangle_component *
+mangled_name_to_comp (const char *mangled_name, int options,
+		      void **memory, char **demangled_p)
+{
+  struct demangle_component *ret;
+  char *demangled_name;
+  int len;
+
+  /* If it looks like a v3 mangled name, then try to go directly
+     to trees.  */
+  if (mangled_name[0] == '_' && mangled_name[1] == 'Z')
     {
-      if (*p == '<' || *p == '(')
-	depth--;
-      else if (*p == '>' || *p == ')')
-	depth++;
-      p--;
+      ret = cplus_demangle_v3_components (mangled_name, options, memory);
+      if (ret)
+	{
+	  *demangled_p = NULL;
+	  return ret;
+	}
     }
 
-  if (p == name)
-    return NULL;
-
-  while (p > name && *p != ':')
-    p--;
+  /* If it doesn't, or if that failed, then try to demangle the name.  */
+  demangled_name = cplus_demangle (mangled_name, options);
+  if (demangled_name == NULL)
+   return NULL;
+  
+  /* If we could demangle the name, parse it to build the component tree.  */
+  ret = cp_demangled_name_to_comp (demangled_name, memory, NULL);
 
-  if (p == name || p == name + 1 || p[-1] != ':')
-    return NULL;
+  if (ret == NULL)
+    {
+      free (demangled_name);
+      return NULL;
+    }
 
-  return p - 1;
+  *demangled_p = demangled_name;
+  return ret;
 }
 
 /* Return the name of the class containing method PHYSNAME.  */
@@ -150,23 +149,66 @@ find_last_component (const char *name)
 char *
 cp_class_name_from_physname (const char *physname)
 {
-  char *ret = NULL;
-  const char *end;
-  int depth = 0;
-  char *demangled_name = cplus_demangle (physname, DMGL_ANSI | DMGL_PARAMS);
-
-  if (demangled_name == NULL)
+  void *storage;
+  char *demangled_name = NULL, *ret;
+  struct demangle_component *ret_comp, *prev_comp;
+  int done;
+
+  ret_comp = mangled_name_to_comp (physname, DMGL_ANSI, &storage,
+				   &demangled_name);
+  if (ret_comp == NULL)
     return NULL;
 
-  end = find_last_component (demangled_name);
-  if (end != NULL)
-    {
-      ret = xmalloc (end - demangled_name + 1);
-      memcpy (ret, demangled_name, end - demangled_name);
-      ret[end - demangled_name] = '\0';
-    }
-
-  xfree (demangled_name);
+  done = 0;
+  prev_comp = NULL;
+  while (!done)
+    switch (ret_comp->type)
+      {
+      case DEMANGLE_COMPONENT_TYPED_NAME:
+	prev_comp = NULL;
+        ret_comp = d_right (ret_comp);
+        break;
+      case DEMANGLE_COMPONENT_QUAL_NAME:
+      case DEMANGLE_COMPONENT_LOCAL_NAME:
+	prev_comp = ret_comp;
+        ret_comp = d_right (ret_comp);
+        break;
+      case DEMANGLE_COMPONENT_CONST:
+      case DEMANGLE_COMPONENT_RESTRICT:
+      case DEMANGLE_COMPONENT_VOLATILE:
+      case DEMANGLE_COMPONENT_CONST_THIS:
+      case DEMANGLE_COMPONENT_RESTRICT_THIS:
+      case DEMANGLE_COMPONENT_VOLATILE_THIS:
+      case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+	prev_comp = NULL;
+        ret_comp = d_left (ret_comp);
+        break;
+      case DEMANGLE_COMPONENT_NAME:
+      case DEMANGLE_COMPONENT_TEMPLATE:
+      case DEMANGLE_COMPONENT_CTOR:
+      case DEMANGLE_COMPONENT_DTOR:
+      case DEMANGLE_COMPONENT_OPERATOR:
+      case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+	done = 1;
+	break;
+      default:
+	done = 1;
+	prev_comp = NULL;
+	ret_comp = NULL;
+	break;
+      }
+
+  ret = NULL;
+  if (prev_comp != NULL)
+    {
+      *prev_comp = *d_left (prev_comp);
+      /* The ten is completely arbitrary; we don't have a good estimate.  */
+      ret = cp_comp_to_string (prev_comp, 10);
+    }
+
+  xfree (storage);
+  if (demangled_name)
+    xfree (demangled_name);
   return ret;
 }
 
@@ -175,42 +217,85 @@ cp_class_name_from_physname (const char 
 char *
 method_name_from_physname (const char *physname)
 {
-  char *ret = NULL;
-  const char *end;
-  int depth = 0;
-  char *demangled_name = cplus_demangle (physname, DMGL_ANSI | DMGL_PARAMS);
-
-  if (demangled_name == NULL)
+  void *storage;
+  char *demangled_name = NULL, *ret;
+  struct demangle_component *ret_comp;
+  int done;
+
+  ret_comp = mangled_name_to_comp (physname, DMGL_ANSI, &storage,
+				   &demangled_name);
+  if (ret_comp == NULL)
     return NULL;
 
-  end = find_last_component (demangled_name);
-  if (end != NULL)
-    {
-      char *args;
-      int len;
+  done = 0;
+  while (!done)
+    switch (ret_comp->type)
+      {
+      case DEMANGLE_COMPONENT_QUAL_NAME:
+      case DEMANGLE_COMPONENT_LOCAL_NAME:
+      case DEMANGLE_COMPONENT_TYPED_NAME:
+        ret_comp = d_right (ret_comp);
+        break;
+      case DEMANGLE_COMPONENT_CONST:
+      case DEMANGLE_COMPONENT_RESTRICT:
+      case DEMANGLE_COMPONENT_VOLATILE:
+      case DEMANGLE_COMPONENT_CONST_THIS:
+      case DEMANGLE_COMPONENT_RESTRICT_THIS:
+      case DEMANGLE_COMPONENT_VOLATILE_THIS:
+      case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+        ret_comp = d_left (ret_comp);
+        break;
+      case DEMANGLE_COMPONENT_NAME:
+      case DEMANGLE_COMPONENT_TEMPLATE:
+      case DEMANGLE_COMPONENT_CTOR:
+      case DEMANGLE_COMPONENT_DTOR:
+      case DEMANGLE_COMPONENT_OPERATOR:
+      case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+	done = 1;
+	break;
+      default:
+	done = 1;
+	ret_comp = NULL;
+	break;
+      }
+
+  ret = NULL;
+  if (ret_comp != NULL)
+    /* The ten is completely arbitrary; we don't have a good estimate.  */
+    ret = cp_comp_to_string (ret_comp, 10);
+
+  xfree (storage);
+  if (demangled_name)
+    xfree (demangled_name);
+  return ret;
+}
 
-      /* Skip "::".  */
-      end = end + 2;
+/* Here are some random pieces of trivia to keep in mind while trying
+   to take apart demangled names:
 
-      /* Find the argument list, if any.  */
-      args = strchr (end, '(');
-      if (args == NULL)
-	len = strlen (end + 2);
-      else
-	{
-	  args --;
-	  while (*args == ' ')
-	    args --;
-	  len = args - end + 1;
-	}
-      ret = xmalloc (len + 1);
-      memcpy (ret, end, len);
-      ret[len] = 0;
-    }
+   - Names can contain function arguments or templates, so the process
+     has to be, to some extent recursive: maybe keep track of your
+     depth based on encountering <> and ().
+
+   - Parentheses don't just have to happen at the end of a name: they
+     can occur even if the name in question isn't a function, because
+     a template argument might be a type that's a function.
+
+   - Conversely, even if you're trying to deal with a function, its
+     demangled name might not end with ')': it could be a const or
+     volatile class method, in which case it ends with "const" or
+     "volatile".
+
+   - Parentheses are also used in anonymous namespaces: a variable
+     'foo' in an anonymous namespace gets demangled as "(anonymous
+     namespace)::foo".
+
+   - And operator names can contain parentheses or angle brackets.  */
+
+/* FIXME: carlton/2003-03-13: We have several functions here with
+   overlapping functionality; can we combine them?  Also, do they
+   handle all the above considerations correctly?  */
 
-  xfree (demangled_name);
-  return ret;
-}
 
 /* This returns the length of first component of NAME, which should be
    the demangled name of a C++ variable/function/method/etc.
Index: cp-support.h
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/cp-support.h,v
retrieving revision 1.15
diff -u -p -r1.15 cp-support.h
--- cp-support.h	9 Feb 2005 00:04:28 -0000	1.15
+++ cp-support.h	10 Mar 2005 03:53:45 -0000
@@ -1,5 +1,5 @@
 /* Helper routines for C++ support in GDB.
-   Copyright 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
    Contributed by MontaVista Software.
    Namespace support contributed by David Carlton.
@@ -35,6 +35,7 @@ struct obstack;
 struct block;
 struct objfile;
 struct type;
+struct demangle_component;
 
 /* This struct is designed to store data from using directives.  It
    says that names from namespace INNER should be visible within
@@ -52,6 +53,8 @@ struct using_direct
 
 /* Functions from cp-support.c.  */
 
+extern char *cp_canonicalize_string (const char *string);
+
 extern char *cp_class_name_from_physname (const char *physname);
 
 extern char *method_name_from_physname (const char *physname);
@@ -113,6 +116,14 @@ extern void cp_check_possible_namespace_
 
 struct type *cp_lookup_transparent_type (const char *name);
 
+/* Functions from cp-names.y.  */
+
+extern struct demangle_component *cp_demangled_name_to_comp
+  (const char *demangled_name, void **memory_p, const char **errmsg);
+
+extern char *cp_comp_to_string (struct demangle_component *result,
+				int estimated_len);
+
 /* The list of "maint cplus" commands.  */
 
 extern struct cmd_list_element *maint_cplus_cmd_list;
Index: config/djgpp/fnchange.lst
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/config/djgpp/fnchange.lst,v
retrieving revision 1.86
diff -u -p -r1.86 fnchange.lst
--- config/djgpp/fnchange.lst	18 Dec 2004 14:53:39 -0000	1.86
+++ config/djgpp/fnchange.lst	9 Mar 2005 19:57:33 -0000
@@ -103,6 +103,7 @@
 @V@/gdb/config/rs6000/tm-rs6000.h @V@/gdb/config/rs6000/tm-rs6k.h
 @V@/gdb/config/rs6000/tm-rs6000ly.h @V@/gdb/config/rs6000/tm-rs6kly.h
 @V@/gdb/config/sparc/tm-sparclynx.h @V@/gdb/config/sparc/tm-splynx.h
+@V@/gdb/cp-names.c @V@/gdb/cpnames.c
 @V@/gdb/f-exp.tab.c @V@/gdb/f-exp_tab.c
 @V@/gdb/gdbtk/ChangeLog-2001 @V@/gdb/gdbtk/ChangeLog.001
 @V@/gdb/gdbtk/ChangeLog-2002 @V@/gdb/gdbtk/ChangeLog.002


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]