| /* YACC grammar for Modula-2 expressions, for GDB. |
| Copyright (C) 1986-2024 Free Software Foundation, Inc. |
| Generated from expread.y (now c-exp.y) and contributed by the Department |
| of Computer Science at the State University of New York at Buffalo, 1991. |
| |
| 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 Modula-2 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 "expression.h" |
| #include "language.h" |
| #include "value.h" |
| #include "parser-defs.h" |
| #include "m2-lang.h" |
| #include "block.h" |
| #include "m2-exp.h" |
| |
| #define parse_type(ps) builtin_type (ps->gdbarch ()) |
| #define parse_m2_type(ps) builtin_m2_type (ps->gdbarch ()) |
| |
| /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, |
| etc). */ |
| #define GDB_YY_REMAP_PREFIX m2_ |
| #include "yy-remap.h" |
| |
| /* The state of the parser, used internally when we are parsing the |
| expression. */ |
| |
| static struct parser_state *pstate = NULL; |
| |
| int yyparse (void); |
| |
| static int yylex (void); |
| |
| static void yyerror (const char *); |
| |
| static int parse_number (int); |
| |
| /* The sign of the number being parsed. */ |
| static int number_sign = 1; |
| |
| 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; |
| ULONGEST ulval; |
| gdb_byte val[16]; |
| struct symbol *sym; |
| struct type *tval; |
| struct stoken sval; |
| int voidval; |
| const struct block *bval; |
| enum exp_opcode opcode; |
| struct internalvar *ivar; |
| |
| struct type **tvec; |
| int *ivec; |
| } |
| |
| %type <voidval> exp type_exp start set |
| %type <voidval> variable |
| %type <tval> type |
| %type <bval> block |
| %type <sym> fblock |
| |
| %token <lval> INT HEX ERROR |
| %token <ulval> UINT M2_TRUE M2_FALSE CHAR |
| %token <val> 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 <sval> STRING |
| %token <sval> NAME BLOCKNAME IDENT VARNAME |
| %token <sval> TYPENAME |
| |
| %token SIZE CAP ORD HIGH ABS MIN_FUNC MAX_FUNC FLOAT_FUNC VAL CHR ODD TRUNC |
| %token TSIZE |
| %token INC DEC INCL EXCL |
| |
| /* The GDB scope operator */ |
| %token COLONCOLON |
| |
| %token <sval> DOLLAR_VARIABLE |
| |
| /* M2 tokens */ |
| %left ',' |
| %left ABOVE_COMMA |
| %nonassoc ASSIGN |
| %left '<' '>' LEQ GEQ '=' NOTEQUAL '#' IN |
| %left OROR |
| %left LOGICAL_AND '&' |
| %left '@' |
| %left '+' '-' |
| %left '*' '/' DIV MOD |
| %right UNARY |
| %right '^' DOT '[' '(' |
| %right NOT '~' |
| %left COLONCOLON QID |
| /* This is not an actual token ; it is used for precedence. |
| %right QID |
| */ |
| |
| |
| %% |
| |
| start : exp |
| | type_exp |
| ; |
| |
| type_exp: type |
| { pstate->push_new<type_operation> ($1); } |
| ; |
| |
| /* Expressions */ |
| |
| exp : exp '^' %prec UNARY |
| { pstate->wrap<unop_ind_operation> (); } |
| ; |
| |
| exp : '-' |
| { number_sign = -1; } |
| exp %prec UNARY |
| { number_sign = 1; |
| pstate->wrap<unary_neg_operation> (); } |
| ; |
| |
| exp : '+' exp %prec UNARY |
| { pstate->wrap<unary_plus_operation> (); } |
| ; |
| |
| exp : not_exp exp %prec UNARY |
| { pstate->wrap<unary_logical_not_operation> (); } |
| ; |
| |
| not_exp : NOT |
| | '~' |
| ; |
| |
| exp : CAP '(' exp ')' |
| { error (_("CAP function is not implemented")); } |
| ; |
| |
| exp : ORD '(' exp ')' |
| { error (_("ORD function is not implemented")); } |
| ; |
| |
| exp : ABS '(' exp ')' |
| { error (_("ABS function is not implemented")); } |
| ; |
| |
| exp : HIGH '(' exp ')' |
| { pstate->wrap<m2_unop_high_operation> (); } |
| ; |
| |
| exp : MIN_FUNC '(' type ')' |
| { error (_("MIN function is not implemented")); } |
| ; |
| |
| exp : MAX_FUNC '(' type ')' |
| { error (_("MAX function is not implemented")); } |
| ; |
| |
| exp : FLOAT_FUNC '(' exp ')' |
| { error (_("FLOAT function is not implemented")); } |
| ; |
| |
| exp : VAL '(' type ',' exp ')' |
| { error (_("VAL function is not implemented")); } |
| ; |
| |
| exp : CHR '(' exp ')' |
| { error (_("CHR function is not implemented")); } |
| ; |
| |
| exp : ODD '(' exp ')' |
| { error (_("ODD function is not implemented")); } |
| ; |
| |
| exp : TRUNC '(' exp ')' |
| { error (_("TRUNC function is not implemented")); } |
| ; |
| |
| exp : TSIZE '(' exp ')' |
| { pstate->wrap<unop_sizeof_operation> (); } |
| ; |
| |
| exp : SIZE exp %prec UNARY |
| { pstate->wrap<unop_sizeof_operation> (); } |
| ; |
| |
| |
| exp : INC '(' exp ')' |
| { pstate->wrap<preinc_operation> (); } |
| ; |
| |
| exp : INC '(' exp ',' exp ')' |
| { |
| operation_up rhs = pstate->pop (); |
| operation_up lhs = pstate->pop (); |
| pstate->push_new<assign_modify_operation> |
| (BINOP_ADD, std::move (lhs), std::move (rhs)); |
| } |
| ; |
| |
| exp : DEC '(' exp ')' |
| { pstate->wrap<predec_operation> (); } |
| ; |
| |
| exp : DEC '(' exp ',' exp ')' |
| { |
| operation_up rhs = pstate->pop (); |
| operation_up lhs = pstate->pop (); |
| pstate->push_new<assign_modify_operation> |
| (BINOP_SUB, std::move (lhs), std::move (rhs)); |
| } |
| ; |
| |
| exp : exp DOT NAME |
| { |
| pstate->push_new<structop_operation> |
| (pstate->pop (), copy_name ($3)); |
| } |
| ; |
| |
| exp : set |
| ; |
| |
| exp : exp IN set |
| { error (_("Sets are not implemented."));} |
| ; |
| |
| exp : INCL '(' exp ',' exp ')' |
| { error (_("Sets are not implemented."));} |
| ; |
| |
| exp : EXCL '(' exp ',' exp ')' |
| { error (_("Sets are not implemented."));} |
| ; |
| |
| set : '{' arglist '}' |
| { error (_("Sets are not implemented."));} |
| | type '{' arglist '}' |
| { error (_("Sets are not implemented."));} |
| ; |
| |
| |
| /* Modula-2 array subscript notation [a,b,c...]. */ |
| exp : exp '[' |
| /* This function just saves the number of arguments |
| that follow in the list. It is *not* specific to |
| function types */ |
| { pstate->start_arglist(); } |
| non_empty_arglist ']' %prec DOT |
| { |
| gdb_assert (pstate->arglist_len > 0); |
| std::vector<operation_up> args |
| = pstate->pop_vector (pstate->end_arglist ()); |
| pstate->push_new<multi_subscript_operation> |
| (pstate->pop (), std::move (args)); |
| } |
| ; |
| |
| exp : exp '(' |
| /* This is to save the value of arglist_len |
| being accumulated by an outer function call. */ |
| { pstate->start_arglist (); } |
| arglist ')' %prec DOT |
| { |
| std::vector<operation_up> args |
| = pstate->pop_vector (pstate->end_arglist ()); |
| pstate->push_new<funcall_operation> |
| (pstate->pop (), std::move (args)); |
| } |
| ; |
| |
| arglist : |
| ; |
| |
| arglist : exp |
| { pstate->arglist_len = 1; } |
| ; |
| |
| arglist : arglist ',' exp %prec ABOVE_COMMA |
| { pstate->arglist_len++; } |
| ; |
| |
| non_empty_arglist |
| : exp |
| { pstate->arglist_len = 1; } |
| ; |
| |
| non_empty_arglist |
| : non_empty_arglist ',' exp %prec ABOVE_COMMA |
| { pstate->arglist_len++; } |
| ; |
| |
| /* GDB construct */ |
| exp : '{' type '}' exp %prec UNARY |
| { |
| pstate->push_new<unop_memval_operation> |
| (pstate->pop (), $2); |
| } |
| ; |
| |
| exp : type '(' exp ')' %prec UNARY |
| { |
| pstate->push_new<unop_cast_operation> |
| (pstate->pop (), $1); |
| } |
| ; |
| |
| exp : '(' exp ')' |
| { } |
| ; |
| |
| /* Binary operators in order of decreasing precedence. Note that some |
| of these operators are overloaded! (ie. sets) */ |
| |
| /* GDB construct */ |
| exp : exp '@' exp |
| { pstate->wrap2<repeat_operation> (); } |
| ; |
| |
| exp : exp '*' exp |
| { pstate->wrap2<mul_operation> (); } |
| ; |
| |
| exp : exp '/' exp |
| { pstate->wrap2<div_operation> (); } |
| ; |
| |
| exp : exp DIV exp |
| { pstate->wrap2<intdiv_operation> (); } |
| ; |
| |
| exp : exp MOD exp |
| { pstate->wrap2<rem_operation> (); } |
| ; |
| |
| exp : exp '+' exp |
| { pstate->wrap2<add_operation> (); } |
| ; |
| |
| exp : exp '-' exp |
| { pstate->wrap2<sub_operation> (); } |
| ; |
| |
| exp : exp '=' exp |
| { pstate->wrap2<equal_operation> (); } |
| ; |
| |
| exp : exp NOTEQUAL exp |
| { pstate->wrap2<notequal_operation> (); } |
| | exp '#' exp |
| { pstate->wrap2<notequal_operation> (); } |
| ; |
| |
| exp : exp LEQ exp |
| { pstate->wrap2<leq_operation> (); } |
| ; |
| |
| exp : exp GEQ exp |
| { pstate->wrap2<geq_operation> (); } |
| ; |
| |
| exp : exp '<' exp |
| { pstate->wrap2<less_operation> (); } |
| ; |
| |
| exp : exp '>' exp |
| { pstate->wrap2<gtr_operation> (); } |
| ; |
| |
| exp : exp LOGICAL_AND exp |
| { pstate->wrap2<logical_and_operation> (); } |
| ; |
| |
| exp : exp OROR exp |
| { pstate->wrap2<logical_or_operation> (); } |
| ; |
| |
| exp : exp ASSIGN exp |
| { pstate->wrap2<assign_operation> (); } |
| ; |
| |
| |
| /* Constants */ |
| |
| exp : M2_TRUE |
| { pstate->push_new<bool_operation> ($1); } |
| ; |
| |
| exp : M2_FALSE |
| { pstate->push_new<bool_operation> ($1); } |
| ; |
| |
| exp : INT |
| { |
| pstate->push_new<long_const_operation> |
| (parse_m2_type (pstate)->builtin_int, $1); |
| } |
| ; |
| |
| exp : UINT |
| { |
| pstate->push_new<long_const_operation> |
| (parse_m2_type (pstate)->builtin_card, $1); |
| } |
| ; |
| |
| exp : CHAR |
| { |
| pstate->push_new<long_const_operation> |
| (parse_m2_type (pstate)->builtin_char, $1); |
| } |
| ; |
| |
| |
| exp : FLOAT |
| { |
| float_data data; |
| std::copy (std::begin ($1), std::end ($1), |
| std::begin (data)); |
| pstate->push_new<float_const_operation> |
| (parse_m2_type (pstate)->builtin_real, data); |
| } |
| ; |
| |
| exp : variable |
| ; |
| |
| exp : SIZE '(' type ')' %prec UNARY |
| { |
| pstate->push_new<long_const_operation> |
| (parse_m2_type (pstate)->builtin_int, |
| $3->length ()); |
| } |
| ; |
| |
| exp : STRING |
| { error (_("strings are not implemented")); } |
| ; |
| |
| /* This will be used for extensions later. Like adding modules. */ |
| block : fblock |
| { $$ = $1->value_block (); } |
| ; |
| |
| fblock : BLOCKNAME |
| { struct symbol *sym |
| = lookup_symbol (copy_name ($1).c_str (), |
| pstate->expression_context_block, |
| SEARCH_VFT, 0).symbol; |
| $$ = sym;} |
| ; |
| |
| |
| /* GDB scope operator */ |
| fblock : block COLONCOLON BLOCKNAME |
| { struct symbol *tem |
| = lookup_symbol (copy_name ($3).c_str (), $1, |
| SEARCH_VFT, 0).symbol; |
| if (!tem || tem->aclass () != LOC_BLOCK) |
| error (_("No function \"%s\" in specified context."), |
| copy_name ($3).c_str ()); |
| $$ = tem; |
| } |
| ; |
| |
| /* Useful for assigning to PROCEDURE variables */ |
| variable: fblock |
| { |
| block_symbol sym { $1, nullptr }; |
| pstate->push_new<var_value_operation> (sym); |
| } |
| ; |
| |
| /* GDB internal ($foo) variable */ |
| variable: DOLLAR_VARIABLE |
| { pstate->push_dollar ($1); } |
| ; |
| |
| /* GDB scope operator */ |
| variable: block COLONCOLON NAME |
| { struct block_symbol sym |
| = lookup_symbol (copy_name ($3).c_str (), $1, |
| SEARCH_VFT, 0); |
| |
| if (sym.symbol == 0) |
| error (_("No symbol \"%s\" in specified context."), |
| copy_name ($3).c_str ()); |
| if (symbol_read_needs_frame (sym.symbol)) |
| pstate->block_tracker->update (sym); |
| |
| pstate->push_new<var_value_operation> (sym); |
| } |
| ; |
| |
| /* Base case for variables. */ |
| variable: NAME |
| { struct block_symbol sym; |
| struct field_of_this_result is_a_field_of_this; |
| |
| std::string name = copy_name ($1); |
| sym |
| = lookup_symbol (name.c_str (), |
| pstate->expression_context_block, |
| SEARCH_VFT, |
| &is_a_field_of_this); |
| |
| pstate->push_symbol (name.c_str (), sym); |
| } |
| ; |
| |
| type |
| : TYPENAME |
| { $$ |
| = lookup_typename (pstate->language (), |
| copy_name ($1).c_str (), |
| pstate->expression_context_block, |
| 0); |
| } |
| |
| ; |
| |
| %% |
| |
| /* 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 (int olen) |
| { |
| const char *p = pstate->lexptr; |
| ULONGEST n = 0; |
| ULONGEST prevn = 0; |
| int c,i,ischar=0; |
| int base = input_radix; |
| int len = olen; |
| |
| if(p[len-1] == 'H') |
| { |
| base = 16; |
| len--; |
| } |
| else if(p[len-1] == 'C' || p[len-1] == 'B') |
| { |
| base = 8; |
| ischar = p[len-1] == 'C'; |
| len--; |
| } |
| |
| /* Scan the number */ |
| for (c = 0; c < len; c++) |
| { |
| if (p[c] == '.' && base == 10) |
| { |
| /* It's a float since it contains a point. */ |
| if (!parse_float (p, len, |
| parse_m2_type (pstate)->builtin_real, |
| yylval.val)) |
| return ERROR; |
| |
| pstate->lexptr += len; |
| return FLOAT; |
| } |
| if (p[c] == '.' && base != 10) |
| error (_("Floating point numbers must be base 10.")); |
| if (base == 10 && (p[c] < '0' || p[c] > '9')) |
| error (_("Invalid digit \'%c\' in number."),p[c]); |
| } |
| |
| while (len-- > 0) |
| { |
| c = *p++; |
| n *= base; |
| if( base == 8 && (c == '8' || c == '9')) |
| error (_("Invalid digit \'%c\' in octal number."),c); |
| if (c >= '0' && c <= '9') |
| i = c - '0'; |
| else |
| { |
| if (base == 16 && c >= 'A' && c <= 'F') |
| i = c - 'A' + 10; |
| else |
| return ERROR; |
| } |
| n+=i; |
| if(i >= base) |
| return ERROR; |
| if (n == 0 && prevn == 0) |
| ; |
| else if (RANGE_CHECK && prevn >= n) |
| range_error (_("Overflow on numeric constant.")); |
| |
| prevn=n; |
| } |
| |
| pstate->lexptr = p; |
| if(*p == 'B' || *p == 'C' || *p == 'H') |
| pstate->lexptr++; /* Advance past B,C or H */ |
| |
| if (ischar) |
| { |
| yylval.ulval = n; |
| return CHAR; |
| } |
| |
| int int_bits = gdbarch_int_bit (pstate->gdbarch ()); |
| bool have_signed = number_sign == -1; |
| bool have_unsigned = number_sign == 1; |
| if (have_signed && fits_in_type (number_sign, n, int_bits, true)) |
| { |
| yylval.lval = n; |
| return INT; |
| } |
| else if (have_unsigned && fits_in_type (number_sign, n, int_bits, false)) |
| { |
| yylval.ulval = n; |
| return UINT; |
| } |
| else |
| error (_("Overflow on numeric constant.")); |
| } |
| |
| |
| /* Some tokens */ |
| |
| static struct |
| { |
| char name[2]; |
| int token; |
| } tokentab2[] = |
| { |
| { {'<', '>'}, NOTEQUAL }, |
| { {':', '='}, ASSIGN }, |
| { {'<', '='}, LEQ }, |
| { {'>', '='}, GEQ }, |
| { {':', ':'}, COLONCOLON }, |
| |
| }; |
| |
| /* Some specific keywords */ |
| |
| struct keyword { |
| char keyw[10]; |
| int token; |
| }; |
| |
| static struct keyword keytab[] = |
| { |
| {"OR" , OROR }, |
| {"IN", IN },/* Note space after IN */ |
| {"AND", LOGICAL_AND}, |
| {"ABS", ABS }, |
| {"CHR", CHR }, |
| {"DEC", DEC }, |
| {"NOT", NOT }, |
| {"DIV", DIV }, |
| {"INC", INC }, |
| {"MAX", MAX_FUNC }, |
| {"MIN", MIN_FUNC }, |
| {"MOD", MOD }, |
| {"ODD", ODD }, |
| {"CAP", CAP }, |
| {"ORD", ORD }, |
| {"VAL", VAL }, |
| {"EXCL", EXCL }, |
| {"HIGH", HIGH }, |
| {"INCL", INCL }, |
| {"SIZE", SIZE }, |
| {"FLOAT", FLOAT_FUNC }, |
| {"TRUNC", TRUNC }, |
| {"TSIZE", SIZE }, |
| }; |
| |
| |
| /* Depth of parentheses. */ |
| static int paren_depth; |
| |
| /* Read one token, getting characters through lexptr. */ |
| |
| /* This is where we will check to make sure that the language and the |
| operators used are compatible */ |
| |
| static int |
| yylex (void) |
| { |
| int c; |
| int namelen; |
| int i; |
| const char *tokstart; |
| char quote; |
| |
| retry: |
| |
| pstate->prev_lexptr = pstate->lexptr; |
| |
| tokstart = pstate->lexptr; |
| |
| |
| /* See if it is a special token of length 2 */ |
| for( i = 0 ; i < (int) (sizeof tokentab2 / sizeof tokentab2[0]) ; i++) |
| if (strncmp (tokentab2[i].name, tokstart, 2) == 0) |
| { |
| pstate->lexptr += 2; |
| return tokentab2[i].token; |
| } |
| |
| switch (c = *tokstart) |
| { |
| case 0: |
| return 0; |
| |
| case ' ': |
| case '\t': |
| case '\n': |
| pstate->lexptr++; |
| goto retry; |
| |
| case '(': |
| paren_depth++; |
| pstate->lexptr++; |
| return c; |
| |
| 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') |
| break; /* Falls into number code. */ |
| else |
| { |
| pstate->lexptr++; |
| return DOT; |
| } |
| |
| /* These are character tokens that appear as-is in the YACC grammar */ |
| case '+': |
| case '-': |
| case '*': |
| case '/': |
| case '^': |
| case '<': |
| case '>': |
| case '[': |
| case ']': |
| case '=': |
| case '{': |
| case '}': |
| case '#': |
| case '@': |
| case '~': |
| case '&': |
| pstate->lexptr++; |
| return c; |
| |
| case '\'' : |
| case '"': |
| quote = c; |
| for (namelen = 1; (c = tokstart[namelen]) != quote && c != '\0'; namelen++) |
| if (c == '\\') |
| { |
| c = tokstart[++namelen]; |
| if (c >= '0' && c <= '9') |
| { |
| c = tokstart[++namelen]; |
| if (c >= '0' && c <= '9') |
| c = tokstart[++namelen]; |
| } |
| } |
| if(c != quote) |
| error (_("Unterminated string or character constant.")); |
| yylval.sval.ptr = tokstart + 1; |
| yylval.sval.length = namelen - 1; |
| pstate->lexptr += namelen + 1; |
| |
| if(namelen == 2) /* Single character */ |
| { |
| yylval.ulval = tokstart[1]; |
| return CHAR; |
| } |
| else |
| return STRING; |
| } |
| |
| /* Is it a number? */ |
| /* Note: We have already dealt with the case of the token '.'. |
| See case '.' above. */ |
| if ((c >= '0' && c <= '9')) |
| { |
| /* It's a number. */ |
| int got_dot = 0, got_e = 0; |
| const char *p = tokstart; |
| int toktype; |
| |
| for (++p ;; ++p) |
| { |
| if (!got_e && (*p == 'e' || *p == 'E')) |
| got_dot = got_e = 1; |
| else if (!got_dot && *p == '.') |
| got_dot = 1; |
| else if (got_e && (p[-1] == 'e' || p[-1] == 'E') |
| && (*p == '-' || *p == '+')) |
| /* This is the sign of the exponent, not the end of the |
| number. */ |
| continue; |
| else if ((*p < '0' || *p > '9') && |
| (*p < 'A' || *p > 'F') && |
| (*p != 'H')) /* Modula-2 hexadecimal number */ |
| break; |
| } |
| toktype = parse_number (p - tokstart); |
| if (toktype == ERROR) |
| error (_("Invalid number \"%.*s\"."), (int) (p - tokstart), |
| tokstart); |
| pstate->lexptr = p; |
| return toktype; |
| } |
| |
| 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; |
| } |
| |
| pstate->lexptr += namelen; |
| |
| /* Lookup special keywords */ |
| for(i = 0 ; i < (int) (sizeof(keytab) / sizeof(keytab[0])) ; i++) |
| if (namelen == strlen (keytab[i].keyw) |
| && strncmp (tokstart, keytab[i].keyw, namelen) == 0) |
| return keytab[i].token; |
| |
| yylval.sval.ptr = tokstart; |
| yylval.sval.length = namelen; |
| |
| if (*tokstart == '$') |
| return DOLLAR_VARIABLE; |
| |
| /* Use token-type BLOCKNAME for symbols that happen to be defined as |
| functions. If this is not so, then ... |
| Use token-type TYPENAME for symbols that happen to be defined |
| currently as names of types; NAME for other symbols. |
| The caller is not constrained to care about the distinction. */ |
| { |
| std::string tmp = copy_name (yylval.sval); |
| struct symbol *sym; |
| |
| if (lookup_symtab (tmp.c_str ())) |
| return BLOCKNAME; |
| sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block, |
| SEARCH_VFT, 0).symbol; |
| if (sym && sym->aclass () == LOC_BLOCK) |
| return BLOCKNAME; |
| if (lookup_typename (pstate->language (), |
| tmp.c_str (), pstate->expression_context_block, 1)) |
| return TYPENAME; |
| |
| if(sym) |
| { |
| switch(sym->aclass ()) |
| { |
| case LOC_STATIC: |
| case LOC_REGISTER: |
| case LOC_ARG: |
| case LOC_REF_ARG: |
| case LOC_REGPARM_ADDR: |
| case LOC_LOCAL: |
| case LOC_CONST: |
| case LOC_CONST_BYTES: |
| case LOC_OPTIMIZED_OUT: |
| case LOC_COMPUTED: |
| return NAME; |
| |
| case LOC_TYPEDEF: |
| return TYPENAME; |
| |
| case LOC_BLOCK: |
| return BLOCKNAME; |
| |
| case LOC_UNDEF: |
| error (_("internal: Undefined class in m2lex()")); |
| |
| case LOC_LABEL: |
| case LOC_UNRESOLVED: |
| error (_("internal: Unforseen case in m2lex()")); |
| |
| default: |
| error (_("unhandled token in m2lex()")); |
| break; |
| } |
| } |
| else |
| { |
| /* Built-in BOOLEAN type. This is sort of a hack. */ |
| if (startswith (tokstart, "TRUE")) |
| { |
| yylval.ulval = 1; |
| return M2_TRUE; |
| } |
| else if (startswith (tokstart, "FALSE")) |
| { |
| yylval.ulval = 0; |
| return M2_FALSE; |
| } |
| } |
| |
| /* Must be another type of name... */ |
| return NAME; |
| } |
| } |
| |
| int |
| m2_language::parser (struct parser_state *par_state) const |
| { |
| /* Setting up the parser state. */ |
| scoped_restore pstate_restore = make_scoped_restore (&pstate); |
| gdb_assert (par_state != NULL); |
| pstate = par_state; |
| paren_depth = 0; |
| |
| int result = yyparse (); |
| if (!result) |
| pstate->set_operation (pstate->pop ()); |
| return result; |
| } |
| |
| static void |
| yyerror (const char *msg) |
| { |
| pstate->parse_error (msg); |
| } |