| /* YACC parser for D expressions, for GDB. |
| |
| Copyright (C) 2014-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/>. */ |
| |
| /* This file is derived from c-exp.y, jv-exp.y. */ |
| |
| /* Parse a D 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 "d-lang.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 "type-stack.h" |
| #include "expop.h" |
| |
| #define parse_type(ps) builtin_type (ps->gdbarch ()) |
| #define parse_d_type(ps) builtin_d_type (ps->gdbarch ()) |
| |
| /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, |
| etc). */ |
| #define GDB_YY_REMAP_PREFIX d_ |
| #include "yy-remap.h" |
| |
| /* The state of the parser, used internally when we are parsing the |
| expression. */ |
| |
| static struct parser_state *pstate = NULL; |
| |
| /* The current type stack. */ |
| static struct type_stack *type_stack; |
| |
| 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 |
| { |
| struct { |
| LONGEST val; |
| struct type *type; |
| } typed_val_int; |
| struct { |
| gdb_byte val[16]; |
| struct type *type; |
| } typed_val_float; |
| struct symbol *sym; |
| struct type *tval; |
| struct typed_stoken tsval; |
| struct stoken sval; |
| struct ttype tsym; |
| struct symtoken ssym; |
| int ival; |
| int voidval; |
| enum exp_opcode opcode; |
| struct stoken_vector svec; |
| } |
| |
| %{ |
| /* YYSTYPE gets defined by %union */ |
| static int parse_number (struct parser_state *, const char *, |
| int, int, YYSTYPE *); |
| %} |
| |
| %token <sval> IDENTIFIER UNKNOWN_NAME |
| %token <tsym> TYPENAME |
| %token <voidval> COMPLETE |
| |
| /* 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 <sval> NAME_OR_INT |
| |
| %token <typed_val_int> INTEGER_LITERAL |
| %token <typed_val_float> FLOAT_LITERAL |
| %token <tsval> CHARACTER_LITERAL |
| %token <tsval> STRING_LITERAL |
| |
| %type <svec> StringExp |
| %type <tval> BasicType TypeExp |
| %type <sval> IdentifierExp |
| %type <ival> ArrayLiteral |
| |
| %token ENTRY |
| %token ERROR |
| |
| /* Keywords that have a constant value. */ |
| %token TRUE_KEYWORD FALSE_KEYWORD NULL_KEYWORD |
| /* Class 'super' accessor. */ |
| %token SUPER_KEYWORD |
| /* Properties. */ |
| %token CAST_KEYWORD SIZEOF_KEYWORD |
| %token TYPEOF_KEYWORD TYPEID_KEYWORD |
| %token INIT_KEYWORD |
| /* Comparison keywords. */ |
| /* Type storage classes. */ |
| %token IMMUTABLE_KEYWORD CONST_KEYWORD SHARED_KEYWORD |
| /* Non-scalar type keywords. */ |
| %token STRUCT_KEYWORD UNION_KEYWORD |
| %token CLASS_KEYWORD INTERFACE_KEYWORD |
| %token ENUM_KEYWORD TEMPLATE_KEYWORD |
| %token DELEGATE_KEYWORD FUNCTION_KEYWORD |
| |
| %token <sval> DOLLAR_VARIABLE |
| |
| %token <opcode> ASSIGN_MODIFY |
| |
| %left ',' |
| %right '=' ASSIGN_MODIFY |
| %right '?' |
| %left OROR |
| %left ANDAND |
| %left '|' |
| %left '^' |
| %left '&' |
| %left EQUAL NOTEQUAL '<' '>' LEQ GEQ |
| %right LSH RSH |
| %left '+' '-' |
| %left '*' '/' '%' |
| %right HATHAT |
| %left IDENTITY NOTIDENTITY |
| %right INCREMENT DECREMENT |
| %right '.' '[' '(' |
| %token DOTDOT |
| |
| |
| %% |
| |
| start : |
| Expression |
| | TypeExp |
| ; |
| |
| /* Expressions, including the comma operator. */ |
| |
| Expression: |
| CommaExpression |
| ; |
| |
| CommaExpression: |
| AssignExpression |
| | AssignExpression ',' CommaExpression |
| { pstate->wrap2<comma_operation> (); } |
| ; |
| |
| AssignExpression: |
| ConditionalExpression |
| | ConditionalExpression '=' AssignExpression |
| { pstate->wrap2<assign_operation> (); } |
| | ConditionalExpression ASSIGN_MODIFY AssignExpression |
| { |
| operation_up rhs = pstate->pop (); |
| operation_up lhs = pstate->pop (); |
| pstate->push_new<assign_modify_operation> |
| ($2, std::move (lhs), std::move (rhs)); |
| } |
| ; |
| |
| ConditionalExpression: |
| OrOrExpression |
| | OrOrExpression '?' Expression ':' ConditionalExpression |
| { |
| operation_up last = pstate->pop (); |
| operation_up mid = pstate->pop (); |
| operation_up first = pstate->pop (); |
| pstate->push_new<ternop_cond_operation> |
| (std::move (first), std::move (mid), |
| std::move (last)); |
| } |
| ; |
| |
| OrOrExpression: |
| AndAndExpression |
| | OrOrExpression OROR AndAndExpression |
| { pstate->wrap2<logical_or_operation> (); } |
| ; |
| |
| AndAndExpression: |
| OrExpression |
| | AndAndExpression ANDAND OrExpression |
| { pstate->wrap2<logical_and_operation> (); } |
| ; |
| |
| OrExpression: |
| XorExpression |
| | OrExpression '|' XorExpression |
| { pstate->wrap2<bitwise_ior_operation> (); } |
| ; |
| |
| XorExpression: |
| AndExpression |
| | XorExpression '^' AndExpression |
| { pstate->wrap2<bitwise_xor_operation> (); } |
| ; |
| |
| AndExpression: |
| CmpExpression |
| | AndExpression '&' CmpExpression |
| { pstate->wrap2<bitwise_and_operation> (); } |
| ; |
| |
| CmpExpression: |
| ShiftExpression |
| | EqualExpression |
| | IdentityExpression |
| | RelExpression |
| ; |
| |
| EqualExpression: |
| ShiftExpression EQUAL ShiftExpression |
| { pstate->wrap2<equal_operation> (); } |
| | ShiftExpression NOTEQUAL ShiftExpression |
| { pstate->wrap2<notequal_operation> (); } |
| ; |
| |
| IdentityExpression: |
| ShiftExpression IDENTITY ShiftExpression |
| { pstate->wrap2<equal_operation> (); } |
| | ShiftExpression NOTIDENTITY ShiftExpression |
| { pstate->wrap2<notequal_operation> (); } |
| ; |
| |
| RelExpression: |
| ShiftExpression '<' ShiftExpression |
| { pstate->wrap2<less_operation> (); } |
| | ShiftExpression LEQ ShiftExpression |
| { pstate->wrap2<leq_operation> (); } |
| | ShiftExpression '>' ShiftExpression |
| { pstate->wrap2<gtr_operation> (); } |
| | ShiftExpression GEQ ShiftExpression |
| { pstate->wrap2<geq_operation> (); } |
| ; |
| |
| ShiftExpression: |
| AddExpression |
| | ShiftExpression LSH AddExpression |
| { pstate->wrap2<lsh_operation> (); } |
| | ShiftExpression RSH AddExpression |
| { pstate->wrap2<rsh_operation> (); } |
| ; |
| |
| AddExpression: |
| MulExpression |
| | AddExpression '+' MulExpression |
| { pstate->wrap2<add_operation> (); } |
| | AddExpression '-' MulExpression |
| { pstate->wrap2<sub_operation> (); } |
| | AddExpression '~' MulExpression |
| { pstate->wrap2<concat_operation> (); } |
| ; |
| |
| MulExpression: |
| UnaryExpression |
| | MulExpression '*' UnaryExpression |
| { pstate->wrap2<mul_operation> (); } |
| | MulExpression '/' UnaryExpression |
| { pstate->wrap2<div_operation> (); } |
| | MulExpression '%' UnaryExpression |
| { pstate->wrap2<rem_operation> (); } |
| |
| UnaryExpression: |
| '&' UnaryExpression |
| { pstate->wrap<unop_addr_operation> (); } |
| | INCREMENT UnaryExpression |
| { pstate->wrap<preinc_operation> (); } |
| | DECREMENT UnaryExpression |
| { pstate->wrap<predec_operation> (); } |
| | '*' UnaryExpression |
| { pstate->wrap<unop_ind_operation> (); } |
| | '-' UnaryExpression |
| { pstate->wrap<unary_neg_operation> (); } |
| | '+' UnaryExpression |
| { pstate->wrap<unary_plus_operation> (); } |
| | '!' UnaryExpression |
| { pstate->wrap<unary_logical_not_operation> (); } |
| | '~' UnaryExpression |
| { pstate->wrap<unary_complement_operation> (); } |
| | TypeExp '.' SIZEOF_KEYWORD |
| { pstate->wrap<unop_sizeof_operation> (); } |
| | CastExpression |
| | PowExpression |
| ; |
| |
| CastExpression: |
| CAST_KEYWORD '(' TypeExp ')' UnaryExpression |
| { pstate->wrap2<unop_cast_type_operation> (); } |
| /* C style cast is illegal D, but is still recognised in |
| the grammar, so we keep this around for convenience. */ |
| | '(' TypeExp ')' UnaryExpression |
| { pstate->wrap2<unop_cast_type_operation> (); } |
| ; |
| |
| PowExpression: |
| PostfixExpression |
| | PostfixExpression HATHAT UnaryExpression |
| { pstate->wrap2<exp_operation> (); } |
| ; |
| |
| PostfixExpression: |
| PrimaryExpression |
| | PostfixExpression '.' COMPLETE |
| { |
| structop_base_operation *op |
| = new structop_ptr_operation (pstate->pop (), ""); |
| pstate->mark_struct_expression (op); |
| pstate->push (operation_up (op)); |
| } |
| | PostfixExpression '.' IDENTIFIER |
| { |
| pstate->push_new<structop_operation> |
| (pstate->pop (), copy_name ($3)); |
| } |
| | PostfixExpression '.' IDENTIFIER COMPLETE |
| { |
| structop_base_operation *op |
| = new structop_operation (pstate->pop (), copy_name ($3)); |
| pstate->mark_struct_expression (op); |
| pstate->push (operation_up (op)); |
| } |
| | PostfixExpression '.' SIZEOF_KEYWORD |
| { pstate->wrap<unop_sizeof_operation> (); } |
| | PostfixExpression INCREMENT |
| { pstate->wrap<postinc_operation> (); } |
| | PostfixExpression DECREMENT |
| { pstate->wrap<postdec_operation> (); } |
| | CallExpression |
| | IndexExpression |
| | SliceExpression |
| ; |
| |
| ArgumentList: |
| AssignExpression |
| { pstate->arglist_len = 1; } |
| | ArgumentList ',' AssignExpression |
| { pstate->arglist_len++; } |
| ; |
| |
| ArgumentList_opt: |
| /* EMPTY */ |
| { pstate->arglist_len = 0; } |
| | ArgumentList |
| ; |
| |
| CallExpression: |
| PostfixExpression '(' |
| { pstate->start_arglist (); } |
| ArgumentList_opt ')' |
| { |
| std::vector<operation_up> args |
| = pstate->pop_vector (pstate->end_arglist ()); |
| pstate->push_new<funcall_operation> |
| (pstate->pop (), std::move (args)); |
| } |
| ; |
| |
| IndexExpression: |
| PostfixExpression '[' ArgumentList ']' |
| { if (pstate->arglist_len > 0) |
| { |
| std::vector<operation_up> args |
| = pstate->pop_vector (pstate->arglist_len); |
| pstate->push_new<multi_subscript_operation> |
| (pstate->pop (), std::move (args)); |
| } |
| else |
| pstate->wrap2<subscript_operation> (); |
| } |
| ; |
| |
| SliceExpression: |
| PostfixExpression '[' ']' |
| { /* Do nothing. */ } |
| | PostfixExpression '[' AssignExpression DOTDOT AssignExpression ']' |
| { |
| operation_up last = pstate->pop (); |
| operation_up mid = pstate->pop (); |
| operation_up first = pstate->pop (); |
| pstate->push_new<ternop_slice_operation> |
| (std::move (first), std::move (mid), |
| std::move (last)); |
| } |
| ; |
| |
| PrimaryExpression: |
| '(' Expression ')' |
| { /* Do nothing. */ } |
| | IdentifierExp |
| { struct bound_minimal_symbol msymbol; |
| std::string copy = copy_name ($1); |
| struct field_of_this_result is_a_field_of_this; |
| struct block_symbol sym; |
| |
| /* Handle VAR, which could be local or global. */ |
| sym = lookup_symbol (copy.c_str (), |
| pstate->expression_context_block, |
| VAR_DOMAIN, &is_a_field_of_this); |
| if (sym.symbol && sym.symbol->aclass () != LOC_TYPEDEF) |
| { |
| if (symbol_read_needs_frame (sym.symbol)) |
| pstate->block_tracker->update (sym); |
| pstate->push_new<var_value_operation> (sym); |
| } |
| else if (is_a_field_of_this.type != NULL) |
| { |
| /* 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), std::move (copy)); |
| } |
| else |
| { |
| /* Lookup foreign name in global static symbols. */ |
| msymbol = lookup_bound_minimal_symbol (copy.c_str ()); |
| if (msymbol.minsym != NULL) |
| pstate->push_new<var_msym_value_operation> (msymbol); |
| else 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."), |
| copy.c_str ()); |
| } |
| } |
| | TypeExp '.' IdentifierExp |
| { struct type *type = check_typedef ($1); |
| |
| /* Check if the qualified name is in the global |
| context. However if the symbol has not already |
| been resolved, it's not likely to be found. */ |
| if (type->code () == TYPE_CODE_MODULE) |
| { |
| struct block_symbol sym; |
| const char *type_name = TYPE_SAFE_NAME (type); |
| int type_name_len = strlen (type_name); |
| std::string name |
| = string_printf ("%.*s.%.*s", |
| type_name_len, type_name, |
| $3.length, $3.ptr); |
| |
| sym = |
| lookup_symbol (name.c_str (), |
| (const struct block *) NULL, |
| VAR_DOMAIN, NULL); |
| pstate->push_symbol (name.c_str (), sym); |
| } |
| else |
| { |
| /* Check if the qualified name resolves as a member |
| of an aggregate or an enum 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)); |
| } |
| } |
| | DOLLAR_VARIABLE |
| { pstate->push_dollar ($1); } |
| | NAME_OR_INT |
| { YYSTYPE val; |
| parse_number (pstate, $1.ptr, $1.length, 0, &val); |
| pstate->push_new<long_const_operation> |
| (val.typed_val_int.type, val.typed_val_int.val); } |
| | NULL_KEYWORD |
| { struct type *type = parse_d_type (pstate)->builtin_void; |
| type = lookup_pointer_type (type); |
| pstate->push_new<long_const_operation> (type, 0); } |
| | TRUE_KEYWORD |
| { pstate->push_new<bool_operation> (true); } |
| | FALSE_KEYWORD |
| { pstate->push_new<bool_operation> (false); } |
| | INTEGER_LITERAL |
| { pstate->push_new<long_const_operation> ($1.type, $1.val); } |
| | FLOAT_LITERAL |
| { |
| 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); |
| } |
| | CHARACTER_LITERAL |
| { struct stoken_vector vec; |
| vec.len = 1; |
| vec.tokens = &$1; |
| pstate->push_c_string (0, &vec); } |
| | StringExp |
| { int i; |
| pstate->push_c_string (0, &$1); |
| for (i = 0; i < $1.len; ++i) |
| free ($1.tokens[i].ptr); |
| free ($1.tokens); } |
| | ArrayLiteral |
| { |
| std::vector<operation_up> args |
| = pstate->pop_vector ($1); |
| pstate->push_new<array_operation> |
| (0, $1 - 1, std::move (args)); |
| } |
| | TYPEOF_KEYWORD '(' Expression ')' |
| { pstate->wrap<typeof_operation> (); } |
| ; |
| |
| ArrayLiteral: |
| '[' ArgumentList_opt ']' |
| { $$ = pstate->arglist_len; } |
| ; |
| |
| IdentifierExp: |
| IDENTIFIER |
| ; |
| |
| StringExp: |
| STRING_LITERAL |
| { /* 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); |
| } |
| | StringExp STRING_LITERAL |
| { /* 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; |
| } |
| ; |
| |
| TypeExp: |
| '(' TypeExp ')' |
| { /* Do nothing. */ } |
| | BasicType |
| { pstate->push_new<type_operation> ($1); } |
| | BasicType BasicType2 |
| { $$ = type_stack->follow_types ($1); |
| pstate->push_new<type_operation> ($$); |
| } |
| ; |
| |
| BasicType2: |
| '*' |
| { type_stack->push (tp_pointer); } |
| | '*' BasicType2 |
| { type_stack->push (tp_pointer); } |
| | '[' INTEGER_LITERAL ']' |
| { type_stack->push ($2.val); |
| type_stack->push (tp_array); } |
| | '[' INTEGER_LITERAL ']' BasicType2 |
| { type_stack->push ($2.val); |
| type_stack->push (tp_array); } |
| ; |
| |
| BasicType: |
| TYPENAME |
| { $$ = $1.type; } |
| ; |
| |
| %% |
| |
| /* 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_MODULE |
| || (type->code () == TYPE_CODE_ENUM |
| && type->is_declared_class ())); |
| } |
| |
| /* 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 *ps, const char *p, |
| int len, int parsed_float, YYSTYPE *putithere) |
| { |
| ULONGEST n = 0; |
| ULONGEST prevn = 0; |
| ULONGEST un; |
| |
| int i = 0; |
| int c; |
| int base = input_radix; |
| int unsigned_p = 0; |
| int long_p = 0; |
| |
| /* We have found a "L" or "U" suffix. */ |
| int found_suffix = 0; |
| |
| ULONGEST high_bit; |
| struct type *signed_type; |
| struct type *unsigned_type; |
| |
| if (parsed_float) |
| { |
| char *s, *sp; |
| |
| /* Strip out all embedded '_' before passing to parse_float. */ |
| s = (char *) alloca (len + 1); |
| sp = s; |
| while (len-- > 0) |
| { |
| if (*p != '_') |
| *sp++ = *p; |
| p++; |
| } |
| *sp = '\0'; |
| len = strlen (s); |
| |
| /* Check suffix for `i' , `fi' or `li' (idouble, ifloat or ireal). */ |
| if (len >= 1 && tolower (s[len - 1]) == 'i') |
| { |
| if (len >= 2 && tolower (s[len - 2]) == 'f') |
| { |
| putithere->typed_val_float.type |
| = parse_d_type (ps)->builtin_ifloat; |
| len -= 2; |
| } |
| else if (len >= 2 && tolower (s[len - 2]) == 'l') |
| { |
| putithere->typed_val_float.type |
| = parse_d_type (ps)->builtin_ireal; |
| len -= 2; |
| } |
| else |
| { |
| putithere->typed_val_float.type |
| = parse_d_type (ps)->builtin_idouble; |
| len -= 1; |
| } |
| } |
| /* Check suffix for `f' or `l'' (float or real). */ |
| else if (len >= 1 && tolower (s[len - 1]) == 'f') |
| { |
| putithere->typed_val_float.type |
| = parse_d_type (ps)->builtin_float; |
| len -= 1; |
| } |
| else if (len >= 1 && tolower (s[len - 1]) == 'l') |
| { |
| putithere->typed_val_float.type |
| = parse_d_type (ps)->builtin_real; |
| len -= 1; |
| } |
| /* Default type if no suffix. */ |
| else |
| { |
| putithere->typed_val_float.type |
| = parse_d_type (ps)->builtin_double; |
| } |
| |
| if (!parse_float (s, len, |
| putithere->typed_val_float.type, |
| putithere->typed_val_float.val)) |
| return ERROR; |
| |
| return FLOAT_LITERAL; |
| } |
| |
| /* Handle base-switching prefixes 0x, 0b, 0 */ |
| if (p[0] == '0') |
| 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; |
| |
| default: |
| base = 8; |
| break; |
| } |
| |
| while (len-- > 0) |
| { |
| c = *p++; |
| if (c == '_') |
| continue; /* Ignore embedded '_'. */ |
| if (c >= 'A' && c <= 'Z') |
| c += 'a' - 'A'; |
| if (c != 'l' && c != 'u') |
| 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 == 0) |
| { |
| long_p = 1; |
| found_suffix = 1; |
| } |
| else if (c == 'u' && unsigned_p == 0) |
| { |
| unsigned_p = 1; |
| found_suffix = 1; |
| } |
| else |
| return ERROR; /* Char not a digit */ |
| } |
| if (i >= base) |
| return ERROR; /* Invalid digit in this base. */ |
| /* Portably test for integer overflow. */ |
| if (c != 'l' && c != 'u') |
| { |
| ULONGEST n2 = prevn * base; |
| if ((n2 / base != prevn) || (n2 + i < prevn)) |
| error (_("Numeric constant too large.")); |
| } |
| prevn = n; |
| } |
| |
| /* An integer constant is an int or a long. An L suffix forces it to |
| be long, and a U suffix forces it to be unsigned. 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). To deal with the case |
| where it is we just always shift the value more than once, with fewer |
| bits each time. */ |
| un = (ULONGEST) n >> 2; |
| if (long_p == 0 && (un >> 30) == 0) |
| { |
| high_bit = ((ULONGEST) 1) << 31; |
| signed_type = parse_d_type (ps)->builtin_int; |
| /* For decimal notation, keep the sign of the worked out type. */ |
| if (base == 10 && !unsigned_p) |
| unsigned_type = parse_d_type (ps)->builtin_long; |
| else |
| unsigned_type = parse_d_type (ps)->builtin_uint; |
| } |
| else |
| { |
| int shift; |
| if (sizeof (ULONGEST) * HOST_CHAR_BIT < 64) |
| /* A long long does not fit in a LONGEST. */ |
| shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1); |
| else |
| shift = 63; |
| high_bit = (ULONGEST) 1 << shift; |
| signed_type = parse_d_type (ps)->builtin_long; |
| unsigned_type = parse_d_type (ps)->builtin_ulong; |
| } |
| |
| putithere->typed_val_int.val = n; |
| |
| /* If the high bit of the worked out type is set then this number |
| has to be unsigned_type. */ |
| if (unsigned_p || (n & high_bit)) |
| putithere->typed_val_int.type = unsigned_type; |
| else |
| putithere->typed_val_int.type = signed_type; |
| |
| return INTEGER_LITERAL; |
| } |
| |
| /* Temporary obstack used for holding strings. */ |
| static struct obstack tempbuf; |
| static int tempbuf_init; |
| |
| /* 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; |
| |
| /* 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); |
| |
| /* Skip the quote. */ |
| quote = *tokptr; |
| ++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 == '"' || quote == '`') |
| error (_("Unterminated string in expression.")); |
| else |
| error (_("Unmatched single quote.")); |
| } |
| ++tokptr; |
| |
| /* FIXME: should instead use own language string_type enum |
| and handle D-specific string suffixes here. */ |
| if (quote == '\'') |
| value->type = C_CHAR; |
| else |
| value->type = C_STRING; |
| |
| value->ptr = (char *) obstack_base (&tempbuf); |
| value->length = obstack_object_size (&tempbuf); |
| |
| *outptr = tokptr; |
| |
| return quote == '\'' ? CHARACTER_LITERAL : STRING_LITERAL; |
| } |
| |
| struct token |
| { |
| const char *oper; |
| int token; |
| enum exp_opcode opcode; |
| }; |
| |
| static const struct token tokentab3[] = |
| { |
| {"^^=", ASSIGN_MODIFY, BINOP_EXP}, |
| {"<<=", ASSIGN_MODIFY, BINOP_LSH}, |
| {">>=", ASSIGN_MODIFY, BINOP_RSH}, |
| }; |
| |
| static const struct token tokentab2[] = |
| { |
| {"+=", ASSIGN_MODIFY, BINOP_ADD}, |
| {"-=", ASSIGN_MODIFY, BINOP_SUB}, |
| {"*=", ASSIGN_MODIFY, BINOP_MUL}, |
| {"/=", ASSIGN_MODIFY, BINOP_DIV}, |
| {"%=", ASSIGN_MODIFY, BINOP_REM}, |
| {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR}, |
| {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND}, |
| {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR}, |
| {"++", INCREMENT, OP_NULL}, |
| {"--", DECREMENT, OP_NULL}, |
| {"&&", ANDAND, OP_NULL}, |
| {"||", OROR, OP_NULL}, |
| {"^^", HATHAT, OP_NULL}, |
| {"<<", LSH, OP_NULL}, |
| {">>", RSH, OP_NULL}, |
| {"==", EQUAL, OP_NULL}, |
| {"!=", NOTEQUAL, OP_NULL}, |
| {"<=", LEQ, OP_NULL}, |
| {">=", GEQ, OP_NULL}, |
| {"..", DOTDOT, OP_NULL}, |
| }; |
| |
| /* Identifier-like tokens. */ |
| static const struct token ident_tokens[] = |
| { |
| {"is", IDENTITY, OP_NULL}, |
| {"!is", NOTIDENTITY, OP_NULL}, |
| |
| {"cast", CAST_KEYWORD, OP_NULL}, |
| {"const", CONST_KEYWORD, OP_NULL}, |
| {"immutable", IMMUTABLE_KEYWORD, OP_NULL}, |
| {"shared", SHARED_KEYWORD, OP_NULL}, |
| {"super", SUPER_KEYWORD, OP_NULL}, |
| |
| {"null", NULL_KEYWORD, OP_NULL}, |
| {"true", TRUE_KEYWORD, OP_NULL}, |
| {"false", FALSE_KEYWORD, OP_NULL}, |
| |
| {"init", INIT_KEYWORD, OP_NULL}, |
| {"sizeof", SIZEOF_KEYWORD, OP_NULL}, |
| {"typeof", TYPEOF_KEYWORD, OP_NULL}, |
| {"typeid", TYPEID_KEYWORD, OP_NULL}, |
| |
| {"delegate", DELEGATE_KEYWORD, OP_NULL}, |
| {"function", FUNCTION_KEYWORD, OP_NULL}, |
| {"struct", STRUCT_KEYWORD, OP_NULL}, |
| {"union", UNION_KEYWORD, OP_NULL}, |
| {"class", CLASS_KEYWORD, OP_NULL}, |
| {"interface", INTERFACE_KEYWORD, OP_NULL}, |
| {"enum", ENUM_KEYWORD, OP_NULL}, |
| {"template", TEMPLATE_KEYWORD, OP_NULL}, |
| }; |
| |
| /* 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. |
| This is used only when parsing to do field name completion. */ |
| static int 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) |
| { |
| int c; |
| int namelen; |
| const char *tokstart; |
| int saw_structop = last_was_structop; |
| |
| last_was_structop = 0; |
| |
| retry: |
| |
| 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) |
| { |
| 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) |
| { |
| pstate->lexptr += 2; |
| yylval.opcode = token.opcode; |
| return token.token; |
| } |
| |
| switch (c = *tokstart) |
| { |
| case 0: |
| /* 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 (saw_name_at_eof) |
| { |
| saw_name_at_eof = 0; |
| return COMPLETE; |
| } |
| else if (saw_structop) |
| return COMPLETE; |
| else |
| return 0; |
| |
| case ' ': |
| case '\t': |
| case '\n': |
| pstate->lexptr++; |
| goto retry; |
| |
| case '[': |
| case '(': |
| paren_depth++; |
| pstate->lexptr++; |
| return c; |
| |
| case ']': |
| case ')': |
| if (paren_depth == 0) |
| return 0; |
| paren_depth--; |
| pstate->lexptr++; |
| return c; |
| |
| case ',': |
| if (pstate->comma_terminates && paren_depth == 0) |
| return 0; |
| pstate->lexptr++; |
| return c; |
| |
| case '.': |
| /* Might be a floating point number. */ |
| if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9') |
| { |
| if (pstate->parse_completion) |
| last_was_structop = 1; |
| 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, toktype; |
| const char *p = tokstart; |
| int hex = input_radix > 10; |
| |
| if (c == '0' && (p[1] == 'x' || p[1] == 'X')) |
| { |
| p += 2; |
| hex = 1; |
| } |
| |
| for (;; ++p) |
| { |
| /* Hex exponents start with 'p', 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 && tolower (p[0]) == 'e') |
| || (hex && !got_e && tolower (p[0] == 'p'))) |
| got_dot = got_e = 1; |
| /* A '.' always indicates a decimal floating point number |
| regardless of the radix. If we have a '..' then its the |
| end of the number and the beginning of a slice. */ |
| else if (!got_dot && (p[0] == '.' && p[1] != '.')) |
| got_dot = 1; |
| /* This is the sign of the exponent, not the end of the number. */ |
| else if (got_e && (tolower (p[-1]) == 'e' || tolower (p[-1]) == 'p') |
| && (*p == '-' || *p == '+')) |
| continue; |
| /* We will take any letters or digits, ignoring any embedded '_'. |
| parse_number will complain if past the radix, or if L or U are |
| not final. */ |
| else if ((*p < '0' || *p > '9') && (*p != '_') |
| && ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z'))) |
| break; |
| } |
| |
| toktype = parse_number (par_state, tokstart, p - tokstart, |
| got_dot|got_e, &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]; |
| size_t len = strlen ("entry"); |
| |
| while (isspace (*p)) |
| p++; |
| if (strncmp (p, "entry", len) == 0 && !isalnum (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 '\'': |
| case '"': |
| case '`': |
| { |
| int host_len; |
| int result = parse_string_or_char (tokstart, &pstate->lexptr, |
| &yylval.tsval, &host_len); |
| if (result == CHARACTER_LITERAL) |
| { |
| if (host_len == 0) |
| error (_("Empty character constant.")); |
| else if (host_len > 2 && c == '\'') |
| { |
| ++tokstart; |
| namelen = pstate->lexptr - tokstart - 1; |
| goto tryname; |
| } |
| else if (host_len > 1) |
| error (_("Invalid character constant.")); |
| } |
| return result; |
| } |
| } |
| |
| if (!(c == '_' || c == '$' |
| || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) |
| /* 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 >= '0' && c <= '9') |
| || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));) |
| c = tokstart[++namelen]; |
| |
| /* The token "if" terminates the expression and is NOT |
| removed from the input stream. */ |
| if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f') |
| 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')) |
| { |
| 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) |
| { |
| /* 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; |
| |
| yylval.tsym.type |
| = language_lookup_primitive_type (par_state->language (), |
| par_state->gdbarch (), copy.c_str ()); |
| if (yylval.tsym.type != NULL) |
| return TYPENAME; |
| |
| /* 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 ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10) |
| || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)) |
| { |
| YYSTYPE newlval; /* Its value is ignored. */ |
| int hextype = parse_number (par_state, tokstart, namelen, 0, &newlval); |
| if (hextype == INTEGER_LITERAL) |
| return NAME_OR_INT; |
| } |
| |
| if (pstate->parse_completion && *pstate->lexptr == '\0') |
| saw_name_at_eof = 1; |
| |
| return IDENTIFIER; |
| } |
| |
| /* 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 yylex; this holds symbol names as they are |
| built up. */ |
| static auto_obstack name_obstack; |
| |
| /* Classify an IDENTIFIER 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. */ |
| |
| static int |
| classify_name (struct parser_state *par_state, const struct block *block) |
| { |
| struct block_symbol sym; |
| struct field_of_this_result is_a_field_of_this; |
| |
| std::string copy = copy_name (yylval.sval); |
| |
| sym = lookup_symbol (copy.c_str (), block, VAR_DOMAIN, &is_a_field_of_this); |
| if (sym.symbol && sym.symbol->aclass () == LOC_TYPEDEF) |
| { |
| yylval.tsym.type = sym.symbol->type (); |
| return TYPENAME; |
| } |
| else if (sym.symbol == NULL) |
| { |
| /* Look-up first for a module name, then a type. */ |
| sym = lookup_symbol (copy.c_str (), block, MODULE_DOMAIN, NULL); |
| if (sym.symbol == NULL) |
| sym = lookup_symbol (copy.c_str (), block, STRUCT_DOMAIN, NULL); |
| |
| if (sym.symbol != NULL) |
| { |
| yylval.tsym.type = sym.symbol->type (); |
| return TYPENAME; |
| } |
| |
| return UNKNOWN_NAME; |
| } |
| |
| return IDENTIFIER; |
| } |
| |
| /* 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); |
| |
| type = check_typedef (context); |
| if (!type_aggregate_p (type)) |
| return ERROR; |
| |
| std::string copy = copy_name (yylval.ssym.stoken); |
| yylval.ssym.sym = d_lookup_nested_symbol (type, copy.c_str (), block); |
| |
| if (yylval.ssym.sym.symbol == NULL) |
| return ERROR; |
| |
| if (yylval.ssym.sym.symbol->aclass () == LOC_TYPEDEF) |
| { |
| yylval.tsym.type = yylval.ssym.sym.symbol->type (); |
| return TYPENAME; |
| } |
| |
| return IDENTIFIER; |
| } |
| |
| /* 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. */ |
| |
| static int |
| yylex (void) |
| { |
| token_and_value current; |
| int last_was_dot; |
| struct type *context_type = NULL; |
| int last_to_examine, next_to_examine, checkpoint; |
| const struct block *search_block; |
| |
| if (popping && !token_fifo.empty ()) |
| goto do_pop; |
| popping = 0; |
| |
| /* Read the first token and decide what to do. */ |
| current.token = lex_one_token (pstate); |
| if (current.token != IDENTIFIER && current.token != '.') |
| return current.token; |
| |
| /* Read any sequence of alternating "." and identifier tokens into |
| the token FIFO. */ |
| current.value = yylval; |
| token_fifo.push_back (current); |
| last_was_dot = current.token == '.'; |
| |
| while (1) |
| { |
| current.token = lex_one_token (pstate); |
| current.value = yylval; |
| token_fifo.push_back (current); |
| |
| if ((last_was_dot && current.token != IDENTIFIER) |
| || (!last_was_dot && current.token != '.')) |
| break; |
| |
| last_was_dot = !last_was_dot; |
| } |
| 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; |
| |
| /* If we are not dealing with a typename, now is the time to find out. */ |
| if (current.token == IDENTIFIER) |
| { |
| yylval = current.value; |
| current.token = classify_name (pstate, pstate->expression_context_block); |
| current.value = yylval; |
| } |
| |
| /* If the IDENTIFIER is not known, it could be a package symbol, |
| first try building up a name until we find the qualified module. */ |
| if (current.token == UNKNOWN_NAME) |
| { |
| name_obstack.clear (); |
| obstack_grow (&name_obstack, current.value.sval.ptr, |
| current.value.sval.length); |
| |
| last_was_dot = 0; |
| |
| while (next_to_examine <= last_to_examine) |
| { |
| token_and_value next; |
| |
| next = token_fifo[next_to_examine]; |
| ++next_to_examine; |
| |
| if (next.token == IDENTIFIER && last_was_dot) |
| { |
| /* Update the partial name we are constructing. */ |
| obstack_grow_str (&name_obstack, "."); |
| obstack_grow (&name_obstack, next.value.sval.ptr, |
| next.value.sval.length); |
| |
| yylval.sval.ptr = (char *) obstack_base (&name_obstack); |
| yylval.sval.length = obstack_object_size (&name_obstack); |
| |
| current.token = classify_name (pstate, |
| pstate->expression_context_block); |
| current.value = yylval; |
| |
| /* We keep going until we find a TYPENAME. */ |
| if (current.token == TYPENAME) |
| { |
| /* Install it as the first token in the FIFO. */ |
| token_fifo[0] = current; |
| token_fifo.erase (token_fifo.begin () + 1, |
| token_fifo.begin () + next_to_examine); |
| break; |
| } |
| } |
| else if (next.token == '.' && !last_was_dot) |
| last_was_dot = 1; |
| else |
| { |
| /* We've reached the end of the name. */ |
| break; |
| } |
| } |
| |
| /* Reset our current token back to the start, if we found nothing |
| this means that we will just jump to do pop. */ |
| current = token_fifo[0]; |
| next_to_examine = 1; |
| } |
| if (current.token != TYPENAME && current.token != '.') |
| goto do_pop; |
| |
| name_obstack.clear (); |
| checkpoint = 0; |
| if (current.token == '.') |
| 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; |
| } |
| |
| last_was_dot = current.token == '.'; |
| |
| while (next_to_examine <= last_to_examine) |
| { |
| token_and_value next; |
| |
| next = token_fifo[next_to_examine]; |
| ++next_to_examine; |
| |
| if (next.token == IDENTIFIER && last_was_dot) |
| { |
| 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 != IDENTIFIER) |
| 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 = (char *) obstack_base (&name_obstack); |
| yylval.sval.length = obstack_object_size (&name_obstack); |
| current.value = yylval; |
| current.token = classification; |
| |
| last_was_dot = 0; |
| |
| if (classification == IDENTIFIER) |
| break; |
| |
| context_type = yylval.tsym.type; |
| } |
| else if (next.token == '.' && !last_was_dot) |
| last_was_dot = 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) |
| { |
| 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 |
| d_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; |
| |
| scoped_restore restore_yydebug = make_scoped_restore (&yydebug, |
| parser_debug); |
| |
| struct type_stack stack; |
| scoped_restore restore_type_stack = make_scoped_restore (&type_stack, |
| &stack); |
| |
| /* Initialize some state used by the lexer. */ |
| last_was_structop = 0; |
| 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; |
| } |
| |
| 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); |
| } |
| |