|  | /* YACC parser for C expressions, for GDB. | 
|  | Copyright (C) 1986-2022 Free Software Foundation, Inc. | 
|  |  | 
|  | 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 3 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, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* Parse a C expression from text in a string, | 
|  | and return the result as a  struct expression  pointer. | 
|  | That structure contains arithmetic operations in reverse polish, | 
|  | with constants represented by operations that are followed by special data. | 
|  | See expression.h for the details of the format. | 
|  | What is important here is that it can be built up sequentially | 
|  | during the process of parsing; the lower levels of the tree always | 
|  | come first in the result. | 
|  |  | 
|  | 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 "defs.h" | 
|  | #include <ctype.h> | 
|  | #include "expression.h" | 
|  | #include "value.h" | 
|  | #include "parser-defs.h" | 
|  | #include "language.h" | 
|  | #include "c-lang.h" | 
|  | #include "c-support.h" | 
|  | #include "bfd.h" /* Required by objfiles.h.  */ | 
|  | #include "symfile.h" /* Required by objfiles.h.  */ | 
|  | #include "objfiles.h" /* For have_full_symbols and have_partial_symbols */ | 
|  | #include "charset.h" | 
|  | #include "block.h" | 
|  | #include "cp-support.h" | 
|  | #include "macroscope.h" | 
|  | #include "objc-lang.h" | 
|  | #include "typeprint.h" | 
|  | #include "cp-abi.h" | 
|  | #include "type-stack.h" | 
|  | #include "target-float.h" | 
|  | #include "c-exp.h" | 
|  |  | 
|  | #define parse_type(ps) builtin_type (ps->gdbarch ()) | 
|  |  | 
|  | /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, | 
|  | etc).  */ | 
|  | #define GDB_YY_REMAP_PREFIX c_ | 
|  | #include "yy-remap.h" | 
|  |  | 
|  | /* The state of the parser, used internally when we are parsing the | 
|  | expression.  */ | 
|  |  | 
|  | static struct parser_state *pstate = NULL; | 
|  |  | 
|  | /* Data that must be held for the duration of a parse.  */ | 
|  |  | 
|  | struct c_parse_state | 
|  | { | 
|  | /* These are used to hold type lists and type stacks that are | 
|  | allocated during the parse.  */ | 
|  | std::vector<std::unique_ptr<std::vector<struct type *>>> type_lists; | 
|  | std::vector<std::unique_ptr<struct type_stack>> type_stacks; | 
|  |  | 
|  | /* Storage for some strings allocated during the parse.  */ | 
|  | std::vector<gdb::unique_xmalloc_ptr<char>> strings; | 
|  |  | 
|  | /* When we find that lexptr (the global var defined in parse.c) is | 
|  | pointing at a macro invocation, we expand the invocation, and call | 
|  | scan_macro_expansion to save the old lexptr here and point lexptr | 
|  | into the expanded text.  When we reach the end of that, we call | 
|  | end_macro_expansion to pop back to the value we saved here.  The | 
|  | macro expansion code promises to return only fully-expanded text, | 
|  | so we don't need to "push" more than one level. | 
|  |  | 
|  | This is disgusting, of course.  It would be cleaner to do all macro | 
|  | expansion beforehand, and then hand that to lexptr.  But we don't | 
|  | really know where the expression ends.  Remember, in a command like | 
|  |  | 
|  | (gdb) break *ADDRESS if CONDITION | 
|  |  | 
|  | we evaluate ADDRESS in the scope of the current frame, but we | 
|  | evaluate CONDITION in the scope of the breakpoint's location.  So | 
|  | it's simply wrong to try to macro-expand the whole thing at once.  */ | 
|  | const char *macro_original_text = nullptr; | 
|  |  | 
|  | /* We save all intermediate macro expansions on this obstack for the | 
|  | duration of a single parse.  The expansion text may sometimes have | 
|  | to live past the end of the expansion, due to yacc lookahead. | 
|  | Rather than try to be clever about saving the data for a single | 
|  | token, we simply keep it all and delete it after parsing has | 
|  | completed.  */ | 
|  | auto_obstack expansion_obstack; | 
|  |  | 
|  | /* The type stack.  */ | 
|  | struct type_stack type_stack; | 
|  | }; | 
|  |  | 
|  | /* This is set and cleared in c_parse.  */ | 
|  |  | 
|  | static struct c_parse_state *cpstate; | 
|  |  | 
|  | int yyparse (void); | 
|  |  | 
|  | static int yylex (void); | 
|  |  | 
|  | static void yyerror (const char *); | 
|  |  | 
|  | static int type_aggregate_p (struct type *); | 
|  |  | 
|  | using namespace expr; | 
|  | %} | 
|  |  | 
|  | /* Although the yacc "value" of an expression is not used, | 
|  | since the result is stored in the structure being created, | 
|  | other node types do have values.  */ | 
|  |  | 
|  | %union | 
|  | { | 
|  | LONGEST lval; | 
|  | struct { | 
|  | LONGEST val; | 
|  | struct type *type; | 
|  | } typed_val_int; | 
|  | struct { | 
|  | gdb_byte val[16]; | 
|  | struct type *type; | 
|  | } typed_val_float; | 
|  | struct type *tval; | 
|  | struct stoken sval; | 
|  | struct typed_stoken tsval; | 
|  | struct ttype tsym; | 
|  | struct symtoken ssym; | 
|  | int voidval; | 
|  | const struct block *bval; | 
|  | enum exp_opcode opcode; | 
|  |  | 
|  | struct stoken_vector svec; | 
|  | std::vector<struct type *> *tvec; | 
|  |  | 
|  | struct type_stack *type_stack; | 
|  |  | 
|  | struct objc_class_str theclass; | 
|  | } | 
|  |  | 
|  | %{ | 
|  | /* YYSTYPE gets defined by %union */ | 
|  | static int parse_number (struct parser_state *par_state, | 
|  | const char *, int, int, YYSTYPE *); | 
|  | static struct stoken operator_stoken (const char *); | 
|  | static struct stoken typename_stoken (const char *); | 
|  | static void check_parameter_typelist (std::vector<struct type *> *); | 
|  |  | 
|  | #if defined(YYBISON) && YYBISON < 30800 | 
|  | static void c_print_token (FILE *file, int type, YYSTYPE value); | 
|  | #define YYPRINT(FILE, TYPE, VALUE) c_print_token (FILE, TYPE, VALUE) | 
|  | #endif | 
|  | %} | 
|  |  | 
|  | %type <voidval> exp exp1 type_exp start variable qualified_name lcurly function_method | 
|  | %type <lval> rcurly | 
|  | %type <tval> type typebase scalar_type | 
|  | %type <tvec> nonempty_typelist func_mod parameter_typelist | 
|  | /* %type <bval> block */ | 
|  |  | 
|  | /* Fancy type parsing.  */ | 
|  | %type <tval> ptype | 
|  | %type <lval> array_mod | 
|  | %type <tval> conversion_type_id | 
|  |  | 
|  | %type <type_stack> ptr_operator_ts abs_decl direct_abs_decl | 
|  |  | 
|  | %token <typed_val_int> INT COMPLEX_INT | 
|  | %token <typed_val_float> FLOAT COMPLEX_FLOAT | 
|  |  | 
|  | /* Both NAME and TYPENAME tokens represent symbols in the input, | 
|  | and both convey their data as strings. | 
|  | But a TYPENAME is a string that happens to be defined as a typedef | 
|  | or builtin type name (such as int or char) | 
|  | and a NAME is any other symbol. | 
|  | Contexts where this distinction is not important can use the | 
|  | nonterminal "name", which matches either NAME or TYPENAME.  */ | 
|  |  | 
|  | %token <tsval> STRING | 
|  | %token <sval> NSSTRING		/* ObjC Foundation "NSString" literal */ | 
|  | %token SELECTOR			/* ObjC "@selector" pseudo-operator   */ | 
|  | %token <tsval> CHAR | 
|  | %token <ssym> NAME /* BLOCKNAME defined below to give it higher precedence. */ | 
|  | %token <ssym> UNKNOWN_CPP_NAME | 
|  | %token <voidval> COMPLETE | 
|  | %token <tsym> TYPENAME | 
|  | %token <theclass> CLASSNAME	/* ObjC Class name */ | 
|  | %type <sval> name field_name | 
|  | %type <svec> string_exp | 
|  | %type <ssym> name_not_typename | 
|  | %type <tsym> type_name | 
|  |  | 
|  | /* This is like a '[' token, but is only generated when parsing | 
|  | Objective C.  This lets us reuse the same parser without | 
|  | erroneously parsing ObjC-specific expressions in C.  */ | 
|  | %token OBJC_LBRAC | 
|  |  | 
|  | /* A NAME_OR_INT is a symbol which is not known in the symbol table, | 
|  | but which would parse as a valid number in the current input radix. | 
|  | E.g. "c" when input_radix==16.  Depending on the parse, it will be | 
|  | turned into a name or into a number.  */ | 
|  |  | 
|  | %token <ssym> NAME_OR_INT | 
|  |  | 
|  | %token OPERATOR | 
|  | %token STRUCT CLASS UNION ENUM SIZEOF ALIGNOF UNSIGNED COLONCOLON | 
|  | %token TEMPLATE | 
|  | %token ERROR | 
|  | %token NEW DELETE | 
|  | %type <sval> oper | 
|  | %token REINTERPRET_CAST DYNAMIC_CAST STATIC_CAST CONST_CAST | 
|  | %token ENTRY | 
|  | %token TYPEOF | 
|  | %token DECLTYPE | 
|  | %token TYPEID | 
|  |  | 
|  | /* 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 | 
|  | %token RESTRICT ATOMIC | 
|  | %token FLOAT_KEYWORD COMPLEX | 
|  |  | 
|  | %token <sval> DOLLAR_VARIABLE | 
|  |  | 
|  | %token <opcode> ASSIGN_MODIFY | 
|  |  | 
|  | /* C++ */ | 
|  | %token TRUEKEYWORD | 
|  | %token FALSEKEYWORD | 
|  |  | 
|  |  | 
|  | %left ',' | 
|  | %left ABOVE_COMMA | 
|  | %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 | 
|  | %right ARROW ARROW_STAR '.' DOT_STAR '[' OBJC_LBRAC '(' | 
|  | %token <ssym> BLOCKNAME | 
|  | %token <bval> FILENAME | 
|  | %type <bval> block | 
|  | %left COLONCOLON | 
|  |  | 
|  | %token DOTDOTDOT | 
|  |  | 
|  |  | 
|  | %% | 
|  |  | 
|  | start   :	exp1 | 
|  | |	type_exp | 
|  | ; | 
|  |  | 
|  | type_exp:	type | 
|  | { | 
|  | pstate->push_new<type_operation> ($1); | 
|  | } | 
|  | |	TYPEOF '(' exp ')' | 
|  | { | 
|  | pstate->wrap<typeof_operation> (); | 
|  | } | 
|  | |	TYPEOF '(' type ')' | 
|  | { | 
|  | pstate->push_new<type_operation> ($3); | 
|  | } | 
|  | |	DECLTYPE '(' exp ')' | 
|  | { | 
|  | pstate->wrap<decltype_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* Expressions, including the comma operator.  */ | 
|  | exp1	:	exp | 
|  | |	exp1 ',' exp | 
|  | { pstate->wrap2<comma_operation> (); } | 
|  | ; | 
|  |  | 
|  | /* Expressions, not including the comma operator.  */ | 
|  | exp	:	'*' exp    %prec UNARY | 
|  | { pstate->wrap<unop_ind_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	'&' exp    %prec UNARY | 
|  | { pstate->wrap<unop_addr_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	'-' exp    %prec UNARY | 
|  | { pstate->wrap<unary_neg_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	'+' exp    %prec UNARY | 
|  | { pstate->wrap<unary_plus_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	'!' exp    %prec UNARY | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap<opencl_not_operation> (); | 
|  | else | 
|  | pstate->wrap<unary_logical_not_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	'~' exp    %prec UNARY | 
|  | { pstate->wrap<unary_complement_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	INCREMENT exp    %prec UNARY | 
|  | { pstate->wrap<preinc_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	DECREMENT exp    %prec UNARY | 
|  | { pstate->wrap<predec_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp INCREMENT    %prec UNARY | 
|  | { pstate->wrap<postinc_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp DECREMENT    %prec UNARY | 
|  | { pstate->wrap<postdec_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	TYPEID '(' exp ')' %prec UNARY | 
|  | { pstate->wrap<typeid_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	TYPEID '(' type_exp ')' %prec UNARY | 
|  | { pstate->wrap<typeid_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	SIZEOF exp       %prec UNARY | 
|  | { pstate->wrap<unop_sizeof_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	ALIGNOF '(' type_exp ')'	%prec UNARY | 
|  | { pstate->wrap<unop_alignof_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW field_name | 
|  | { | 
|  | pstate->push_new<structop_ptr_operation> | 
|  | (pstate->pop (), copy_name ($3)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW field_name COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_ptr_operation (pstate->pop (), | 
|  | copy_name ($3)); | 
|  | pstate->mark_struct_expression (op); | 
|  | pstate->push (operation_up (op)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_ptr_operation (pstate->pop (), ""); | 
|  | pstate->mark_struct_expression (op); | 
|  | pstate->push (operation_up (op)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW '~' name | 
|  | { | 
|  | pstate->push_new<structop_ptr_operation> | 
|  | (pstate->pop (), "~" + copy_name ($4)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW '~' name COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_ptr_operation (pstate->pop (), | 
|  | "~" + copy_name ($4)); | 
|  | pstate->mark_struct_expression (op); | 
|  | pstate->push (operation_up (op)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW qualified_name | 
|  | { /* exp->type::name becomes exp->*(&type::name) */ | 
|  | /* Note: this doesn't work if name is a | 
|  | static member!  FIXME */ | 
|  | pstate->wrap<unop_addr_operation> (); | 
|  | pstate->wrap2<structop_mptr_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ARROW_STAR exp | 
|  | { pstate->wrap2<structop_mptr_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' field_name | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->push_new<opencl_structop_operation> | 
|  | (pstate->pop (), copy_name ($3)); | 
|  | else | 
|  | pstate->push_new<structop_operation> | 
|  | (pstate->pop (), copy_name ($3)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' field_name COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_operation (pstate->pop (), | 
|  | copy_name ($3)); | 
|  | pstate->mark_struct_expression (op); | 
|  | pstate->push (operation_up (op)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_operation (pstate->pop (), ""); | 
|  | pstate->mark_struct_expression (op); | 
|  | pstate->push (operation_up (op)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' '~' name | 
|  | { | 
|  | pstate->push_new<structop_operation> | 
|  | (pstate->pop (), "~" + copy_name ($4)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' '~' name COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_operation (pstate->pop (), | 
|  | "~" + copy_name ($4)); | 
|  | pstate->mark_struct_expression (op); | 
|  | pstate->push (operation_up (op)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' qualified_name | 
|  | { /* exp.type::name becomes exp.*(&type::name) */ | 
|  | /* Note: this doesn't work if name is a | 
|  | static member!  FIXME */ | 
|  | pstate->wrap<unop_addr_operation> (); | 
|  | pstate->wrap2<structop_member_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp DOT_STAR exp | 
|  | { pstate->wrap2<structop_member_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '[' exp1 ']' | 
|  | { pstate->wrap2<subscript_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp OBJC_LBRAC exp1 ']' | 
|  | { pstate->wrap2<subscript_operation> (); } | 
|  | ; | 
|  |  | 
|  | /* | 
|  | * The rules below parse ObjC message calls of the form: | 
|  | *	'[' target selector {':' argument}* ']' | 
|  | */ | 
|  |  | 
|  | exp	: 	OBJC_LBRAC TYPENAME | 
|  | { | 
|  | CORE_ADDR theclass; | 
|  |  | 
|  | std::string copy = copy_name ($2.stoken); | 
|  | theclass = lookup_objc_class (pstate->gdbarch (), | 
|  | copy.c_str ()); | 
|  | if (theclass == 0) | 
|  | error (_("%s is not an ObjC Class"), | 
|  | copy.c_str ()); | 
|  | pstate->push_new<long_const_operation> | 
|  | (parse_type (pstate)->builtin_int, | 
|  | (LONGEST) theclass); | 
|  | start_msglist(); | 
|  | } | 
|  | msglist ']' | 
|  | { end_msglist (pstate); } | 
|  | ; | 
|  |  | 
|  | exp	:	OBJC_LBRAC CLASSNAME | 
|  | { | 
|  | pstate->push_new<long_const_operation> | 
|  | (parse_type (pstate)->builtin_int, | 
|  | (LONGEST) $2.theclass); | 
|  | start_msglist(); | 
|  | } | 
|  | msglist ']' | 
|  | { end_msglist (pstate); } | 
|  | ; | 
|  |  | 
|  | exp	:	OBJC_LBRAC exp | 
|  | { start_msglist(); } | 
|  | msglist ']' | 
|  | { end_msglist (pstate); } | 
|  | ; | 
|  |  | 
|  | msglist :	name | 
|  | { add_msglist(&$1, 0); } | 
|  | |	msgarglist | 
|  | ; | 
|  |  | 
|  | msgarglist :	msgarg | 
|  | |	msgarglist msgarg | 
|  | ; | 
|  |  | 
|  | msgarg	:	name ':' exp | 
|  | { add_msglist(&$1, 1); } | 
|  | |	':' exp	/* Unnamed arg.  */ | 
|  | { add_msglist(0, 1);   } | 
|  | |	',' exp	/* Variable number of args.  */ | 
|  | { add_msglist(0, 0);   } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '(' | 
|  | /* This is to save the value of arglist_len | 
|  | being accumulated by an outer function call.  */ | 
|  | { pstate->start_arglist (); } | 
|  | arglist ')'	%prec ARROW | 
|  | { | 
|  | std::vector<operation_up> args | 
|  | = pstate->pop_vector (pstate->end_arglist ()); | 
|  | pstate->push_new<funcall_operation> | 
|  | (pstate->pop (), std::move (args)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* This is here to disambiguate with the production for | 
|  | "func()::static_var" further below, which uses | 
|  | function_method_void.  */ | 
|  | exp	:	exp '(' ')' %prec ARROW | 
|  | { | 
|  | pstate->push_new<funcall_operation> | 
|  | (pstate->pop (), std::vector<operation_up> ()); | 
|  | } | 
|  | ; | 
|  |  | 
|  |  | 
|  | exp	:	UNKNOWN_CPP_NAME '(' | 
|  | { | 
|  | /* This could potentially be a an argument defined | 
|  | lookup function (Koenig).  */ | 
|  | /* This is to save the value of arglist_len | 
|  | being accumulated by an outer function call.  */ | 
|  | pstate->start_arglist (); | 
|  | } | 
|  | arglist ')'	%prec ARROW | 
|  | { | 
|  | std::vector<operation_up> args | 
|  | = pstate->pop_vector (pstate->end_arglist ()); | 
|  | pstate->push_new<adl_func_operation> | 
|  | (copy_name ($1.stoken), | 
|  | pstate->expression_context_block, | 
|  | std::move (args)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | lcurly	:	'{' | 
|  | { pstate->start_arglist (); } | 
|  | ; | 
|  |  | 
|  | arglist	: | 
|  | ; | 
|  |  | 
|  | arglist	:	exp | 
|  | { pstate->arglist_len = 1; } | 
|  | ; | 
|  |  | 
|  | arglist	:	arglist ',' exp   %prec ABOVE_COMMA | 
|  | { pstate->arglist_len++; } | 
|  | ; | 
|  |  | 
|  | function_method:       exp '(' parameter_typelist ')' const_or_volatile | 
|  | { | 
|  | std::vector<struct type *> *type_list = $3; | 
|  | /* Save the const/volatile qualifiers as | 
|  | recorded by the const_or_volatile | 
|  | production's actions.  */ | 
|  | type_instance_flags flags | 
|  | = (cpstate->type_stack | 
|  | .follow_type_instance_flags ()); | 
|  | pstate->push_new<type_instance_operation> | 
|  | (flags, std::move (*type_list), | 
|  | pstate->pop ()); | 
|  | } | 
|  | ; | 
|  |  | 
|  | function_method_void:	    exp '(' ')' const_or_volatile | 
|  | { | 
|  | type_instance_flags flags | 
|  | = (cpstate->type_stack | 
|  | .follow_type_instance_flags ()); | 
|  | pstate->push_new<type_instance_operation> | 
|  | (flags, std::vector<type *> (), pstate->pop ()); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp     :       function_method | 
|  | ; | 
|  |  | 
|  | /* Normally we must interpret "func()" as a function call, instead of | 
|  | a type.  The user needs to write func(void) to disambiguate. | 
|  | However, in the "func()::static_var" case, there's no | 
|  | ambiguity.  */ | 
|  | function_method_void_or_typelist: function_method | 
|  | |               function_method_void | 
|  | ; | 
|  |  | 
|  | exp     :       function_method_void_or_typelist COLONCOLON name | 
|  | { | 
|  | pstate->push_new<func_static_var_operation> | 
|  | (pstate->pop (), copy_name ($3)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | rcurly	:	'}' | 
|  | { $$ = pstate->end_arglist () - 1; } | 
|  | ; | 
|  | exp	:	lcurly arglist rcurly	%prec ARROW | 
|  | { | 
|  | std::vector<operation_up> args | 
|  | = pstate->pop_vector ($3 + 1); | 
|  | pstate->push_new<array_operation> (0, $3, | 
|  | std::move (args)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	lcurly type_exp rcurly exp  %prec UNARY | 
|  | { pstate->wrap2<unop_memval_type_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	'(' type_exp ')' exp  %prec UNARY | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_cast_type_operation> (); | 
|  | else | 
|  | pstate->wrap2<unop_cast_type_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	'(' exp1 ')' | 
|  | { } | 
|  | ; | 
|  |  | 
|  | /* Binary operators in order of decreasing precedence.  */ | 
|  |  | 
|  | exp	:	exp '@' exp | 
|  | { pstate->wrap2<repeat_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '*' exp | 
|  | { pstate->wrap2<mul_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '/' exp | 
|  | { pstate->wrap2<div_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '%' exp | 
|  | { pstate->wrap2<rem_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '+' exp | 
|  | { pstate->wrap2<add_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '-' exp | 
|  | { pstate->wrap2<sub_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp LSH exp | 
|  | { pstate->wrap2<lsh_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp RSH exp | 
|  | { pstate->wrap2<rsh_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp EQUAL exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_equal_operation> (); | 
|  | else | 
|  | pstate->wrap2<equal_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp NOTEQUAL exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_notequal_operation> (); | 
|  | else | 
|  | pstate->wrap2<notequal_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp LEQ exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_leq_operation> (); | 
|  | else | 
|  | pstate->wrap2<leq_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp GEQ exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_geq_operation> (); | 
|  | else | 
|  | pstate->wrap2<geq_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '<' exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_less_operation> (); | 
|  | else | 
|  | pstate->wrap2<less_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '>' exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_gtr_operation> (); | 
|  | else | 
|  | pstate->wrap2<gtr_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '&' exp | 
|  | { pstate->wrap2<bitwise_and_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '^' exp | 
|  | { pstate->wrap2<bitwise_xor_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '|' exp | 
|  | { pstate->wrap2<bitwise_ior_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ANDAND exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | { | 
|  | operation_up rhs = pstate->pop (); | 
|  | operation_up lhs = pstate->pop (); | 
|  | pstate->push_new<opencl_logical_binop_operation> | 
|  | (BINOP_LOGICAL_AND, std::move (lhs), | 
|  | std::move (rhs)); | 
|  | } | 
|  | else | 
|  | pstate->wrap2<logical_and_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp OROR exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | { | 
|  | operation_up rhs = pstate->pop (); | 
|  | operation_up lhs = pstate->pop (); | 
|  | pstate->push_new<opencl_logical_binop_operation> | 
|  | (BINOP_LOGICAL_OR, std::move (lhs), | 
|  | std::move (rhs)); | 
|  | } | 
|  | else | 
|  | pstate->wrap2<logical_or_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '?' exp ':' exp	%prec '?' | 
|  | { | 
|  | operation_up last = pstate->pop (); | 
|  | operation_up mid = pstate->pop (); | 
|  | operation_up first = pstate->pop (); | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->push_new<opencl_ternop_cond_operation> | 
|  | (std::move (first), std::move (mid), | 
|  | std::move (last)); | 
|  | else | 
|  | pstate->push_new<ternop_cond_operation> | 
|  | (std::move (first), std::move (mid), | 
|  | std::move (last)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '=' exp | 
|  | { | 
|  | if (pstate->language ()->la_language | 
|  | == language_opencl) | 
|  | pstate->wrap2<opencl_assign_operation> (); | 
|  | else | 
|  | pstate->wrap2<assign_operation> (); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp ASSIGN_MODIFY exp | 
|  | { | 
|  | operation_up rhs = pstate->pop (); | 
|  | operation_up lhs = pstate->pop (); | 
|  | pstate->push_new<assign_modify_operation> | 
|  | ($2, std::move (lhs), std::move (rhs)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	INT | 
|  | { | 
|  | pstate->push_new<long_const_operation> | 
|  | ($1.type, $1.val); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	COMPLEX_INT | 
|  | { | 
|  | operation_up real | 
|  | = (make_operation<long_const_operation> | 
|  | (TYPE_TARGET_TYPE ($1.type), 0)); | 
|  | operation_up imag | 
|  | = (make_operation<long_const_operation> | 
|  | (TYPE_TARGET_TYPE ($1.type), $1.val)); | 
|  | pstate->push_new<complex_operation> | 
|  | (std::move (real), std::move (imag), $1.type); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	CHAR | 
|  | { | 
|  | struct stoken_vector vec; | 
|  | vec.len = 1; | 
|  | vec.tokens = &$1; | 
|  | pstate->push_c_string ($1.type, &vec); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	NAME_OR_INT | 
|  | { YYSTYPE val; | 
|  | parse_number (pstate, $1.stoken.ptr, | 
|  | $1.stoken.length, 0, &val); | 
|  | pstate->push_new<long_const_operation> | 
|  | (val.typed_val_int.type, | 
|  | val.typed_val_int.val); | 
|  | } | 
|  | ; | 
|  |  | 
|  |  | 
|  | exp	:	FLOAT | 
|  | { | 
|  | float_data data; | 
|  | std::copy (std::begin ($1.val), std::end ($1.val), | 
|  | std::begin (data)); | 
|  | pstate->push_new<float_const_operation> ($1.type, data); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	COMPLEX_FLOAT | 
|  | { | 
|  | struct type *underlying | 
|  | = TYPE_TARGET_TYPE ($1.type); | 
|  |  | 
|  | float_data val; | 
|  | target_float_from_host_double (val.data (), | 
|  | underlying, 0); | 
|  | operation_up real | 
|  | = (make_operation<float_const_operation> | 
|  | (underlying, val)); | 
|  |  | 
|  | std::copy (std::begin ($1.val), std::end ($1.val), | 
|  | std::begin (val)); | 
|  | operation_up imag | 
|  | = (make_operation<float_const_operation> | 
|  | (underlying, val)); | 
|  |  | 
|  | pstate->push_new<complex_operation> | 
|  | (std::move (real), std::move (imag), | 
|  | $1.type); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	variable | 
|  | ; | 
|  |  | 
|  | exp	:	DOLLAR_VARIABLE | 
|  | { | 
|  | pstate->push_dollar ($1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	SELECTOR '(' name ')' | 
|  | { | 
|  | pstate->push_new<objc_selector_operation> | 
|  | (copy_name ($3)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	SIZEOF '(' type ')'	%prec UNARY | 
|  | { struct type *type = $3; | 
|  | struct type *int_type | 
|  | = lookup_signed_typename (pstate->language (), | 
|  | "int"); | 
|  | type = check_typedef (type); | 
|  |  | 
|  | /* $5.3.3/2 of the C++ Standard (n3290 draft) | 
|  | says of sizeof:  "When applied to a reference | 
|  | or a reference type, the result is the size of | 
|  | the referenced type."  */ | 
|  | if (TYPE_IS_REFERENCE (type)) | 
|  | type = check_typedef (TYPE_TARGET_TYPE (type)); | 
|  | pstate->push_new<long_const_operation> | 
|  | (int_type, TYPE_LENGTH (type)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	REINTERPRET_CAST '<' type_exp '>' '(' exp ')' %prec UNARY | 
|  | { pstate->wrap2<reinterpret_cast_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	STATIC_CAST '<' type_exp '>' '(' exp ')' %prec UNARY | 
|  | { pstate->wrap2<unop_cast_type_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	DYNAMIC_CAST '<' type_exp '>' '(' exp ')' %prec UNARY | 
|  | { pstate->wrap2<dynamic_cast_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	CONST_CAST '<' type_exp '>' '(' exp ')' %prec UNARY | 
|  | { /* We could do more error checking here, but | 
|  | it doesn't seem worthwhile.  */ | 
|  | pstate->wrap2<unop_cast_type_operation> (); } | 
|  | ; | 
|  |  | 
|  | string_exp: | 
|  | STRING | 
|  | { | 
|  | /* We copy the string here, and not in the | 
|  | lexer, to guarantee that we do not leak a | 
|  | string.  Note that we follow the | 
|  | NUL-termination convention of the | 
|  | lexer.  */ | 
|  | struct typed_stoken *vec = XNEW (struct typed_stoken); | 
|  | $$.len = 1; | 
|  | $$.tokens = vec; | 
|  |  | 
|  | vec->type = $1.type; | 
|  | vec->length = $1.length; | 
|  | vec->ptr = (char *) malloc ($1.length + 1); | 
|  | memcpy (vec->ptr, $1.ptr, $1.length + 1); | 
|  | } | 
|  |  | 
|  | |	string_exp STRING | 
|  | { | 
|  | /* Note that we NUL-terminate here, but just | 
|  | for convenience.  */ | 
|  | char *p; | 
|  | ++$$.len; | 
|  | $$.tokens = XRESIZEVEC (struct typed_stoken, | 
|  | $$.tokens, $$.len); | 
|  |  | 
|  | p = (char *) malloc ($2.length + 1); | 
|  | memcpy (p, $2.ptr, $2.length + 1); | 
|  |  | 
|  | $$.tokens[$$.len - 1].type = $2.type; | 
|  | $$.tokens[$$.len - 1].length = $2.length; | 
|  | $$.tokens[$$.len - 1].ptr = p; | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	string_exp | 
|  | { | 
|  | int i; | 
|  | c_string_type type = C_STRING; | 
|  |  | 
|  | for (i = 0; i < $1.len; ++i) | 
|  | { | 
|  | switch ($1.tokens[i].type) | 
|  | { | 
|  | case C_STRING: | 
|  | break; | 
|  | case C_WIDE_STRING: | 
|  | case C_STRING_16: | 
|  | case C_STRING_32: | 
|  | if (type != C_STRING | 
|  | && type != $1.tokens[i].type) | 
|  | error (_("Undefined string concatenation.")); | 
|  | type = (enum c_string_type_values) $1.tokens[i].type; | 
|  | break; | 
|  | default: | 
|  | /* internal error */ | 
|  | internal_error (__FILE__, __LINE__, | 
|  | "unrecognized type in string concatenation"); | 
|  | } | 
|  | } | 
|  |  | 
|  | pstate->push_c_string (type, &$1); | 
|  | for (i = 0; i < $1.len; ++i) | 
|  | free ($1.tokens[i].ptr); | 
|  | free ($1.tokens); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp     :	NSSTRING	/* ObjC NextStep NSString constant | 
|  | * of the form '@' '"' string '"'. | 
|  | */ | 
|  | { | 
|  | pstate->push_new<objc_nsstring_operation> | 
|  | (copy_name ($1)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* C++.  */ | 
|  | exp     :       TRUEKEYWORD | 
|  | { pstate->push_new<long_const_operation> | 
|  | (parse_type (pstate)->builtin_bool, 1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp     :       FALSEKEYWORD | 
|  | { pstate->push_new<long_const_operation> | 
|  | (parse_type (pstate)->builtin_bool, 0); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* end of C++.  */ | 
|  |  | 
|  | block	:	BLOCKNAME | 
|  | { | 
|  | if ($1.sym.symbol) | 
|  | $$ = $1.sym.symbol->value_block (); | 
|  | else | 
|  | error (_("No file or function \"%s\"."), | 
|  | copy_name ($1.stoken).c_str ()); | 
|  | } | 
|  | |	FILENAME | 
|  | { | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | block	:	block COLONCOLON name | 
|  | { | 
|  | std::string copy = copy_name ($3); | 
|  | struct symbol *tem | 
|  | = lookup_symbol (copy.c_str (), $1, | 
|  | VAR_DOMAIN, NULL).symbol; | 
|  |  | 
|  | if (!tem || tem->aclass () != LOC_BLOCK) | 
|  | error (_("No function \"%s\" in specified context."), | 
|  | copy.c_str ()); | 
|  | $$ = tem->value_block (); } | 
|  | ; | 
|  |  | 
|  | variable:	name_not_typename ENTRY | 
|  | { struct symbol *sym = $1.sym.symbol; | 
|  |  | 
|  | if (sym == NULL || !sym->is_argument () | 
|  | || !symbol_read_needs_frame (sym)) | 
|  | error (_("@entry can be used only for function " | 
|  | "parameters, not for \"%s\""), | 
|  | copy_name ($1.stoken).c_str ()); | 
|  |  | 
|  | pstate->push_new<var_entry_value_operation> (sym); | 
|  | } | 
|  | ; | 
|  |  | 
|  | variable:	block COLONCOLON name | 
|  | { | 
|  | std::string copy = copy_name ($3); | 
|  | struct block_symbol sym | 
|  | = lookup_symbol (copy.c_str (), $1, | 
|  | VAR_DOMAIN, NULL); | 
|  |  | 
|  | if (sym.symbol == 0) | 
|  | error (_("No symbol \"%s\" in specified context."), | 
|  | copy.c_str ()); | 
|  | if (symbol_read_needs_frame (sym.symbol)) | 
|  | pstate->block_tracker->update (sym); | 
|  |  | 
|  | pstate->push_new<var_value_operation> (sym); | 
|  | } | 
|  | ; | 
|  |  | 
|  | qualified_name:	TYPENAME COLONCOLON name | 
|  | { | 
|  | struct type *type = $1.type; | 
|  | type = check_typedef (type); | 
|  | if (!type_aggregate_p (type)) | 
|  | error (_("`%s' is not defined as an aggregate type."), | 
|  | TYPE_SAFE_NAME (type)); | 
|  |  | 
|  | pstate->push_new<scope_operation> (type, | 
|  | copy_name ($3)); | 
|  | } | 
|  | |	TYPENAME COLONCOLON '~' name | 
|  | { | 
|  | struct type *type = $1.type; | 
|  |  | 
|  | type = check_typedef (type); | 
|  | if (!type_aggregate_p (type)) | 
|  | error (_("`%s' is not defined as an aggregate type."), | 
|  | TYPE_SAFE_NAME (type)); | 
|  | std::string name = "~" + std::string ($4.ptr, | 
|  | $4.length); | 
|  |  | 
|  | /* Check for valid destructor name.  */ | 
|  | destructor_name_p (name.c_str (), $1.type); | 
|  | pstate->push_new<scope_operation> (type, | 
|  | std::move (name)); | 
|  | } | 
|  | |	TYPENAME COLONCOLON name COLONCOLON name | 
|  | { | 
|  | std::string copy = copy_name ($3); | 
|  | error (_("No type \"%s\" within class " | 
|  | "or namespace \"%s\"."), | 
|  | copy.c_str (), TYPE_SAFE_NAME ($1.type)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | variable:	qualified_name | 
|  | |	COLONCOLON name_not_typename | 
|  | { | 
|  | std::string name = copy_name ($2.stoken); | 
|  | struct block_symbol sym | 
|  | = lookup_symbol (name.c_str (), | 
|  | (const struct block *) NULL, | 
|  | VAR_DOMAIN, NULL); | 
|  | pstate->push_symbol (name.c_str (), sym); | 
|  | } | 
|  | ; | 
|  |  | 
|  | variable:	name_not_typename | 
|  | { struct block_symbol sym = $1.sym; | 
|  |  | 
|  | if (sym.symbol) | 
|  | { | 
|  | if (symbol_read_needs_frame (sym.symbol)) | 
|  | pstate->block_tracker->update (sym); | 
|  |  | 
|  | /* If we found a function, see if it's | 
|  | an ifunc resolver that has the same | 
|  | address as the ifunc symbol itself. | 
|  | If so, prefer the ifunc symbol.  */ | 
|  |  | 
|  | bound_minimal_symbol resolver | 
|  | = find_gnu_ifunc (sym.symbol); | 
|  | if (resolver.minsym != NULL) | 
|  | pstate->push_new<var_msym_value_operation> | 
|  | (resolver); | 
|  | else | 
|  | pstate->push_new<var_value_operation> (sym); | 
|  | } | 
|  | else if ($1.is_a_field_of_this) | 
|  | { | 
|  | /* C++: it hangs off of `this'.  Must | 
|  | not inadvertently convert from a method call | 
|  | to data ref.  */ | 
|  | pstate->block_tracker->update (sym); | 
|  | operation_up thisop | 
|  | = make_operation<op_this_operation> (); | 
|  | pstate->push_new<structop_ptr_operation> | 
|  | (std::move (thisop), copy_name ($1.stoken)); | 
|  | } | 
|  | else | 
|  | { | 
|  | std::string arg = copy_name ($1.stoken); | 
|  |  | 
|  | bound_minimal_symbol msymbol | 
|  | = lookup_bound_minimal_symbol (arg.c_str ()); | 
|  | if (msymbol.minsym == NULL) | 
|  | { | 
|  | if (!have_full_symbols () && !have_partial_symbols ()) | 
|  | error (_("No symbol table is loaded.  Use the \"file\" command.")); | 
|  | else | 
|  | error (_("No symbol \"%s\" in current context."), | 
|  | arg.c_str ()); | 
|  | } | 
|  |  | 
|  | /* This minsym might be an alias for | 
|  | another function.  See if we can find | 
|  | the debug symbol for the target, and | 
|  | if so, use it instead, since it has | 
|  | return type / prototype info.  This | 
|  | is important for example for "p | 
|  | *__errno_location()".  */ | 
|  | symbol *alias_target | 
|  | = ((msymbol.minsym->type () != mst_text_gnu_ifunc | 
|  | && msymbol.minsym->type () != mst_data_gnu_ifunc) | 
|  | ? find_function_alias_target (msymbol) | 
|  | : NULL); | 
|  | if (alias_target != NULL) | 
|  | { | 
|  | block_symbol bsym { alias_target, | 
|  | alias_target->value_block () }; | 
|  | pstate->push_new<var_value_operation> (bsym); | 
|  | } | 
|  | else | 
|  | pstate->push_new<var_msym_value_operation> | 
|  | (msymbol); | 
|  | } | 
|  | } | 
|  | ; | 
|  |  | 
|  | const_or_volatile: const_or_volatile_noopt | 
|  | | | 
|  | ; | 
|  |  | 
|  | single_qualifier: | 
|  | CONST_KEYWORD | 
|  | { cpstate->type_stack.insert (tp_const); } | 
|  | | 	VOLATILE_KEYWORD | 
|  | { cpstate->type_stack.insert (tp_volatile); } | 
|  | | 	ATOMIC | 
|  | { cpstate->type_stack.insert (tp_atomic); } | 
|  | | 	RESTRICT | 
|  | { cpstate->type_stack.insert (tp_restrict); } | 
|  | |	'@' NAME | 
|  | { | 
|  | cpstate->type_stack.insert (pstate, | 
|  | copy_name ($2.stoken).c_str ()); | 
|  | } | 
|  | |	'@' UNKNOWN_CPP_NAME | 
|  | { | 
|  | cpstate->type_stack.insert (pstate, | 
|  | copy_name ($2.stoken).c_str ()); | 
|  | } | 
|  | ; | 
|  |  | 
|  | qualifier_seq_noopt: | 
|  | single_qualifier | 
|  | | 	qualifier_seq_noopt single_qualifier | 
|  | ; | 
|  |  | 
|  | qualifier_seq: | 
|  | qualifier_seq_noopt | 
|  | | | 
|  | ; | 
|  |  | 
|  | ptr_operator: | 
|  | ptr_operator '*' | 
|  | { cpstate->type_stack.insert (tp_pointer); } | 
|  | qualifier_seq | 
|  | |	'*' | 
|  | { cpstate->type_stack.insert (tp_pointer); } | 
|  | qualifier_seq | 
|  | |	'&' | 
|  | { cpstate->type_stack.insert (tp_reference); } | 
|  | |	'&' ptr_operator | 
|  | { cpstate->type_stack.insert (tp_reference); } | 
|  | |       ANDAND | 
|  | { cpstate->type_stack.insert (tp_rvalue_reference); } | 
|  | |       ANDAND ptr_operator | 
|  | { cpstate->type_stack.insert (tp_rvalue_reference); } | 
|  | ; | 
|  |  | 
|  | ptr_operator_ts: ptr_operator | 
|  | { | 
|  | $$ = cpstate->type_stack.create (); | 
|  | cpstate->type_stacks.emplace_back ($$); | 
|  | } | 
|  | ; | 
|  |  | 
|  | abs_decl:	ptr_operator_ts direct_abs_decl | 
|  | { $$ = $2->append ($1); } | 
|  | |	ptr_operator_ts | 
|  | |	direct_abs_decl | 
|  | ; | 
|  |  | 
|  | direct_abs_decl: '(' abs_decl ')' | 
|  | { $$ = $2; } | 
|  | |	direct_abs_decl array_mod | 
|  | { | 
|  | cpstate->type_stack.push ($1); | 
|  | cpstate->type_stack.push ($2); | 
|  | cpstate->type_stack.push (tp_array); | 
|  | $$ = cpstate->type_stack.create (); | 
|  | cpstate->type_stacks.emplace_back ($$); | 
|  | } | 
|  | |	array_mod | 
|  | { | 
|  | cpstate->type_stack.push ($1); | 
|  | cpstate->type_stack.push (tp_array); | 
|  | $$ = cpstate->type_stack.create (); | 
|  | cpstate->type_stacks.emplace_back ($$); | 
|  | } | 
|  |  | 
|  | | 	direct_abs_decl func_mod | 
|  | { | 
|  | cpstate->type_stack.push ($1); | 
|  | cpstate->type_stack.push ($2); | 
|  | $$ = cpstate->type_stack.create (); | 
|  | cpstate->type_stacks.emplace_back ($$); | 
|  | } | 
|  | |	func_mod | 
|  | { | 
|  | cpstate->type_stack.push ($1); | 
|  | $$ = cpstate->type_stack.create (); | 
|  | cpstate->type_stacks.emplace_back ($$); | 
|  | } | 
|  | ; | 
|  |  | 
|  | array_mod:	'[' ']' | 
|  | { $$ = -1; } | 
|  | |	OBJC_LBRAC ']' | 
|  | { $$ = -1; } | 
|  | |	'[' INT ']' | 
|  | { $$ = $2.val; } | 
|  | |	OBJC_LBRAC INT ']' | 
|  | { $$ = $2.val; } | 
|  | ; | 
|  |  | 
|  | func_mod:	'(' ')' | 
|  | { | 
|  | $$ = new std::vector<struct type *>; | 
|  | cpstate->type_lists.emplace_back ($$); | 
|  | } | 
|  | |	'(' parameter_typelist ')' | 
|  | { $$ = $2; } | 
|  | ; | 
|  |  | 
|  | /* We used to try to recognize pointer to member types here, but | 
|  | that didn't work (shift/reduce conflicts meant that these rules never | 
|  | got executed).  The problem is that | 
|  | int (foo::bar::baz::bizzle) | 
|  | is a function type but | 
|  | int (foo::bar::baz::bizzle::*) | 
|  | is a pointer to member type.  Stroustrup loses again!  */ | 
|  |  | 
|  | type	:	ptype | 
|  | ; | 
|  |  | 
|  | /* A helper production that recognizes scalar types that can validly | 
|  | be used with _Complex.  */ | 
|  |  | 
|  | scalar_type: | 
|  | INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "int"); } | 
|  | |	LONG | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	SHORT | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	LONG INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	LONG SIGNED_KEYWORD INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	LONG SIGNED_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	SIGNED_KEYWORD LONG INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	UNSIGNED LONG INT_KEYWORD | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	LONG UNSIGNED INT_KEYWORD | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	LONG UNSIGNED | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long"); } | 
|  | |	LONG LONG | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	LONG LONG INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	LONG LONG SIGNED_KEYWORD INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	LONG LONG SIGNED_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	SIGNED_KEYWORD LONG LONG | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	SIGNED_KEYWORD LONG LONG INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	UNSIGNED LONG LONG | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	UNSIGNED LONG LONG INT_KEYWORD | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	LONG LONG UNSIGNED | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	LONG LONG UNSIGNED INT_KEYWORD | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "long long"); } | 
|  | |	SHORT INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	SHORT SIGNED_KEYWORD INT_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	SHORT SIGNED_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	UNSIGNED SHORT INT_KEYWORD | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	SHORT UNSIGNED | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	SHORT UNSIGNED INT_KEYWORD | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "short"); } | 
|  | |	DOUBLE_KEYWORD | 
|  | { $$ = lookup_typename (pstate->language (), | 
|  | "double", | 
|  | NULL, | 
|  | 0); } | 
|  | |	FLOAT_KEYWORD | 
|  | { $$ = lookup_typename (pstate->language (), | 
|  | "float", | 
|  | NULL, | 
|  | 0); } | 
|  | |	LONG DOUBLE_KEYWORD | 
|  | { $$ = lookup_typename (pstate->language (), | 
|  | "long double", | 
|  | NULL, | 
|  | 0); } | 
|  | |	UNSIGNED type_name | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | $2.type->name ()); } | 
|  | |	UNSIGNED | 
|  | { $$ = lookup_unsigned_typename (pstate->language (), | 
|  | "int"); } | 
|  | |	SIGNED_KEYWORD type_name | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | $2.type->name ()); } | 
|  | |	SIGNED_KEYWORD | 
|  | { $$ = lookup_signed_typename (pstate->language (), | 
|  | "int"); } | 
|  | ; | 
|  |  | 
|  | /* Implements (approximately): (type-qualifier)* type-specifier. | 
|  |  | 
|  | When type-specifier is only ever a single word, like 'float' then these | 
|  | arrive as pre-built TYPENAME tokens thanks to the classify_name | 
|  | function.  However, when a type-specifier can contain multiple words, | 
|  | for example 'double' can appear as just 'double' or 'long double', and | 
|  | similarly 'long' can appear as just 'long' or in 'long double', then | 
|  | these type-specifiers are parsed into their own tokens in the function | 
|  | lex_one_token and the ident_tokens array.  These separate tokens are all | 
|  | recognised here.  */ | 
|  | typebase | 
|  | :	TYPENAME | 
|  | { $$ = $1.type; } | 
|  | |	scalar_type | 
|  | { $$ = $1; } | 
|  | |	COMPLEX scalar_type | 
|  | { | 
|  | $$ = init_complex_type (nullptr, $2); | 
|  | } | 
|  | |	STRUCT name | 
|  | { $$ | 
|  | = lookup_struct (copy_name ($2).c_str (), | 
|  | pstate->expression_context_block); | 
|  | } | 
|  | |	STRUCT COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_STRUCT, | 
|  | "", 0); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	STRUCT name COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_STRUCT, | 
|  | $2.ptr, $2.length); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	CLASS name | 
|  | { $$ = lookup_struct | 
|  | (copy_name ($2).c_str (), | 
|  | pstate->expression_context_block); | 
|  | } | 
|  | |	CLASS COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_STRUCT, | 
|  | "", 0); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	CLASS name COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_STRUCT, | 
|  | $2.ptr, $2.length); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	UNION name | 
|  | { $$ | 
|  | = lookup_union (copy_name ($2).c_str (), | 
|  | pstate->expression_context_block); | 
|  | } | 
|  | |	UNION COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_UNION, | 
|  | "", 0); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	UNION name COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_UNION, | 
|  | $2.ptr, $2.length); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	ENUM name | 
|  | { $$ = lookup_enum (copy_name ($2).c_str (), | 
|  | pstate->expression_context_block); | 
|  | } | 
|  | |	ENUM COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_ENUM, "", 0); | 
|  | $$ = NULL; | 
|  | } | 
|  | |	ENUM name COMPLETE | 
|  | { | 
|  | pstate->mark_completion_tag (TYPE_CODE_ENUM, $2.ptr, | 
|  | $2.length); | 
|  | $$ = NULL; | 
|  | } | 
|  | /* It appears that this rule for templates is never | 
|  | reduced; template recognition happens by lookahead | 
|  | in the token processing code in yylex. */ | 
|  | |	TEMPLATE name '<' type '>' | 
|  | { $$ = lookup_template_type | 
|  | (copy_name($2).c_str (), $4, | 
|  | pstate->expression_context_block); | 
|  | } | 
|  | |	qualifier_seq_noopt typebase | 
|  | { $$ = cpstate->type_stack.follow_types ($2); } | 
|  | |	typebase qualifier_seq_noopt | 
|  | { $$ = cpstate->type_stack.follow_types ($1); } | 
|  | ; | 
|  |  | 
|  | type_name:	TYPENAME | 
|  | |	INT_KEYWORD | 
|  | { | 
|  | $$.stoken.ptr = "int"; | 
|  | $$.stoken.length = 3; | 
|  | $$.type = lookup_signed_typename (pstate->language (), | 
|  | "int"); | 
|  | } | 
|  | |	LONG | 
|  | { | 
|  | $$.stoken.ptr = "long"; | 
|  | $$.stoken.length = 4; | 
|  | $$.type = lookup_signed_typename (pstate->language (), | 
|  | "long"); | 
|  | } | 
|  | |	SHORT | 
|  | { | 
|  | $$.stoken.ptr = "short"; | 
|  | $$.stoken.length = 5; | 
|  | $$.type = lookup_signed_typename (pstate->language (), | 
|  | "short"); | 
|  | } | 
|  | ; | 
|  |  | 
|  | parameter_typelist: | 
|  | nonempty_typelist | 
|  | { check_parameter_typelist ($1); } | 
|  | |	nonempty_typelist ',' DOTDOTDOT | 
|  | { | 
|  | $1->push_back (NULL); | 
|  | check_parameter_typelist ($1); | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | nonempty_typelist | 
|  | :	type | 
|  | { | 
|  | std::vector<struct type *> *typelist | 
|  | = new std::vector<struct type *>; | 
|  | cpstate->type_lists.emplace_back (typelist); | 
|  |  | 
|  | typelist->push_back ($1); | 
|  | $$ = typelist; | 
|  | } | 
|  | |	nonempty_typelist ',' type | 
|  | { | 
|  | $1->push_back ($3); | 
|  | $$ = $1; | 
|  | } | 
|  | ; | 
|  |  | 
|  | ptype	:	typebase | 
|  | |	ptype abs_decl | 
|  | { | 
|  | cpstate->type_stack.push ($2); | 
|  | $$ = cpstate->type_stack.follow_types ($1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | conversion_type_id: typebase conversion_declarator | 
|  | { $$ = cpstate->type_stack.follow_types ($1); } | 
|  | ; | 
|  |  | 
|  | conversion_declarator:  /* Nothing.  */ | 
|  | | ptr_operator conversion_declarator | 
|  | ; | 
|  |  | 
|  | const_and_volatile: 	CONST_KEYWORD VOLATILE_KEYWORD | 
|  | | 		VOLATILE_KEYWORD CONST_KEYWORD | 
|  | ; | 
|  |  | 
|  | const_or_volatile_noopt:  	const_and_volatile | 
|  | { cpstate->type_stack.insert (tp_const); | 
|  | cpstate->type_stack.insert (tp_volatile); | 
|  | } | 
|  | | 		CONST_KEYWORD | 
|  | { cpstate->type_stack.insert (tp_const); } | 
|  | | 		VOLATILE_KEYWORD | 
|  | { cpstate->type_stack.insert (tp_volatile); } | 
|  | ; | 
|  |  | 
|  | oper:	OPERATOR NEW | 
|  | { $$ = operator_stoken (" new"); } | 
|  | |	OPERATOR DELETE | 
|  | { $$ = operator_stoken (" delete"); } | 
|  | |	OPERATOR NEW '[' ']' | 
|  | { $$ = operator_stoken (" new[]"); } | 
|  | |	OPERATOR DELETE '[' ']' | 
|  | { $$ = operator_stoken (" delete[]"); } | 
|  | |	OPERATOR NEW OBJC_LBRAC ']' | 
|  | { $$ = operator_stoken (" new[]"); } | 
|  | |	OPERATOR DELETE OBJC_LBRAC ']' | 
|  | { $$ = operator_stoken (" delete[]"); } | 
|  | |	OPERATOR '+' | 
|  | { $$ = operator_stoken ("+"); } | 
|  | |	OPERATOR '-' | 
|  | { $$ = operator_stoken ("-"); } | 
|  | |	OPERATOR '*' | 
|  | { $$ = operator_stoken ("*"); } | 
|  | |	OPERATOR '/' | 
|  | { $$ = operator_stoken ("/"); } | 
|  | |	OPERATOR '%' | 
|  | { $$ = operator_stoken ("%"); } | 
|  | |	OPERATOR '^' | 
|  | { $$ = operator_stoken ("^"); } | 
|  | |	OPERATOR '&' | 
|  | { $$ = operator_stoken ("&"); } | 
|  | |	OPERATOR '|' | 
|  | { $$ = operator_stoken ("|"); } | 
|  | |	OPERATOR '~' | 
|  | { $$ = operator_stoken ("~"); } | 
|  | |	OPERATOR '!' | 
|  | { $$ = operator_stoken ("!"); } | 
|  | |	OPERATOR '=' | 
|  | { $$ = operator_stoken ("="); } | 
|  | |	OPERATOR '<' | 
|  | { $$ = operator_stoken ("<"); } | 
|  | |	OPERATOR '>' | 
|  | { $$ = operator_stoken (">"); } | 
|  | |	OPERATOR ASSIGN_MODIFY | 
|  | { const char *op = " unknown"; | 
|  | switch ($2) | 
|  | { | 
|  | case BINOP_RSH: | 
|  | op = ">>="; | 
|  | break; | 
|  | case BINOP_LSH: | 
|  | op = "<<="; | 
|  | break; | 
|  | case BINOP_ADD: | 
|  | op = "+="; | 
|  | break; | 
|  | case BINOP_SUB: | 
|  | op = "-="; | 
|  | break; | 
|  | case BINOP_MUL: | 
|  | op = "*="; | 
|  | break; | 
|  | case BINOP_DIV: | 
|  | op = "/="; | 
|  | break; | 
|  | case BINOP_REM: | 
|  | op = "%="; | 
|  | break; | 
|  | case BINOP_BITWISE_IOR: | 
|  | op = "|="; | 
|  | break; | 
|  | case BINOP_BITWISE_AND: | 
|  | op = "&="; | 
|  | break; | 
|  | case BINOP_BITWISE_XOR: | 
|  | op = "^="; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | $$ = operator_stoken (op); | 
|  | } | 
|  | |	OPERATOR LSH | 
|  | { $$ = operator_stoken ("<<"); } | 
|  | |	OPERATOR RSH | 
|  | { $$ = operator_stoken (">>"); } | 
|  | |	OPERATOR EQUAL | 
|  | { $$ = operator_stoken ("=="); } | 
|  | |	OPERATOR NOTEQUAL | 
|  | { $$ = operator_stoken ("!="); } | 
|  | |	OPERATOR LEQ | 
|  | { $$ = operator_stoken ("<="); } | 
|  | |	OPERATOR GEQ | 
|  | { $$ = operator_stoken (">="); } | 
|  | |	OPERATOR ANDAND | 
|  | { $$ = operator_stoken ("&&"); } | 
|  | |	OPERATOR OROR | 
|  | { $$ = operator_stoken ("||"); } | 
|  | |	OPERATOR INCREMENT | 
|  | { $$ = operator_stoken ("++"); } | 
|  | |	OPERATOR DECREMENT | 
|  | { $$ = operator_stoken ("--"); } | 
|  | |	OPERATOR ',' | 
|  | { $$ = operator_stoken (","); } | 
|  | |	OPERATOR ARROW_STAR | 
|  | { $$ = operator_stoken ("->*"); } | 
|  | |	OPERATOR ARROW | 
|  | { $$ = operator_stoken ("->"); } | 
|  | |	OPERATOR '(' ')' | 
|  | { $$ = operator_stoken ("()"); } | 
|  | |	OPERATOR '[' ']' | 
|  | { $$ = operator_stoken ("[]"); } | 
|  | |	OPERATOR OBJC_LBRAC ']' | 
|  | { $$ = operator_stoken ("[]"); } | 
|  | |	OPERATOR conversion_type_id | 
|  | { | 
|  | string_file buf; | 
|  | c_print_type ($2, NULL, &buf, -1, 0, | 
|  | pstate->language ()->la_language, | 
|  | &type_print_raw_options); | 
|  | std::string name = buf.release (); | 
|  |  | 
|  | /* This also needs canonicalization.  */ | 
|  | gdb::unique_xmalloc_ptr<char> canon | 
|  | = cp_canonicalize_string (name.c_str ()); | 
|  | if (canon != nullptr) | 
|  | name = canon.get (); | 
|  | $$ = operator_stoken ((" " + name).c_str ()); | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* This rule exists in order to allow some tokens that would not normally | 
|  | match the 'name' rule to appear as fields within a struct.  The example | 
|  | that initially motivated this was the RISC-V target which models the | 
|  | floating point registers as a union with fields called 'float' and | 
|  | 'double'.  */ | 
|  | field_name | 
|  | :	name | 
|  | |	DOUBLE_KEYWORD { $$ = typename_stoken ("double"); } | 
|  | |	FLOAT_KEYWORD { $$ = typename_stoken ("float"); } | 
|  | |	INT_KEYWORD { $$ = typename_stoken ("int"); } | 
|  | |	LONG { $$ = typename_stoken ("long"); } | 
|  | |	SHORT { $$ = typename_stoken ("short"); } | 
|  | |	SIGNED_KEYWORD { $$ = typename_stoken ("signed"); } | 
|  | |	UNSIGNED { $$ = typename_stoken ("unsigned"); } | 
|  | ; | 
|  |  | 
|  | name	:	NAME { $$ = $1.stoken; } | 
|  | |	BLOCKNAME { $$ = $1.stoken; } | 
|  | |	TYPENAME { $$ = $1.stoken; } | 
|  | |	NAME_OR_INT  { $$ = $1.stoken; } | 
|  | |	UNKNOWN_CPP_NAME  { $$ = $1.stoken; } | 
|  | |	oper { $$ = $1; } | 
|  | ; | 
|  |  | 
|  | name_not_typename :	NAME | 
|  | |	BLOCKNAME | 
|  | /* These would be useful if name_not_typename was useful, but it is just | 
|  | a fake for "variable", so these cause reduce/reduce conflicts because | 
|  | the parser can't tell whether NAME_OR_INT is a name_not_typename (=variable, | 
|  | =exp) or just an exp.  If name_not_typename was ever used in an lvalue | 
|  | context where only a name could occur, this might be useful. | 
|  | |	NAME_OR_INT | 
|  | */ | 
|  | |	oper | 
|  | { | 
|  | struct field_of_this_result is_a_field_of_this; | 
|  |  | 
|  | $$.stoken = $1; | 
|  | $$.sym | 
|  | = lookup_symbol ($1.ptr, | 
|  | pstate->expression_context_block, | 
|  | VAR_DOMAIN, | 
|  | &is_a_field_of_this); | 
|  | $$.is_a_field_of_this | 
|  | = is_a_field_of_this.type != NULL; | 
|  | } | 
|  | |	UNKNOWN_CPP_NAME | 
|  | ; | 
|  |  | 
|  | %% | 
|  |  | 
|  | /* Returns a stoken of the operator name given by OP (which does not | 
|  | include the string "operator").  */ | 
|  |  | 
|  | static struct stoken | 
|  | operator_stoken (const char *op) | 
|  | { | 
|  | struct stoken st = { NULL, 0 }; | 
|  | char *buf; | 
|  |  | 
|  | st.length = CP_OPERATOR_LEN + strlen (op); | 
|  | buf = (char *) malloc (st.length + 1); | 
|  | strcpy (buf, CP_OPERATOR_STR); | 
|  | strcat (buf, op); | 
|  | st.ptr = buf; | 
|  |  | 
|  | /* The toplevel (c_parse) will free the memory allocated here.  */ | 
|  | cpstate->strings.emplace_back (buf); | 
|  | return st; | 
|  | }; | 
|  |  | 
|  | /* Returns a stoken of the type named TYPE.  */ | 
|  |  | 
|  | static struct stoken | 
|  | typename_stoken (const char *type) | 
|  | { | 
|  | struct stoken st = { type, 0 }; | 
|  | st.length = strlen (type); | 
|  | return st; | 
|  | }; | 
|  |  | 
|  | /* Return true if the type is aggregate-like.  */ | 
|  |  | 
|  | static int | 
|  | type_aggregate_p (struct type *type) | 
|  | { | 
|  | return (type->code () == TYPE_CODE_STRUCT | 
|  | || type->code () == TYPE_CODE_UNION | 
|  | || type->code () == TYPE_CODE_NAMESPACE | 
|  | || (type->code () == TYPE_CODE_ENUM | 
|  | && type->is_declared_class ())); | 
|  | } | 
|  |  | 
|  | /* Validate a parameter typelist.  */ | 
|  |  | 
|  | static void | 
|  | check_parameter_typelist (std::vector<struct type *> *params) | 
|  | { | 
|  | struct type *type; | 
|  | int ix; | 
|  |  | 
|  | for (ix = 0; ix < params->size (); ++ix) | 
|  | { | 
|  | type = (*params)[ix]; | 
|  | if (type != NULL && check_typedef (type)->code () == TYPE_CODE_VOID) | 
|  | { | 
|  | if (ix == 0) | 
|  | { | 
|  | if (params->size () == 1) | 
|  | { | 
|  | /* Ok.  */ | 
|  | break; | 
|  | } | 
|  | error (_("parameter types following 'void'")); | 
|  | } | 
|  | else | 
|  | error (_("'void' invalid as parameter type")); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Take care of parsing a number (anything that starts with a digit). | 
|  | Set yylval and return the token type; update lexptr. | 
|  | LEN is the number of characters in it.  */ | 
|  |  | 
|  | /*** Needs some error checking for the float case ***/ | 
|  |  | 
|  | static int | 
|  | parse_number (struct parser_state *par_state, | 
|  | const char *buf, int len, int parsed_float, YYSTYPE *putithere) | 
|  | { | 
|  | ULONGEST n = 0; | 
|  | ULONGEST prevn = 0; | 
|  |  | 
|  | int i = 0; | 
|  | int c; | 
|  | int base = input_radix; | 
|  | int unsigned_p = 0; | 
|  |  | 
|  | /* Number of "L" suffixes encountered.  */ | 
|  | int long_p = 0; | 
|  |  | 
|  | /* Imaginary number.  */ | 
|  | bool imaginary_p = false; | 
|  |  | 
|  | /* We have found a "L" or "U" (or "i") suffix.  */ | 
|  | int found_suffix = 0; | 
|  |  | 
|  | char *p; | 
|  |  | 
|  | p = (char *) alloca (len); | 
|  | memcpy (p, buf, len); | 
|  |  | 
|  | if (parsed_float) | 
|  | { | 
|  | if (len >= 1 && p[len - 1] == 'i') | 
|  | { | 
|  | imaginary_p = true; | 
|  | --len; | 
|  | } | 
|  |  | 
|  | /* Handle suffixes for decimal floating-point: "df", "dd" or "dl".  */ | 
|  | if (len >= 2 && p[len - 2] == 'd' && p[len - 1] == 'f') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_decfloat; | 
|  | len -= 2; | 
|  | } | 
|  | else if (len >= 2 && p[len - 2] == 'd' && p[len - 1] == 'd') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_decdouble; | 
|  | len -= 2; | 
|  | } | 
|  | else if (len >= 2 && p[len - 2] == 'd' && p[len - 1] == 'l') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_declong; | 
|  | len -= 2; | 
|  | } | 
|  | /* Handle suffixes: 'f' for float, 'l' for long double.  */ | 
|  | else if (len >= 1 && TOLOWER (p[len - 1]) == 'f') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_float; | 
|  | len -= 1; | 
|  | } | 
|  | else if (len >= 1 && TOLOWER (p[len - 1]) == 'l') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_long_double; | 
|  | len -= 1; | 
|  | } | 
|  | /* Default type for floating-point literals is double.  */ | 
|  | else | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_double; | 
|  | } | 
|  |  | 
|  | if (!parse_float (p, len, | 
|  | putithere->typed_val_float.type, | 
|  | putithere->typed_val_float.val)) | 
|  | return ERROR; | 
|  |  | 
|  | if (imaginary_p) | 
|  | putithere->typed_val_float.type | 
|  | = init_complex_type (nullptr, putithere->typed_val_float.type); | 
|  |  | 
|  | return imaginary_p ? COMPLEX_FLOAT : FLOAT; | 
|  | } | 
|  |  | 
|  | /* Handle base-switching prefixes 0x, 0t, 0d, 0 */ | 
|  | if (p[0] == '0' && len > 1) | 
|  | switch (p[1]) | 
|  | { | 
|  | case 'x': | 
|  | case 'X': | 
|  | if (len >= 3) | 
|  | { | 
|  | p += 2; | 
|  | base = 16; | 
|  | len -= 2; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'b': | 
|  | case 'B': | 
|  | if (len >= 3) | 
|  | { | 
|  | p += 2; | 
|  | base = 2; | 
|  | len -= 2; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 't': | 
|  | case 'T': | 
|  | case 'd': | 
|  | case 'D': | 
|  | if (len >= 3) | 
|  | { | 
|  | p += 2; | 
|  | base = 10; | 
|  | len -= 2; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | base = 8; | 
|  | break; | 
|  | } | 
|  |  | 
|  | while (len-- > 0) | 
|  | { | 
|  | c = *p++; | 
|  | if (c >= 'A' && c <= 'Z') | 
|  | c += 'a' - 'A'; | 
|  | if (c != 'l' && c != 'u' && c != 'i') | 
|  | n *= base; | 
|  | if (c >= '0' && c <= '9') | 
|  | { | 
|  | if (found_suffix) | 
|  | return ERROR; | 
|  | n += i = c - '0'; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (base > 10 && c >= 'a' && c <= 'f') | 
|  | { | 
|  | if (found_suffix) | 
|  | return ERROR; | 
|  | n += i = c - 'a' + 10; | 
|  | } | 
|  | else if (c == 'l') | 
|  | { | 
|  | ++long_p; | 
|  | found_suffix = 1; | 
|  | } | 
|  | else if (c == 'u') | 
|  | { | 
|  | unsigned_p = 1; | 
|  | found_suffix = 1; | 
|  | } | 
|  | else if (c == 'i') | 
|  | { | 
|  | imaginary_p = true; | 
|  | found_suffix = 1; | 
|  | } | 
|  | else | 
|  | return ERROR;	/* Char not a digit */ | 
|  | } | 
|  | if (i >= base) | 
|  | return ERROR;		/* Invalid digit in this base */ | 
|  |  | 
|  | if (c != 'l' && c != 'u' && c != 'i') | 
|  | { | 
|  | /* Test for overflow.  */ | 
|  | if (prevn == 0 && n == 0) | 
|  | ; | 
|  | else if (prevn >= n) | 
|  | error (_("Numeric constant too large.")); | 
|  | } | 
|  | prevn = n; | 
|  | } | 
|  |  | 
|  | /* An integer constant is an int, a long, or a long long.  An L | 
|  | suffix forces it to be long; an LL suffix forces it to be long | 
|  | long.  If not forced to a larger size, it gets the first type of | 
|  | the above that it fits in.  To figure out whether it fits, we | 
|  | shift it right and see whether anything remains.  Note that we | 
|  | can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or more in one | 
|  | operation, because many compilers will warn about such a shift | 
|  | (which always produces a zero result).  Sometimes gdbarch_int_bit | 
|  | or gdbarch_long_bit will be that big, sometimes not.  To deal with | 
|  | the case where it is we just always shift the value more than | 
|  | once, with fewer bits each time.  */ | 
|  | int int_bits = gdbarch_int_bit (par_state->gdbarch ()); | 
|  | int long_bits = gdbarch_long_bit (par_state->gdbarch ()); | 
|  | int long_long_bits = gdbarch_long_long_bit (par_state->gdbarch ()); | 
|  | bool have_signed | 
|  | /* No 'u' suffix.  */ | 
|  | = !unsigned_p; | 
|  | bool have_unsigned | 
|  | = ((/* 'u' suffix.  */ | 
|  | unsigned_p) | 
|  | || (/* Not a decimal.  */ | 
|  | base != 10) | 
|  | || (/* Allowed as a convenience, in case decimal doesn't fit in largest | 
|  | signed type.  */ | 
|  | !fits_in_type (1, n, long_long_bits, true))); | 
|  | bool have_int | 
|  | /* No 'l' or 'll' suffix.  */ | 
|  | = long_p == 0; | 
|  | bool have_long | 
|  | /* No 'll' suffix.  */ | 
|  | = long_p <= 1; | 
|  | if (have_int && have_signed && fits_in_type (1, n, int_bits, true)) | 
|  | putithere->typed_val_int.type = parse_type (par_state)->builtin_int; | 
|  | else if (have_int && have_unsigned && fits_in_type (1, n, int_bits, false)) | 
|  | putithere->typed_val_int.type | 
|  | = parse_type (par_state)->builtin_unsigned_int; | 
|  | else if (have_long && have_signed && fits_in_type (1, n, long_bits, true)) | 
|  | putithere->typed_val_int.type = parse_type (par_state)->builtin_long; | 
|  | else if (have_long && have_unsigned && fits_in_type (1, n, long_bits, false)) | 
|  | putithere->typed_val_int.type | 
|  | = parse_type (par_state)->builtin_unsigned_long; | 
|  | else if (have_signed && fits_in_type (1, n, long_long_bits, true)) | 
|  | putithere->typed_val_int.type | 
|  | = parse_type (par_state)->builtin_long_long; | 
|  | else if (have_unsigned && fits_in_type (1, n, long_long_bits, false)) | 
|  | putithere->typed_val_int.type | 
|  | = parse_type (par_state)->builtin_unsigned_long_long; | 
|  | else | 
|  | error (_("Numeric constant too large.")); | 
|  | putithere->typed_val_int.val = n; | 
|  |  | 
|  | if (imaginary_p) | 
|  | putithere->typed_val_int.type | 
|  | = init_complex_type (nullptr, putithere->typed_val_int.type); | 
|  |  | 
|  | return imaginary_p ? COMPLEX_INT : INT; | 
|  | } | 
|  |  | 
|  | /* Temporary obstack used for holding strings.  */ | 
|  | static struct obstack tempbuf; | 
|  | static int tempbuf_init; | 
|  |  | 
|  | /* Parse a C escape sequence.  The initial backslash of the sequence | 
|  | is at (*PTR)[-1].  *PTR will be updated to point to just after the | 
|  | last character of the sequence.  If OUTPUT is not NULL, the | 
|  | translated form of the escape sequence will be written there.  If | 
|  | OUTPUT is NULL, no output is written and the call will only affect | 
|  | *PTR.  If an escape sequence is expressed in target bytes, then the | 
|  | entire sequence will simply be copied to OUTPUT.  Return 1 if any | 
|  | character was emitted, 0 otherwise.  */ | 
|  |  | 
|  | int | 
|  | c_parse_escape (const char **ptr, struct obstack *output) | 
|  | { | 
|  | const char *tokptr = *ptr; | 
|  | int result = 1; | 
|  |  | 
|  | /* Some escape sequences undergo character set conversion.  Those we | 
|  | translate here.  */ | 
|  | switch (*tokptr) | 
|  | { | 
|  | /* Hex escapes do not undergo character set conversion, so keep | 
|  | the escape sequence for later.  */ | 
|  | case 'x': | 
|  | if (output) | 
|  | obstack_grow_str (output, "\\x"); | 
|  | ++tokptr; | 
|  | if (!ISXDIGIT (*tokptr)) | 
|  | error (_("\\x escape without a following hex digit")); | 
|  | while (ISXDIGIT (*tokptr)) | 
|  | { | 
|  | if (output) | 
|  | obstack_1grow (output, *tokptr); | 
|  | ++tokptr; | 
|  | } | 
|  | break; | 
|  |  | 
|  | /* Octal escapes do not undergo character set conversion, so | 
|  | keep the escape sequence for later.  */ | 
|  | case '0': | 
|  | case '1': | 
|  | case '2': | 
|  | case '3': | 
|  | case '4': | 
|  | case '5': | 
|  | case '6': | 
|  | case '7': | 
|  | { | 
|  | int i; | 
|  | if (output) | 
|  | obstack_grow_str (output, "\\"); | 
|  | for (i = 0; | 
|  | i < 3 && ISDIGIT (*tokptr) && *tokptr != '8' && *tokptr != '9'; | 
|  | ++i) | 
|  | { | 
|  | if (output) | 
|  | obstack_1grow (output, *tokptr); | 
|  | ++tokptr; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | /* We handle UCNs later.  We could handle them here, but that | 
|  | would mean a spurious error in the case where the UCN could | 
|  | be converted to the target charset but not the host | 
|  | charset.  */ | 
|  | case 'u': | 
|  | case 'U': | 
|  | { | 
|  | char c = *tokptr; | 
|  | int i, len = c == 'U' ? 8 : 4; | 
|  | if (output) | 
|  | { | 
|  | obstack_1grow (output, '\\'); | 
|  | obstack_1grow (output, *tokptr); | 
|  | } | 
|  | ++tokptr; | 
|  | if (!ISXDIGIT (*tokptr)) | 
|  | error (_("\\%c escape without a following hex digit"), c); | 
|  | for (i = 0; i < len && ISXDIGIT (*tokptr); ++i) | 
|  | { | 
|  | if (output) | 
|  | obstack_1grow (output, *tokptr); | 
|  | ++tokptr; | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | /* We must pass backslash through so that it does not | 
|  | cause quoting during the second expansion.  */ | 
|  | case '\\': | 
|  | if (output) | 
|  | obstack_grow_str (output, "\\\\"); | 
|  | ++tokptr; | 
|  | break; | 
|  |  | 
|  | /* Escapes which undergo conversion.  */ | 
|  | case 'a': | 
|  | if (output) | 
|  | obstack_1grow (output, '\a'); | 
|  | ++tokptr; | 
|  | break; | 
|  | case 'b': | 
|  | if (output) | 
|  | obstack_1grow (output, '\b'); | 
|  | ++tokptr; | 
|  | break; | 
|  | case 'f': | 
|  | if (output) | 
|  | obstack_1grow (output, '\f'); | 
|  | ++tokptr; | 
|  | break; | 
|  | case 'n': | 
|  | if (output) | 
|  | obstack_1grow (output, '\n'); | 
|  | ++tokptr; | 
|  | break; | 
|  | case 'r': | 
|  | if (output) | 
|  | obstack_1grow (output, '\r'); | 
|  | ++tokptr; | 
|  | break; | 
|  | case 't': | 
|  | if (output) | 
|  | obstack_1grow (output, '\t'); | 
|  | ++tokptr; | 
|  | break; | 
|  | case 'v': | 
|  | if (output) | 
|  | obstack_1grow (output, '\v'); | 
|  | ++tokptr; | 
|  | break; | 
|  |  | 
|  | /* GCC extension.  */ | 
|  | case 'e': | 
|  | if (output) | 
|  | obstack_1grow (output, HOST_ESCAPE_CHAR); | 
|  | ++tokptr; | 
|  | break; | 
|  |  | 
|  | /* Backslash-newline expands to nothing at all.  */ | 
|  | case '\n': | 
|  | ++tokptr; | 
|  | result = 0; | 
|  | break; | 
|  |  | 
|  | /* A few escapes just expand to the character itself.  */ | 
|  | case '\'': | 
|  | case '\"': | 
|  | case '?': | 
|  | /* GCC extensions.  */ | 
|  | case '(': | 
|  | case '{': | 
|  | case '[': | 
|  | case '%': | 
|  | /* Unrecognized escapes turn into the character itself.  */ | 
|  | default: | 
|  | if (output) | 
|  | obstack_1grow (output, *tokptr); | 
|  | ++tokptr; | 
|  | break; | 
|  | } | 
|  | *ptr = tokptr; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* Parse a string or character literal from TOKPTR.  The string or | 
|  | character may be wide or unicode.  *OUTPTR is set to just after the | 
|  | end of the literal in the input string.  The resulting token is | 
|  | stored in VALUE.  This returns a token value, either STRING or | 
|  | CHAR, depending on what was parsed.  *HOST_CHARS is set to the | 
|  | number of host characters in the literal.  */ | 
|  |  | 
|  | static int | 
|  | parse_string_or_char (const char *tokptr, const char **outptr, | 
|  | struct typed_stoken *value, int *host_chars) | 
|  | { | 
|  | int quote; | 
|  | c_string_type type; | 
|  | int is_objc = 0; | 
|  |  | 
|  | /* Build the gdb internal form of the input string in tempbuf.  Note | 
|  | that the buffer is null byte terminated *only* for the | 
|  | convenience of debugging gdb itself and printing the buffer | 
|  | contents when the buffer contains no embedded nulls.  Gdb does | 
|  | not depend upon the buffer being null byte terminated, it uses | 
|  | the length string instead.  This allows gdb to handle C strings | 
|  | (as well as strings in other languages) with embedded null | 
|  | bytes */ | 
|  |  | 
|  | if (!tempbuf_init) | 
|  | tempbuf_init = 1; | 
|  | else | 
|  | obstack_free (&tempbuf, NULL); | 
|  | obstack_init (&tempbuf); | 
|  |  | 
|  | /* Record the string type.  */ | 
|  | if (*tokptr == 'L') | 
|  | { | 
|  | type = C_WIDE_STRING; | 
|  | ++tokptr; | 
|  | } | 
|  | else if (*tokptr == 'u') | 
|  | { | 
|  | type = C_STRING_16; | 
|  | ++tokptr; | 
|  | } | 
|  | else if (*tokptr == 'U') | 
|  | { | 
|  | type = C_STRING_32; | 
|  | ++tokptr; | 
|  | } | 
|  | else if (*tokptr == '@') | 
|  | { | 
|  | /* An Objective C string.  */ | 
|  | is_objc = 1; | 
|  | type = C_STRING; | 
|  | ++tokptr; | 
|  | } | 
|  | else | 
|  | type = C_STRING; | 
|  |  | 
|  | /* Skip the quote.  */ | 
|  | quote = *tokptr; | 
|  | if (quote == '\'') | 
|  | type |= C_CHAR; | 
|  | ++tokptr; | 
|  |  | 
|  | *host_chars = 0; | 
|  |  | 
|  | while (*tokptr) | 
|  | { | 
|  | char c = *tokptr; | 
|  | if (c == '\\') | 
|  | { | 
|  | ++tokptr; | 
|  | *host_chars += c_parse_escape (&tokptr, &tempbuf); | 
|  | } | 
|  | else if (c == quote) | 
|  | break; | 
|  | else | 
|  | { | 
|  | obstack_1grow (&tempbuf, c); | 
|  | ++tokptr; | 
|  | /* FIXME: this does the wrong thing with multi-byte host | 
|  | characters.  We could use mbrlen here, but that would | 
|  | make "set host-charset" a bit less useful.  */ | 
|  | ++*host_chars; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (*tokptr != quote) | 
|  | { | 
|  | if (quote == '"') | 
|  | error (_("Unterminated string in expression.")); | 
|  | else | 
|  | error (_("Unmatched single quote.")); | 
|  | } | 
|  | ++tokptr; | 
|  |  | 
|  | value->type = type; | 
|  | value->ptr = (char *) obstack_base (&tempbuf); | 
|  | value->length = obstack_object_size (&tempbuf); | 
|  |  | 
|  | *outptr = tokptr; | 
|  |  | 
|  | return quote == '"' ? (is_objc ? NSSTRING : STRING) : CHAR; | 
|  | } | 
|  |  | 
|  | /* This is used to associate some attributes with a token.  */ | 
|  |  | 
|  | enum token_flag | 
|  | { | 
|  | /* If this bit is set, the token is C++-only.  */ | 
|  |  | 
|  | FLAG_CXX = 1, | 
|  |  | 
|  | /* If this bit is set, the token is C-only.  */ | 
|  |  | 
|  | FLAG_C = 2, | 
|  |  | 
|  | /* If this bit is set, the token is conditional: if there is a | 
|  | symbol of the same name, then the token is a symbol; otherwise, | 
|  | the token is a keyword.  */ | 
|  |  | 
|  | FLAG_SHADOW = 4 | 
|  | }; | 
|  | DEF_ENUM_FLAGS_TYPE (enum token_flag, token_flags); | 
|  |  | 
|  | struct token | 
|  | { | 
|  | const char *oper; | 
|  | int token; | 
|  | enum exp_opcode opcode; | 
|  | token_flags flags; | 
|  | }; | 
|  |  | 
|  | static const struct token tokentab3[] = | 
|  | { | 
|  | {">>=", ASSIGN_MODIFY, BINOP_RSH, 0}, | 
|  | {"<<=", ASSIGN_MODIFY, BINOP_LSH, 0}, | 
|  | {"->*", ARROW_STAR, OP_NULL, FLAG_CXX}, | 
|  | {"...", DOTDOTDOT, OP_NULL, 0} | 
|  | }; | 
|  |  | 
|  | static const struct token tokentab2[] = | 
|  | { | 
|  | {"+=", ASSIGN_MODIFY, BINOP_ADD, 0}, | 
|  | {"-=", ASSIGN_MODIFY, BINOP_SUB, 0}, | 
|  | {"*=", ASSIGN_MODIFY, BINOP_MUL, 0}, | 
|  | {"/=", ASSIGN_MODIFY, BINOP_DIV, 0}, | 
|  | {"%=", ASSIGN_MODIFY, BINOP_REM, 0}, | 
|  | {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR, 0}, | 
|  | {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND, 0}, | 
|  | {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR, 0}, | 
|  | {"++", INCREMENT, OP_NULL, 0}, | 
|  | {"--", DECREMENT, OP_NULL, 0}, | 
|  | {"->", ARROW, OP_NULL, 0}, | 
|  | {"&&", ANDAND, OP_NULL, 0}, | 
|  | {"||", OROR, OP_NULL, 0}, | 
|  | /* "::" is *not* only C++: gdb overrides its meaning in several | 
|  | different ways, e.g., 'filename'::func, function::variable.  */ | 
|  | {"::", COLONCOLON, OP_NULL, 0}, | 
|  | {"<<", LSH, OP_NULL, 0}, | 
|  | {">>", RSH, OP_NULL, 0}, | 
|  | {"==", EQUAL, OP_NULL, 0}, | 
|  | {"!=", NOTEQUAL, OP_NULL, 0}, | 
|  | {"<=", LEQ, OP_NULL, 0}, | 
|  | {">=", GEQ, OP_NULL, 0}, | 
|  | {".*", DOT_STAR, OP_NULL, FLAG_CXX} | 
|  | }; | 
|  |  | 
|  | /* Identifier-like tokens.  Only type-specifiers than can appear in | 
|  | multi-word type names (for example 'double' can appear in 'long | 
|  | double') need to be listed here.  type-specifiers that are only ever | 
|  | single word (like 'char') are handled by the classify_name function.  */ | 
|  | static const struct token ident_tokens[] = | 
|  | { | 
|  | {"unsigned", UNSIGNED, OP_NULL, 0}, | 
|  | {"template", TEMPLATE, OP_NULL, FLAG_CXX}, | 
|  | {"volatile", VOLATILE_KEYWORD, OP_NULL, 0}, | 
|  | {"struct", STRUCT, OP_NULL, 0}, | 
|  | {"signed", SIGNED_KEYWORD, OP_NULL, 0}, | 
|  | {"sizeof", SIZEOF, OP_NULL, 0}, | 
|  | {"_Alignof", ALIGNOF, OP_NULL, 0}, | 
|  | {"alignof", ALIGNOF, OP_NULL, FLAG_CXX}, | 
|  | {"double", DOUBLE_KEYWORD, OP_NULL, 0}, | 
|  | {"float", FLOAT_KEYWORD, OP_NULL, 0}, | 
|  | {"false", FALSEKEYWORD, OP_NULL, FLAG_CXX}, | 
|  | {"class", CLASS, OP_NULL, FLAG_CXX}, | 
|  | {"union", UNION, OP_NULL, 0}, | 
|  | {"short", SHORT, OP_NULL, 0}, | 
|  | {"const", CONST_KEYWORD, OP_NULL, 0}, | 
|  | {"restrict", RESTRICT, OP_NULL, FLAG_C | FLAG_SHADOW}, | 
|  | {"__restrict__", RESTRICT, OP_NULL, 0}, | 
|  | {"__restrict", RESTRICT, OP_NULL, 0}, | 
|  | {"_Atomic", ATOMIC, OP_NULL, 0}, | 
|  | {"enum", ENUM, OP_NULL, 0}, | 
|  | {"long", LONG, OP_NULL, 0}, | 
|  | {"_Complex", COMPLEX, OP_NULL, 0}, | 
|  | {"__complex__", COMPLEX, OP_NULL, 0}, | 
|  |  | 
|  | {"true", TRUEKEYWORD, OP_NULL, FLAG_CXX}, | 
|  | {"int", INT_KEYWORD, OP_NULL, 0}, | 
|  | {"new", NEW, OP_NULL, FLAG_CXX}, | 
|  | {"delete", DELETE, OP_NULL, FLAG_CXX}, | 
|  | {"operator", OPERATOR, OP_NULL, FLAG_CXX}, | 
|  |  | 
|  | {"and", ANDAND, OP_NULL, FLAG_CXX}, | 
|  | {"and_eq", ASSIGN_MODIFY, BINOP_BITWISE_AND, FLAG_CXX}, | 
|  | {"bitand", '&', OP_NULL, FLAG_CXX}, | 
|  | {"bitor", '|', OP_NULL, FLAG_CXX}, | 
|  | {"compl", '~', OP_NULL, FLAG_CXX}, | 
|  | {"not", '!', OP_NULL, FLAG_CXX}, | 
|  | {"not_eq", NOTEQUAL, OP_NULL, FLAG_CXX}, | 
|  | {"or", OROR, OP_NULL, FLAG_CXX}, | 
|  | {"or_eq", ASSIGN_MODIFY, BINOP_BITWISE_IOR, FLAG_CXX}, | 
|  | {"xor", '^', OP_NULL, FLAG_CXX}, | 
|  | {"xor_eq", ASSIGN_MODIFY, BINOP_BITWISE_XOR, FLAG_CXX}, | 
|  |  | 
|  | {"const_cast", CONST_CAST, OP_NULL, FLAG_CXX }, | 
|  | {"dynamic_cast", DYNAMIC_CAST, OP_NULL, FLAG_CXX }, | 
|  | {"static_cast", STATIC_CAST, OP_NULL, FLAG_CXX }, | 
|  | {"reinterpret_cast", REINTERPRET_CAST, OP_NULL, FLAG_CXX }, | 
|  |  | 
|  | {"__typeof__", TYPEOF, OP_TYPEOF, 0 }, | 
|  | {"__typeof", TYPEOF, OP_TYPEOF, 0 }, | 
|  | {"typeof", TYPEOF, OP_TYPEOF, FLAG_SHADOW }, | 
|  | {"__decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX }, | 
|  | {"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW }, | 
|  |  | 
|  | {"typeid", TYPEID, OP_TYPEID, FLAG_CXX} | 
|  | }; | 
|  |  | 
|  |  | 
|  | static void | 
|  | scan_macro_expansion (const char *expansion) | 
|  | { | 
|  | /* We'd better not be trying to push the stack twice.  */ | 
|  | gdb_assert (! cpstate->macro_original_text); | 
|  |  | 
|  | /* Copy to the obstack.  */ | 
|  | const char *copy = obstack_strdup (&cpstate->expansion_obstack, expansion); | 
|  |  | 
|  | /* Save the old lexptr value, so we can return to it when we're done | 
|  | parsing the expanded text.  */ | 
|  | cpstate->macro_original_text = pstate->lexptr; | 
|  | pstate->lexptr = copy; | 
|  | } | 
|  |  | 
|  | static int | 
|  | scanning_macro_expansion (void) | 
|  | { | 
|  | return cpstate->macro_original_text != 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | finished_macro_expansion (void) | 
|  | { | 
|  | /* There'd better be something to pop back to.  */ | 
|  | gdb_assert (cpstate->macro_original_text); | 
|  |  | 
|  | /* Pop back to the original text.  */ | 
|  | pstate->lexptr = cpstate->macro_original_text; | 
|  | cpstate->macro_original_text = 0; | 
|  | } | 
|  |  | 
|  | /* Return true iff the token represents a C++ cast operator.  */ | 
|  |  | 
|  | static int | 
|  | is_cast_operator (const char *token, int len) | 
|  | { | 
|  | return (! strncmp (token, "dynamic_cast", len) | 
|  | || ! strncmp (token, "static_cast", len) | 
|  | || ! strncmp (token, "reinterpret_cast", len) | 
|  | || ! strncmp (token, "const_cast", len)); | 
|  | } | 
|  |  | 
|  | /* The scope used for macro expansion.  */ | 
|  | static struct macro_scope *expression_macro_scope; | 
|  |  | 
|  | /* This is set if a NAME token appeared at the very end of the input | 
|  | string, with no whitespace separating the name from the EOF.  This | 
|  | is used only when parsing to do field name completion.  */ | 
|  | static int saw_name_at_eof; | 
|  |  | 
|  | /* This is set if the previously-returned token was a structure | 
|  | operator -- either '.' or ARROW.  */ | 
|  | static bool last_was_structop; | 
|  |  | 
|  | /* Depth of parentheses.  */ | 
|  | static int paren_depth; | 
|  |  | 
|  | /* Read one token, getting characters through lexptr.  */ | 
|  |  | 
|  | static int | 
|  | lex_one_token (struct parser_state *par_state, bool *is_quoted_name) | 
|  | { | 
|  | int c; | 
|  | int namelen; | 
|  | const char *tokstart; | 
|  | bool saw_structop = last_was_structop; | 
|  |  | 
|  | last_was_structop = false; | 
|  | *is_quoted_name = false; | 
|  |  | 
|  | retry: | 
|  |  | 
|  | /* Check if this is a macro invocation that we need to expand.  */ | 
|  | if (! scanning_macro_expansion ()) | 
|  | { | 
|  | gdb::unique_xmalloc_ptr<char> expanded | 
|  | = macro_expand_next (&pstate->lexptr, *expression_macro_scope); | 
|  |  | 
|  | if (expanded != nullptr) | 
|  | scan_macro_expansion (expanded.get ()); | 
|  | } | 
|  |  | 
|  | pstate->prev_lexptr = pstate->lexptr; | 
|  |  | 
|  | tokstart = pstate->lexptr; | 
|  | /* See if it is a special token of length 3.  */ | 
|  | for (const auto &token : tokentab3) | 
|  | if (strncmp (tokstart, token.oper, 3) == 0) | 
|  | { | 
|  | if ((token.flags & FLAG_CXX) != 0 | 
|  | && par_state->language ()->la_language != language_cplus) | 
|  | break; | 
|  | gdb_assert ((token.flags & FLAG_C) == 0); | 
|  |  | 
|  | pstate->lexptr += 3; | 
|  | yylval.opcode = token.opcode; | 
|  | return token.token; | 
|  | } | 
|  |  | 
|  | /* See if it is a special token of length 2.  */ | 
|  | for (const auto &token : tokentab2) | 
|  | if (strncmp (tokstart, token.oper, 2) == 0) | 
|  | { | 
|  | if ((token.flags & FLAG_CXX) != 0 | 
|  | && par_state->language ()->la_language != language_cplus) | 
|  | break; | 
|  | gdb_assert ((token.flags & FLAG_C) == 0); | 
|  |  | 
|  | pstate->lexptr += 2; | 
|  | yylval.opcode = token.opcode; | 
|  | if (token.token == ARROW) | 
|  | last_was_structop = 1; | 
|  | return token.token; | 
|  | } | 
|  |  | 
|  | switch (c = *tokstart) | 
|  | { | 
|  | case 0: | 
|  | /* If we were just scanning the result of a macro expansion, | 
|  | then we need to resume scanning the original text. | 
|  | If we're parsing for field name completion, and the previous | 
|  | token allows such completion, return a COMPLETE token. | 
|  | Otherwise, we were already scanning the original text, and | 
|  | we're really done.  */ | 
|  | if (scanning_macro_expansion ()) | 
|  | { | 
|  | finished_macro_expansion (); | 
|  | goto retry; | 
|  | } | 
|  | else if (saw_name_at_eof) | 
|  | { | 
|  | saw_name_at_eof = 0; | 
|  | return COMPLETE; | 
|  | } | 
|  | else if (par_state->parse_completion && saw_structop) | 
|  | return COMPLETE; | 
|  | else | 
|  | return 0; | 
|  |  | 
|  | case ' ': | 
|  | case '\t': | 
|  | case '\n': | 
|  | pstate->lexptr++; | 
|  | goto retry; | 
|  |  | 
|  | case '[': | 
|  | case '(': | 
|  | paren_depth++; | 
|  | pstate->lexptr++; | 
|  | if (par_state->language ()->la_language == language_objc | 
|  | && c == '[') | 
|  | return OBJC_LBRAC; | 
|  | return c; | 
|  |  | 
|  | case ']': | 
|  | case ')': | 
|  | if (paren_depth == 0) | 
|  | return 0; | 
|  | paren_depth--; | 
|  | pstate->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case ',': | 
|  | if (pstate->comma_terminates | 
|  | && paren_depth == 0 | 
|  | && ! scanning_macro_expansion ()) | 
|  | return 0; | 
|  | pstate->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case '.': | 
|  | /* Might be a floating point number.  */ | 
|  | if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9') | 
|  | { | 
|  | last_was_structop = true; | 
|  | goto symbol;		/* Nope, must be a symbol. */ | 
|  | } | 
|  | /* FALL THRU.  */ | 
|  |  | 
|  | 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, got_p = 0, toktype; | 
|  | const char *p = tokstart; | 
|  | int hex = input_radix > 10; | 
|  |  | 
|  | 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 && !got_p && (*p == 'e' || *p == 'E')) | 
|  | got_dot = got_e = 1; | 
|  | else if (!got_e && !got_p && (*p == 'p' || *p == 'P')) | 
|  | got_dot = got_p = 1; | 
|  | /* This test does not include !hex, because a '.' always indicates | 
|  | a decimal floating point number regardless of the radix.  */ | 
|  | else if (!got_dot && *p == '.') | 
|  | got_dot = 1; | 
|  | else if (((got_e && (p[-1] == 'e' || p[-1] == 'E')) | 
|  | || (got_p && (p[-1] == 'p' || p[-1] == 'P'))) | 
|  | && (*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 ((*p < '0' || *p > '9') | 
|  | && ((*p < 'a' || *p > 'z') | 
|  | && (*p < 'A' || *p > 'Z'))) | 
|  | break; | 
|  | } | 
|  | toktype = parse_number (par_state, tokstart, p - tokstart, | 
|  | got_dot | got_e | got_p, &yylval); | 
|  | if (toktype == ERROR) | 
|  | { | 
|  | char *err_copy = (char *) alloca (p - tokstart + 1); | 
|  |  | 
|  | memcpy (err_copy, tokstart, p - tokstart); | 
|  | err_copy[p - tokstart] = 0; | 
|  | error (_("Invalid number \"%s\"."), err_copy); | 
|  | } | 
|  | pstate->lexptr = p; | 
|  | return toktype; | 
|  | } | 
|  |  | 
|  | case '@': | 
|  | { | 
|  | const char *p = &tokstart[1]; | 
|  |  | 
|  | if (par_state->language ()->la_language == language_objc) | 
|  | { | 
|  | size_t len = strlen ("selector"); | 
|  |  | 
|  | if (strncmp (p, "selector", len) == 0 | 
|  | && (p[len] == '\0' || ISSPACE (p[len]))) | 
|  | { | 
|  | pstate->lexptr = p + len; | 
|  | return SELECTOR; | 
|  | } | 
|  | else if (*p == '"') | 
|  | goto parse_string; | 
|  | } | 
|  |  | 
|  | while (ISSPACE (*p)) | 
|  | p++; | 
|  | size_t len = strlen ("entry"); | 
|  | if (strncmp (p, "entry", len) == 0 && !c_ident_is_alnum (p[len]) | 
|  | && p[len] != '_') | 
|  | { | 
|  | pstate->lexptr = &p[len]; | 
|  | return ENTRY; | 
|  | } | 
|  | } | 
|  | /* FALLTHRU */ | 
|  | case '+': | 
|  | case '-': | 
|  | case '*': | 
|  | case '/': | 
|  | case '%': | 
|  | case '|': | 
|  | case '&': | 
|  | case '^': | 
|  | case '~': | 
|  | case '!': | 
|  | case '<': | 
|  | case '>': | 
|  | case '?': | 
|  | case ':': | 
|  | case '=': | 
|  | case '{': | 
|  | case '}': | 
|  | symbol: | 
|  | pstate->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case 'L': | 
|  | case 'u': | 
|  | case 'U': | 
|  | if (tokstart[1] != '"' && tokstart[1] != '\'') | 
|  | break; | 
|  | /* Fall through.  */ | 
|  | case '\'': | 
|  | case '"': | 
|  |  | 
|  | parse_string: | 
|  | { | 
|  | int host_len; | 
|  | int result = parse_string_or_char (tokstart, &pstate->lexptr, | 
|  | &yylval.tsval, &host_len); | 
|  | if (result == CHAR) | 
|  | { | 
|  | if (host_len == 0) | 
|  | error (_("Empty character constant.")); | 
|  | else if (host_len > 2 && c == '\'') | 
|  | { | 
|  | ++tokstart; | 
|  | namelen = pstate->lexptr - tokstart - 1; | 
|  | *is_quoted_name = true; | 
|  |  | 
|  | goto tryname; | 
|  | } | 
|  | else if (host_len > 1) | 
|  | error (_("Invalid character constant.")); | 
|  | } | 
|  | return result; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!(c == '_' || c == '$' || c_ident_is_alpha (c))) | 
|  | /* We must have come across a bad character (e.g. ';').  */ | 
|  | error (_("Invalid character '%c' in expression."), c); | 
|  |  | 
|  | /* It's a name.  See how long it is.  */ | 
|  | namelen = 0; | 
|  | for (c = tokstart[namelen]; | 
|  | (c == '_' || c == '$' || c_ident_is_alnum (c) || c == '<');) | 
|  | { | 
|  | /* Template parameter lists are part of the name. | 
|  | FIXME: This mishandles `print $a<4&&$a>3'.  */ | 
|  |  | 
|  | if (c == '<') | 
|  | { | 
|  | if (! is_cast_operator (tokstart, namelen)) | 
|  | { | 
|  | /* Scan ahead to get rest of the template specification.  Note | 
|  | that we look ahead only when the '<' adjoins non-whitespace | 
|  | characters; for comparison expressions, e.g. "a < b > c", | 
|  | there must be spaces before the '<', etc. */ | 
|  | const char *p = find_template_name_end (tokstart + namelen); | 
|  |  | 
|  | if (p) | 
|  | namelen = p - tokstart; | 
|  | } | 
|  | break; | 
|  | } | 
|  | c = tokstart[++namelen]; | 
|  | } | 
|  |  | 
|  | /* The token "if" terminates the expression and is NOT removed from | 
|  | the input stream.  It doesn't count if it appears in the | 
|  | expansion of a macro.  */ | 
|  | if (namelen == 2 | 
|  | && tokstart[0] == 'i' | 
|  | && tokstart[1] == 'f' | 
|  | && ! scanning_macro_expansion ()) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* For the same reason (breakpoint conditions), "thread N" | 
|  | terminates the expression.  "thread" could be an identifier, but | 
|  | an identifier is never followed by a number without intervening | 
|  | punctuation.  "task" is similar.  Handle abbreviations of these, | 
|  | similarly to breakpoint.c:find_condition_and_thread.  */ | 
|  | if (namelen >= 1 | 
|  | && (strncmp (tokstart, "thread", namelen) == 0 | 
|  | || strncmp (tokstart, "task", namelen) == 0) | 
|  | && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t') | 
|  | && ! scanning_macro_expansion ()) | 
|  | { | 
|  | const char *p = tokstart + namelen + 1; | 
|  |  | 
|  | while (*p == ' ' || *p == '\t') | 
|  | p++; | 
|  | if (*p >= '0' && *p <= '9') | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | pstate->lexptr += namelen; | 
|  |  | 
|  | tryname: | 
|  |  | 
|  | yylval.sval.ptr = tokstart; | 
|  | yylval.sval.length = namelen; | 
|  |  | 
|  | /* Catch specific keywords.  */ | 
|  | std::string copy = copy_name (yylval.sval); | 
|  | for (const auto &token : ident_tokens) | 
|  | if (copy == token.oper) | 
|  | { | 
|  | if ((token.flags & FLAG_CXX) != 0 | 
|  | && par_state->language ()->la_language != language_cplus) | 
|  | break; | 
|  | if ((token.flags & FLAG_C) != 0 | 
|  | && par_state->language ()->la_language != language_c | 
|  | && par_state->language ()->la_language != language_objc) | 
|  | break; | 
|  |  | 
|  | if ((token.flags & FLAG_SHADOW) != 0) | 
|  | { | 
|  | struct field_of_this_result is_a_field_of_this; | 
|  |  | 
|  | if (lookup_symbol (copy.c_str (), | 
|  | pstate->expression_context_block, | 
|  | VAR_DOMAIN, | 
|  | (par_state->language ()->la_language | 
|  | == language_cplus ? &is_a_field_of_this | 
|  | : NULL)).symbol | 
|  | != NULL) | 
|  | { | 
|  | /* The keyword is shadowed.  */ | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* It is ok to always set this, even though we don't always | 
|  | strictly need to.  */ | 
|  | yylval.opcode = token.opcode; | 
|  | return token.token; | 
|  | } | 
|  |  | 
|  | if (*tokstart == '$') | 
|  | return DOLLAR_VARIABLE; | 
|  |  | 
|  | if (pstate->parse_completion && *pstate->lexptr == '\0') | 
|  | saw_name_at_eof = 1; | 
|  |  | 
|  | yylval.ssym.stoken = yylval.sval; | 
|  | yylval.ssym.sym.symbol = NULL; | 
|  | yylval.ssym.sym.block = NULL; | 
|  | yylval.ssym.is_a_field_of_this = 0; | 
|  | return NAME; | 
|  | } | 
|  |  | 
|  | /* An object of this type is pushed on a FIFO by the "outer" lexer.  */ | 
|  | struct token_and_value | 
|  | { | 
|  | int token; | 
|  | YYSTYPE value; | 
|  | }; | 
|  |  | 
|  | /* A FIFO of tokens that have been read but not yet returned to the | 
|  | parser.  */ | 
|  | static std::vector<token_and_value> token_fifo; | 
|  |  | 
|  | /* Non-zero if the lexer should return tokens from the FIFO.  */ | 
|  | static int popping; | 
|  |  | 
|  | /* Temporary storage for c_lex; this holds symbol names as they are | 
|  | built up.  */ | 
|  | static auto_obstack name_obstack; | 
|  |  | 
|  | /* Classify a NAME token.  The contents of the token are in `yylval'. | 
|  | Updates yylval and returns the new token type.  BLOCK is the block | 
|  | in which lookups start; this can be NULL to mean the global scope. | 
|  | IS_QUOTED_NAME is non-zero if the name token was originally quoted | 
|  | in single quotes.  IS_AFTER_STRUCTOP is true if this name follows | 
|  | a structure operator -- either '.' or ARROW  */ | 
|  |  | 
|  | static int | 
|  | classify_name (struct parser_state *par_state, const struct block *block, | 
|  | bool is_quoted_name, bool is_after_structop) | 
|  | { | 
|  | struct block_symbol bsym; | 
|  | struct field_of_this_result is_a_field_of_this; | 
|  |  | 
|  | std::string copy = copy_name (yylval.sval); | 
|  |  | 
|  | /* Initialize this in case we *don't* use it in this call; that way | 
|  | we can refer to it unconditionally below.  */ | 
|  | memset (&is_a_field_of_this, 0, sizeof (is_a_field_of_this)); | 
|  |  | 
|  | bsym = lookup_symbol (copy.c_str (), block, VAR_DOMAIN, | 
|  | par_state->language ()->name_of_this () | 
|  | ? &is_a_field_of_this : NULL); | 
|  |  | 
|  | if (bsym.symbol && bsym.symbol->aclass () == LOC_BLOCK) | 
|  | { | 
|  | yylval.ssym.sym = bsym; | 
|  | yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL; | 
|  | return BLOCKNAME; | 
|  | } | 
|  | else if (!bsym.symbol) | 
|  | { | 
|  | /* If we found a field of 'this', we might have erroneously | 
|  | found a constructor where we wanted a type name.  Handle this | 
|  | case by noticing that we found a constructor and then look up | 
|  | the type tag instead.  */ | 
|  | if (is_a_field_of_this.type != NULL | 
|  | && is_a_field_of_this.fn_field != NULL | 
|  | && TYPE_FN_FIELD_CONSTRUCTOR (is_a_field_of_this.fn_field->fn_fields, | 
|  | 0)) | 
|  | { | 
|  | struct field_of_this_result inner_is_a_field_of_this; | 
|  |  | 
|  | bsym = lookup_symbol (copy.c_str (), block, STRUCT_DOMAIN, | 
|  | &inner_is_a_field_of_this); | 
|  | if (bsym.symbol != NULL) | 
|  | { | 
|  | yylval.tsym.type = bsym.symbol->type (); | 
|  | return TYPENAME; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If we found a field on the "this" object, or we are looking | 
|  | up a field on a struct, then we want to prefer it over a | 
|  | filename.  However, if the name was quoted, then it is better | 
|  | to check for a filename or a block, since this is the only | 
|  | way the user has of requiring the extension to be used.  */ | 
|  | if ((is_a_field_of_this.type == NULL && !is_after_structop) | 
|  | || is_quoted_name) | 
|  | { | 
|  | /* See if it's a file name. */ | 
|  | struct symtab *symtab; | 
|  |  | 
|  | symtab = lookup_symtab (copy.c_str ()); | 
|  | if (symtab) | 
|  | { | 
|  | yylval.bval | 
|  | = symtab->compunit ()->blockvector ()->static_block (); | 
|  |  | 
|  | return FILENAME; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (bsym.symbol && bsym.symbol->aclass () == LOC_TYPEDEF) | 
|  | { | 
|  | yylval.tsym.type = bsym.symbol->type (); | 
|  | return TYPENAME; | 
|  | } | 
|  |  | 
|  | /* See if it's an ObjC classname.  */ | 
|  | if (par_state->language ()->la_language == language_objc && !bsym.symbol) | 
|  | { | 
|  | CORE_ADDR Class = lookup_objc_class (par_state->gdbarch (), | 
|  | copy.c_str ()); | 
|  | if (Class) | 
|  | { | 
|  | struct symbol *sym; | 
|  |  | 
|  | yylval.theclass.theclass = Class; | 
|  | sym = lookup_struct_typedef (copy.c_str (), | 
|  | par_state->expression_context_block, 1); | 
|  | if (sym) | 
|  | yylval.theclass.type = sym->type (); | 
|  | return CLASSNAME; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Input names that aren't symbols but ARE valid hex numbers, when | 
|  | the input radix permits them, can be names or numbers depending | 
|  | on the parse.  Note we support radixes > 16 here.  */ | 
|  | if (!bsym.symbol | 
|  | && ((copy[0] >= 'a' && copy[0] < 'a' + input_radix - 10) | 
|  | || (copy[0] >= 'A' && copy[0] < 'A' + input_radix - 10))) | 
|  | { | 
|  | YYSTYPE newlval;	/* Its value is ignored.  */ | 
|  | int hextype = parse_number (par_state, copy.c_str (), yylval.sval.length, | 
|  | 0, &newlval); | 
|  |  | 
|  | if (hextype == INT) | 
|  | { | 
|  | yylval.ssym.sym = bsym; | 
|  | yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL; | 
|  | return NAME_OR_INT; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Any other kind of symbol */ | 
|  | yylval.ssym.sym = bsym; | 
|  | yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL; | 
|  |  | 
|  | if (bsym.symbol == NULL | 
|  | && par_state->language ()->la_language == language_cplus | 
|  | && is_a_field_of_this.type == NULL | 
|  | && lookup_minimal_symbol (copy.c_str (), NULL, NULL).minsym == NULL) | 
|  | return UNKNOWN_CPP_NAME; | 
|  |  | 
|  | return NAME; | 
|  | } | 
|  |  | 
|  | /* Like classify_name, but used by the inner loop of the lexer, when a | 
|  | name might have already been seen.  CONTEXT is the context type, or | 
|  | NULL if this is the first component of a name.  */ | 
|  |  | 
|  | static int | 
|  | classify_inner_name (struct parser_state *par_state, | 
|  | const struct block *block, struct type *context) | 
|  | { | 
|  | struct type *type; | 
|  |  | 
|  | if (context == NULL) | 
|  | return classify_name (par_state, block, false, false); | 
|  |  | 
|  | type = check_typedef (context); | 
|  | if (!type_aggregate_p (type)) | 
|  | return ERROR; | 
|  |  | 
|  | std::string copy = copy_name (yylval.ssym.stoken); | 
|  | /* N.B. We assume the symbol can only be in VAR_DOMAIN.  */ | 
|  | yylval.ssym.sym = cp_lookup_nested_symbol (type, copy.c_str (), block, | 
|  | VAR_DOMAIN); | 
|  |  | 
|  | /* If no symbol was found, search for a matching base class named | 
|  | COPY.  This will allow users to enter qualified names of class members | 
|  | relative to the `this' pointer.  */ | 
|  | if (yylval.ssym.sym.symbol == NULL) | 
|  | { | 
|  | struct type *base_type = cp_find_type_baseclass_by_name (type, | 
|  | copy.c_str ()); | 
|  |  | 
|  | if (base_type != NULL) | 
|  | { | 
|  | yylval.tsym.type = base_type; | 
|  | return TYPENAME; | 
|  | } | 
|  |  | 
|  | return ERROR; | 
|  | } | 
|  |  | 
|  | switch (yylval.ssym.sym.symbol->aclass ()) | 
|  | { | 
|  | case LOC_BLOCK: | 
|  | case LOC_LABEL: | 
|  | /* cp_lookup_nested_symbol might have accidentally found a constructor | 
|  | named COPY when we really wanted a base class of the same name. | 
|  | Double-check this case by looking for a base class.  */ | 
|  | { | 
|  | struct type *base_type | 
|  | = cp_find_type_baseclass_by_name (type, copy.c_str ()); | 
|  |  | 
|  | if (base_type != NULL) | 
|  | { | 
|  | yylval.tsym.type = base_type; | 
|  | return TYPENAME; | 
|  | } | 
|  | } | 
|  | return ERROR; | 
|  |  | 
|  | case LOC_TYPEDEF: | 
|  | yylval.tsym.type = yylval.ssym.sym.symbol->type (); | 
|  | return TYPENAME; | 
|  |  | 
|  | default: | 
|  | return NAME; | 
|  | } | 
|  | internal_error (__FILE__, __LINE__, _("not reached")); | 
|  | } | 
|  |  | 
|  | /* The outer level of a two-level lexer.  This calls the inner lexer | 
|  | to return tokens.  It then either returns these tokens, or | 
|  | aggregates them into a larger token.  This lets us work around a | 
|  | problem in our parsing approach, where the parser could not | 
|  | distinguish between qualified names and qualified types at the | 
|  | right point. | 
|  |  | 
|  | This approach is still not ideal, because it mishandles template | 
|  | types.  See the comment in lex_one_token for an example.  However, | 
|  | this is still an improvement over the earlier approach, and will | 
|  | suffice until we move to better parsing technology.  */ | 
|  |  | 
|  | static int | 
|  | yylex (void) | 
|  | { | 
|  | token_and_value current; | 
|  | int first_was_coloncolon, last_was_coloncolon; | 
|  | struct type *context_type = NULL; | 
|  | int last_to_examine, next_to_examine, checkpoint; | 
|  | const struct block *search_block; | 
|  | bool is_quoted_name, last_lex_was_structop; | 
|  |  | 
|  | if (popping && !token_fifo.empty ()) | 
|  | goto do_pop; | 
|  | popping = 0; | 
|  |  | 
|  | last_lex_was_structop = last_was_structop; | 
|  |  | 
|  | /* Read the first token and decide what to do.  Most of the | 
|  | subsequent code is C++-only; but also depends on seeing a "::" or | 
|  | name-like token.  */ | 
|  | current.token = lex_one_token (pstate, &is_quoted_name); | 
|  | if (current.token == NAME) | 
|  | current.token = classify_name (pstate, pstate->expression_context_block, | 
|  | is_quoted_name, last_lex_was_structop); | 
|  | if (pstate->language ()->la_language != language_cplus | 
|  | || (current.token != TYPENAME && current.token != COLONCOLON | 
|  | && current.token != FILENAME)) | 
|  | return current.token; | 
|  |  | 
|  | /* Read any sequence of alternating "::" and name-like tokens into | 
|  | the token FIFO.  */ | 
|  | current.value = yylval; | 
|  | token_fifo.push_back (current); | 
|  | last_was_coloncolon = current.token == COLONCOLON; | 
|  | while (1) | 
|  | { | 
|  | bool ignore; | 
|  |  | 
|  | /* We ignore quoted names other than the very first one. | 
|  | Subsequent ones do not have any special meaning.  */ | 
|  | current.token = lex_one_token (pstate, &ignore); | 
|  | current.value = yylval; | 
|  | token_fifo.push_back (current); | 
|  |  | 
|  | if ((last_was_coloncolon && current.token != NAME) | 
|  | || (!last_was_coloncolon && current.token != COLONCOLON)) | 
|  | break; | 
|  | last_was_coloncolon = !last_was_coloncolon; | 
|  | } | 
|  | popping = 1; | 
|  |  | 
|  | /* We always read one extra token, so compute the number of tokens | 
|  | to examine accordingly.  */ | 
|  | last_to_examine = token_fifo.size () - 2; | 
|  | next_to_examine = 0; | 
|  |  | 
|  | current = token_fifo[next_to_examine]; | 
|  | ++next_to_examine; | 
|  |  | 
|  | name_obstack.clear (); | 
|  | checkpoint = 0; | 
|  | if (current.token == FILENAME) | 
|  | search_block = current.value.bval; | 
|  | else if (current.token == COLONCOLON) | 
|  | search_block = NULL; | 
|  | else | 
|  | { | 
|  | gdb_assert (current.token == TYPENAME); | 
|  | search_block = pstate->expression_context_block; | 
|  | obstack_grow (&name_obstack, current.value.sval.ptr, | 
|  | current.value.sval.length); | 
|  | context_type = current.value.tsym.type; | 
|  | checkpoint = 1; | 
|  | } | 
|  |  | 
|  | first_was_coloncolon = current.token == COLONCOLON; | 
|  | last_was_coloncolon = first_was_coloncolon; | 
|  |  | 
|  | while (next_to_examine <= last_to_examine) | 
|  | { | 
|  | token_and_value next; | 
|  |  | 
|  | next = token_fifo[next_to_examine]; | 
|  | ++next_to_examine; | 
|  |  | 
|  | if (next.token == NAME && last_was_coloncolon) | 
|  | { | 
|  | int classification; | 
|  |  | 
|  | yylval = next.value; | 
|  | classification = classify_inner_name (pstate, search_block, | 
|  | context_type); | 
|  | /* We keep going until we either run out of names, or until | 
|  | we have a qualified name which is not a type.  */ | 
|  | if (classification != TYPENAME && classification != NAME) | 
|  | break; | 
|  |  | 
|  | /* Accept up to this token.  */ | 
|  | checkpoint = next_to_examine; | 
|  |  | 
|  | /* Update the partial name we are constructing.  */ | 
|  | if (context_type != NULL) | 
|  | { | 
|  | /* We don't want to put a leading "::" into the name.  */ | 
|  | obstack_grow_str (&name_obstack, "::"); | 
|  | } | 
|  | obstack_grow (&name_obstack, next.value.sval.ptr, | 
|  | next.value.sval.length); | 
|  |  | 
|  | yylval.sval.ptr = (const char *) obstack_base (&name_obstack); | 
|  | yylval.sval.length = obstack_object_size (&name_obstack); | 
|  | current.value = yylval; | 
|  | current.token = classification; | 
|  |  | 
|  | last_was_coloncolon = 0; | 
|  |  | 
|  | if (classification == NAME) | 
|  | break; | 
|  |  | 
|  | context_type = yylval.tsym.type; | 
|  | } | 
|  | else if (next.token == COLONCOLON && !last_was_coloncolon) | 
|  | last_was_coloncolon = 1; | 
|  | else | 
|  | { | 
|  | /* We've reached the end of the name.  */ | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If we have a replacement token, install it as the first token in | 
|  | the FIFO, and delete the other constituent tokens.  */ | 
|  | if (checkpoint > 0) | 
|  | { | 
|  | current.value.sval.ptr | 
|  | = obstack_strndup (&cpstate->expansion_obstack, | 
|  | current.value.sval.ptr, | 
|  | current.value.sval.length); | 
|  |  | 
|  | token_fifo[0] = current; | 
|  | if (checkpoint > 1) | 
|  | token_fifo.erase (token_fifo.begin () + 1, | 
|  | token_fifo.begin () + checkpoint); | 
|  | } | 
|  |  | 
|  | do_pop: | 
|  | current = token_fifo[0]; | 
|  | token_fifo.erase (token_fifo.begin ()); | 
|  | yylval = current.value; | 
|  | return current.token; | 
|  | } | 
|  |  | 
|  | int | 
|  | c_parse (struct parser_state *par_state) | 
|  | { | 
|  | /* Setting up the parser state.  */ | 
|  | scoped_restore pstate_restore = make_scoped_restore (&pstate); | 
|  | gdb_assert (par_state != NULL); | 
|  | pstate = par_state; | 
|  |  | 
|  | c_parse_state cstate; | 
|  | scoped_restore cstate_restore = make_scoped_restore (&cpstate, &cstate); | 
|  |  | 
|  | gdb::unique_xmalloc_ptr<struct macro_scope> macro_scope; | 
|  |  | 
|  | if (par_state->expression_context_block) | 
|  | macro_scope | 
|  | = sal_macro_scope (find_pc_line (par_state->expression_context_pc, 0)); | 
|  | else | 
|  | macro_scope = default_macro_scope (); | 
|  | if (! macro_scope) | 
|  | macro_scope = user_macro_scope (); | 
|  |  | 
|  | scoped_restore restore_macro_scope | 
|  | = make_scoped_restore (&expression_macro_scope, macro_scope.get ()); | 
|  |  | 
|  | scoped_restore restore_yydebug = make_scoped_restore (&yydebug, | 
|  | parser_debug); | 
|  |  | 
|  | /* Initialize some state used by the lexer.  */ | 
|  | last_was_structop = false; | 
|  | saw_name_at_eof = 0; | 
|  | paren_depth = 0; | 
|  |  | 
|  | token_fifo.clear (); | 
|  | popping = 0; | 
|  | name_obstack.clear (); | 
|  |  | 
|  | int result = yyparse (); | 
|  | if (!result) | 
|  | pstate->set_operation (pstate->pop ()); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | #if defined(YYBISON) && YYBISON < 30800 | 
|  |  | 
|  |  | 
|  | /* This is called via the YYPRINT macro when parser debugging is | 
|  | enabled.  It prints a token's value.  */ | 
|  |  | 
|  | static void | 
|  | c_print_token (FILE *file, int type, YYSTYPE value) | 
|  | { | 
|  | switch (type) | 
|  | { | 
|  | case INT: | 
|  | parser_fprintf (file, "typed_val_int<%s, %s>", | 
|  | TYPE_SAFE_NAME (value.typed_val_int.type), | 
|  | pulongest (value.typed_val_int.val)); | 
|  | break; | 
|  |  | 
|  | case CHAR: | 
|  | case STRING: | 
|  | { | 
|  | char *copy = (char *) alloca (value.tsval.length + 1); | 
|  |  | 
|  | memcpy (copy, value.tsval.ptr, value.tsval.length); | 
|  | copy[value.tsval.length] = '\0'; | 
|  |  | 
|  | parser_fprintf (file, "tsval<type=%d, %s>", value.tsval.type, copy); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case NSSTRING: | 
|  | case DOLLAR_VARIABLE: | 
|  | parser_fprintf (file, "sval<%s>", copy_name (value.sval).c_str ()); | 
|  | break; | 
|  |  | 
|  | case TYPENAME: | 
|  | parser_fprintf (file, "tsym<type=%s, name=%s>", | 
|  | TYPE_SAFE_NAME (value.tsym.type), | 
|  | copy_name (value.tsym.stoken).c_str ()); | 
|  | break; | 
|  |  | 
|  | case NAME: | 
|  | case UNKNOWN_CPP_NAME: | 
|  | case NAME_OR_INT: | 
|  | case BLOCKNAME: | 
|  | parser_fprintf (file, "ssym<name=%s, sym=%s, field_of_this=%d>", | 
|  | copy_name (value.ssym.stoken).c_str (), | 
|  | (value.ssym.sym.symbol == NULL | 
|  | ? "(null)" : value.ssym.sym.symbol->print_name ()), | 
|  | value.ssym.is_a_field_of_this); | 
|  | break; | 
|  |  | 
|  | case FILENAME: | 
|  | parser_fprintf (file, "bval<%s>", host_address_to_string (value.bval)); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | static void | 
|  | yyerror (const char *msg) | 
|  | { | 
|  | if (pstate->prev_lexptr) | 
|  | pstate->lexptr = pstate->prev_lexptr; | 
|  |  | 
|  | error (_("A %s in expression, near `%s'."), msg, pstate->lexptr); | 
|  | } |