| /* YACC parser for C expressions, for GDB. |
| Copyright (C) 1986-2024 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 <ctype.h> |
| #include "expression.h" |
| #include "value.h" |
| #include "parser-defs.h" |
| #include "language.h" |
| #include "c-lang.h" |
| #include "c-support.h" |
| #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> |
| ($1.type->target_type (), 0)); |
| operation_up imag |
| = (make_operation<long_const_operation> |
| ($1.type->target_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 = $1.type->target_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 ()); |
| |
| pstate->push_new<long_const_operation> |
| (int_type, type->length ()); |
| } |
| ; |
| |
| 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 ("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, |
| SEARCH_FUNCTION_DOMAIN, |
| nullptr).symbol; |
| |
| if (tem == nullptr) |
| 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, |
| SEARCH_VFT, 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, |
| SEARCH_VFT, 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_minimal_symbol (current_program_space, arg.c_str ()); |
| if (msymbol.minsym == NULL) |
| { |
| if (!have_full_symbols (current_program_space) |
| && !have_partial_symbols (current_program_space)) |
| 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, |
| SEARCH_VFT, |
| &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; |
| |
| if (parsed_float) |
| { |
| if (len >= 1 && buf[len - 1] == 'i') |
| { |
| imaginary_p = true; |
| --len; |
| } |
| |
| /* Handle suffixes for decimal floating-point: "df", "dd" or "dl". */ |
| if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'f') |
| { |
| putithere->typed_val_float.type |
| = parse_type (par_state)->builtin_decfloat; |
| len -= 2; |
| } |
| else if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'd') |
| { |
| putithere->typed_val_float.type |
| = parse_type (par_state)->builtin_decdouble; |
| len -= 2; |
| } |
| else if (len >= 2 && buf[len - 2] == 'd' && buf[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 (buf[len - 1]) == 'f') |
| { |
| putithere->typed_val_float.type |
| = parse_type (par_state)->builtin_float; |
| len -= 1; |
| } |
| else if (len >= 1 && TOLOWER (buf[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 (buf, 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 (buf[0] == '0' && len > 1) |
| switch (buf[1]) |
| { |
| case 'x': |
| case 'X': |
| if (len >= 3) |
| { |
| buf += 2; |
| base = 16; |
| len -= 2; |
| } |
| break; |
| |
| case 'b': |
| case 'B': |
| if (len >= 3) |
| { |
| buf += 2; |
| base = 2; |
| len -= 2; |
| } |
| break; |
| |
| case 't': |
| case 'T': |
| case 'd': |
| case 'D': |
| if (len >= 3) |
| { |
| buf += 2; |
| base = 10; |
| len -= 2; |
| } |
| break; |
| |
| default: |
| base = 8; |
| break; |
| } |
| |
| while (len-- > 0) |
| { |
| c = *buf++; |
| 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 c_token |
| { |
| const char *oper; |
| int token; |
| enum exp_opcode opcode; |
| token_flags flags; |
| }; |
| |
| static const struct c_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 c_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 c_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. */ |
| } |
| [[fallthrough]]; |
| |
| 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; |
| } |
| |
| /* If the token includes the C++14 digits separator, we make a |
| copy so that we don't have to handle the separator in |
| parse_number. */ |
| std::optional<std::string> no_tick; |
| 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. */ |
| } |
| else if (*p == '\'') |
| { |
| if (!no_tick.has_value ()) |
| no_tick.emplace (tokstart, p); |
| 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; |
| if (no_tick.has_value ()) |
| no_tick->push_back (*p); |
| } |
| if (no_tick.has_value ()) |
| toktype = parse_number (par_state, no_tick->c_str (), |
| no_tick->length (), |
| got_dot | got_e | got_p, &yylval); |
| else |
| toktype = parse_number (par_state, tokstart, p - tokstart, |
| got_dot | got_e | got_p, &yylval); |
| if (toktype == ERROR) |
| error (_("Invalid number \"%.*s\"."), (int) (p - tokstart), |
| tokstart); |
| 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; |
| } |
| } |
| [[fallthrough]]; |
| 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; |
| [[fallthrough]]; |
| 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, |
| SEARCH_VFT, |
| (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 c_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<c_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, SEARCH_VFT, |
| 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, SEARCH_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 |
| |