* Makefile.in (SFILES): Add cp-names.y.
authorDaniel Jacobowitz <drow@false.org>
Fri, 11 Mar 2005 02:24:23 +0000 (02:24 +0000)
committerDaniel Jacobowitz <drow@false.org>
Fri, 11 Mar 2005 02:24:23 +0000 (02:24 +0000)
(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.

gdb/ChangeLog
gdb/Makefile.in
gdb/config/djgpp/fnchange.lst
gdb/cp-names.y [new file with mode: 0644]
gdb/cp-support.c
gdb/cp-support.h

index 3f4196e..7ed6237 100644 (file)
@@ -1,6 +1,25 @@
+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.
+
 2005-03-10  Bob Rossi  <bob@brasko.net>
 
-       * main.c(print_gdb_help): remove the --[no]sync help message
+       * main.c (print_gdb_help): Remove the --[no]async help message.
 
 2005-03-10  Mark Kettenis  <kettenis@gnu.org>
 
index 064dd88..b08aa4d 100644 (file)
@@ -519,6 +519,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c  \
        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 $(elf_reloc_macros_h)
 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-frv.h
 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) $(command_h) $(gdbcore_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 fe0a509..70f42e2 100644 (file)
 @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
diff --git a/gdb/cp-names.y b/gdb/cp-names.y
new file mode 100644 (file)
index 0000000..589d8d9
--- /dev/null
@@ -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
+
+\f
+%%
+
+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 ecda4a5..9f39b5a 100644 (file)
@@ -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.
 
 #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_list = NULL;
 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:
+/* Return the canonicalized form of STRING, or NULL if STRING can not be
+   parsed.  The return value is allocated via xmalloc.
 
-   - 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 ().
+   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.  */
 
-   - 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.
+char *
+cp_canonicalize_string (const char *string)
+{
+  void *storage;
+  struct demangle_component *ret_comp;
+  char *ret;
+  int len = strlen (string);
 
-   - 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".
+  len = len + len / 8;
 
-   - Parentheses are also used in anonymous namespaces: a variable
-     'foo' in an anonymous namespace gets demangled as "(anonymous
-     namespace)::foo".
+  ret_comp = cp_demangled_name_to_comp (string, &storage, NULL);
+  if (ret_comp == NULL)
+    return NULL;
 
-   - And operator names can contain parentheses or angle brackets.  */
+  ret = cp_comp_to_string (ret_comp, len);
 
-/* 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 (storage);
 
-/* 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.
+  return ret;
+}
 
-   This function return a pointer to the first colon before the
-   last component, or NULL if the name had only one component.  */
+/* 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 const char *
-find_last_component (const char *name)
+static struct demangle_component *
+mangled_name_to_comp (const char *mangled_name, int options,
+                     void **memory, char **demangled_p)
 {
-  const char *p;
-  int depth;
-
-  /* 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--;
-
-  if (p == name)
-    return NULL;
+  struct demangle_component *ret;
+  char *demangled_name;
+  int len;
 
-  /* 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)
+  /* 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)
+  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)
     {
-      ret = xmalloc (end - demangled_name + 1);
-      memcpy (ret, demangled_name, end - demangled_name);
-      ret[end - demangled_name] = '\0';
+      *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 (demangled_name);
+  xfree (storage);
+  if (demangled_name)
+    xfree (demangled_name);
   return ret;
 }
 
@@ -175,42 +217,85 @@ cp_class_name_from_physname (const char *physname)
 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 f463d59..4f8ae2c 100644 (file)
@@ -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_symbols (const char *name,
 
 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;