|  | /* YACC parser for Go expressions, for GDB. | 
|  |  | 
|  | Copyright (C) 2012-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, p-exp.y.  */ | 
|  |  | 
|  | /* Parse a Go 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.  */ | 
|  |  | 
|  | /* Known bugs or limitations: | 
|  |  | 
|  | - Unicode | 
|  | - &^ | 
|  | - '_' (blank identifier) | 
|  | - automatic deref of pointers | 
|  | - method expressions | 
|  | - interfaces, channels, etc. | 
|  |  | 
|  | And lots of other things. | 
|  | I'm sure there's some cleanup to do. | 
|  | */ | 
|  |  | 
|  | %{ | 
|  |  | 
|  | #include "defs.h" | 
|  | #include <ctype.h> | 
|  | #include "expression.h" | 
|  | #include "value.h" | 
|  | #include "parser-defs.h" | 
|  | #include "language.h" | 
|  | #include "c-lang.h" | 
|  | #include "go-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 "expop.h" | 
|  |  | 
|  | #define parse_type(ps) builtin_type (ps->gdbarch ()) | 
|  |  | 
|  | /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, | 
|  | etc).  */ | 
|  | #define GDB_YY_REMAP_PREFIX go_ | 
|  | #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 *); | 
|  |  | 
|  | %} | 
|  |  | 
|  | /* 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 stoken sval; | 
|  | struct symtoken ssym; | 
|  | struct type *tval; | 
|  | struct typed_stoken tsval; | 
|  | struct ttype tsym; | 
|  | int voidval; | 
|  | enum exp_opcode opcode; | 
|  | struct internalvar *ivar; | 
|  | struct stoken_vector svec; | 
|  | } | 
|  |  | 
|  | %{ | 
|  | /* YYSTYPE gets defined by %union.  */ | 
|  | static int parse_number (struct parser_state *, | 
|  | const char *, int, int, YYSTYPE *); | 
|  |  | 
|  | using namespace expr; | 
|  | %} | 
|  |  | 
|  | %type <voidval> exp exp1 type_exp start variable lcurly | 
|  | %type <lval> rcurly | 
|  | %type <tval> type | 
|  |  | 
|  | %token <typed_val_int> INT | 
|  | %token <typed_val_float> 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 type | 
|  | 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> RAW_STRING | 
|  | %token <tsval> STRING | 
|  | %token <tsval> CHAR | 
|  | %token <ssym> NAME | 
|  | %token <tsym> TYPENAME /* Not TYPE_NAME cus already taken.  */ | 
|  | %token <voidval> COMPLETE | 
|  | /*%type <sval> name*/ | 
|  | %type <svec> string_exp | 
|  | %type <ssym> name_not_typename | 
|  |  | 
|  | /* 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 <lval> TRUE_KEYWORD FALSE_KEYWORD | 
|  | %token STRUCT_KEYWORD INTERFACE_KEYWORD TYPE_KEYWORD CHAN_KEYWORD | 
|  | %token SIZEOF_KEYWORD | 
|  | %token LEN_KEYWORD CAP_KEYWORD | 
|  | %token NEW_KEYWORD | 
|  | %token IOTA_KEYWORD NIL_KEYWORD | 
|  | %token CONST_KEYWORD | 
|  | %token DOTDOTDOT | 
|  | %token ENTRY | 
|  | %token ERROR | 
|  |  | 
|  | /* Special type cases.  */ | 
|  | %token BYTE_KEYWORD /* An alias of uint8.  */ | 
|  |  | 
|  | %token <sval> DOLLAR_VARIABLE | 
|  |  | 
|  | %token <opcode> ASSIGN_MODIFY | 
|  |  | 
|  | %left ',' | 
|  | %left ABOVE_COMMA | 
|  | %right '=' ASSIGN_MODIFY | 
|  | %right '?' | 
|  | %left OROR | 
|  | %left ANDAND | 
|  | %left '|' | 
|  | %left '^' | 
|  | %left '&' | 
|  | %left ANDNOT | 
|  | %left EQUAL NOTEQUAL | 
|  | %left '<' '>' LEQ GEQ | 
|  | %left LSH RSH | 
|  | %left '@' | 
|  | %left '+' '-' | 
|  | %left '*' '/' '%' | 
|  | %right UNARY INCREMENT DECREMENT | 
|  | %right LEFT_ARROW '.' '[' '(' | 
|  |  | 
|  |  | 
|  | %% | 
|  |  | 
|  | start   :	exp1 | 
|  | |	type_exp | 
|  | ; | 
|  |  | 
|  | type_exp:	type | 
|  | { pstate->push_new<type_operation> ($1); } | 
|  | ; | 
|  |  | 
|  | /* 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 | 
|  | { pstate->wrap<unary_logical_not_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	'^' exp    %prec UNARY | 
|  | { pstate->wrap<unary_complement_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp INCREMENT    %prec UNARY | 
|  | { pstate->wrap<postinc_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp DECREMENT    %prec UNARY | 
|  | { pstate->wrap<postdec_operation> (); } | 
|  | ; | 
|  |  | 
|  | /* foo->bar is not in Go.  May want as a gdb extension.  Later.  */ | 
|  |  | 
|  | exp	:	exp '.' name_not_typename | 
|  | { | 
|  | pstate->push_new<structop_operation> | 
|  | (pstate->pop (), copy_name ($3.stoken)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '.' name_not_typename COMPLETE | 
|  | { | 
|  | structop_base_operation *op | 
|  | = new structop_operation (pstate->pop (), | 
|  | copy_name ($3.stoken)); | 
|  | 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 '[' exp1 ']' | 
|  | { pstate->wrap2<subscript_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '(' | 
|  | /* This is to save the value of arglist_len | 
|  | being accumulated by an outer function call.  */ | 
|  | { pstate->start_arglist (); } | 
|  | arglist ')'	%prec LEFT_ARROW | 
|  | { | 
|  | std::vector<operation_up> args | 
|  | = pstate->pop_vector (pstate->end_arglist ()); | 
|  | pstate->push_new<funcall_operation> | 
|  | (pstate->pop (), std::move (args)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | lcurly	:	'{' | 
|  | { pstate->start_arglist (); } | 
|  | ; | 
|  |  | 
|  | arglist	: | 
|  | ; | 
|  |  | 
|  | arglist	:	exp | 
|  | { pstate->arglist_len = 1; } | 
|  | ; | 
|  |  | 
|  | arglist	:	arglist ',' exp   %prec ABOVE_COMMA | 
|  | { pstate->arglist_len++; } | 
|  | ; | 
|  |  | 
|  | rcurly	:	'}' | 
|  | { $$ = pstate->end_arglist () - 1; } | 
|  | ; | 
|  |  | 
|  | exp	:	lcurly type rcurly 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	:	'(' 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 | 
|  | { pstate->wrap2<equal_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp NOTEQUAL 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 '&' 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 | 
|  | { pstate->wrap2<logical_and_operation> (); } | 
|  | ; | 
|  |  | 
|  | exp	:	exp OROR exp | 
|  | { 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 (); | 
|  | pstate->push_new<ternop_cond_operation> | 
|  | (std::move (first), std::move (mid), | 
|  | std::move (last)); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	exp '=' exp | 
|  | { 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	:	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	:	variable | 
|  | ; | 
|  |  | 
|  | exp	:	DOLLAR_VARIABLE | 
|  | { | 
|  | pstate->push_dollar ($1); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	SIZEOF_KEYWORD '(' type ')'  %prec UNARY | 
|  | { | 
|  | /* TODO(dje): Go objects in structs.  */ | 
|  | /* TODO(dje): What's the right type here?  */ | 
|  | struct type *size_type | 
|  | = parse_type (pstate)->builtin_unsigned_int; | 
|  | $3 = check_typedef ($3); | 
|  | pstate->push_new<long_const_operation> | 
|  | (size_type, (LONGEST) $3->length ()); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	SIZEOF_KEYWORD  '(' exp ')'  %prec UNARY | 
|  | { | 
|  | /* TODO(dje): Go objects in structs.  */ | 
|  | pstate->wrap<unop_sizeof_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 NUL-terminate here, but just | 
|  | for convenience.  */ | 
|  | 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 ($3.length + 1); | 
|  | memcpy (p, $3.ptr, $3.length + 1); | 
|  |  | 
|  | $$.tokens[$$.len - 1].type = $3.type; | 
|  | $$.tokens[$$.len - 1].length = $3.length; | 
|  | $$.tokens[$$.len - 1].ptr = p; | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	string_exp  %prec ABOVE_COMMA | 
|  | { | 
|  | int i; | 
|  |  | 
|  | /* Always utf8.  */ | 
|  | pstate->push_c_string (0, &$1); | 
|  | for (i = 0; i < $1.len; ++i) | 
|  | free ($1.tokens[i].ptr); | 
|  | free ($1.tokens); | 
|  | } | 
|  | ; | 
|  |  | 
|  | exp	:	TRUE_KEYWORD | 
|  | { pstate->push_new<bool_operation> ($1); } | 
|  | ; | 
|  |  | 
|  | exp	:	FALSE_KEYWORD | 
|  | { pstate->push_new<bool_operation> ($1); } | 
|  | ; | 
|  |  | 
|  | 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:	name_not_typename | 
|  | { struct block_symbol sym = $1.sym; | 
|  |  | 
|  | if (sym.symbol) | 
|  | { | 
|  | if (symbol_read_needs_frame (sym.symbol)) | 
|  | pstate->block_tracker->update (sym); | 
|  |  | 
|  | pstate->push_new<var_value_operation> (sym); | 
|  | } | 
|  | else if ($1.is_a_field_of_this) | 
|  | { | 
|  | /* TODO(dje): Can we get here? | 
|  | E.g., via a mix of c++ and go?  */ | 
|  | gdb_assert_not_reached ("go with `this' field"); | 
|  | } | 
|  | else | 
|  | { | 
|  | struct bound_minimal_symbol msymbol; | 
|  | std::string arg = copy_name ($1.stoken); | 
|  |  | 
|  | msymbol = | 
|  | lookup_bound_minimal_symbol (arg.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."), | 
|  | arg.c_str ()); | 
|  | } | 
|  | } | 
|  | ; | 
|  |  | 
|  | /* TODO | 
|  | method_exp: PACKAGENAME '.' name '.' name | 
|  | { | 
|  | } | 
|  | ; | 
|  | */ | 
|  |  | 
|  | type  /* Implements (approximately): [*] type-specifier */ | 
|  | :	'*' type | 
|  | { $$ = lookup_pointer_type ($2); } | 
|  | |	TYPENAME | 
|  | { $$ = $1.type; } | 
|  | /* | 
|  | |	STRUCT_KEYWORD name | 
|  | { $$ = lookup_struct (copy_name ($2), | 
|  | expression_context_block); } | 
|  | */ | 
|  | |	BYTE_KEYWORD | 
|  | { $$ = builtin_go_type (pstate->gdbarch ()) | 
|  | ->builtin_uint8; } | 
|  | ; | 
|  |  | 
|  | /* TODO | 
|  | name	:	NAME { $$ = $1.stoken; } | 
|  | |	TYPENAME { $$ = $1.stoken; } | 
|  | |	NAME_OR_INT  { $$ = $1.stoken; } | 
|  | ; | 
|  | */ | 
|  |  | 
|  | name_not_typename | 
|  | :	NAME | 
|  | /* 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 | 
|  | */ | 
|  | ; | 
|  |  | 
|  | %% | 
|  |  | 
|  | /* 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.  */ | 
|  |  | 
|  | /* FIXME: Needs some error checking for the float case.  */ | 
|  | /* FIXME(dje): IWBN to use c-exp.y's parse_number if we could. | 
|  | That will require moving the guts into a function that we both call | 
|  | as our YYSTYPE is different than c-exp.y's  */ | 
|  |  | 
|  | static int | 
|  | parse_number (struct parser_state *par_state, | 
|  | const char *p, 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; | 
|  |  | 
|  | /* We have found a "L" or "U" suffix.  */ | 
|  | int found_suffix = 0; | 
|  |  | 
|  | if (parsed_float) | 
|  | { | 
|  | const struct builtin_go_type *builtin_go_types | 
|  | = builtin_go_type (par_state->gdbarch ()); | 
|  |  | 
|  | /* Handle suffixes: 'f' for float32, 'l' for long double. | 
|  | FIXME: This appears to be an extension -- do we want this?  */ | 
|  | if (len >= 1 && tolower (p[len - 1]) == 'f') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = builtin_go_types->builtin_float32; | 
|  | len--; | 
|  | } | 
|  | else if (len >= 1 && tolower (p[len - 1]) == 'l') | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = parse_type (par_state)->builtin_long_double; | 
|  | len--; | 
|  | } | 
|  | /* Default type for floating-point literals is float64.  */ | 
|  | else | 
|  | { | 
|  | putithere->typed_val_float.type | 
|  | = builtin_go_types->builtin_float64; | 
|  | } | 
|  |  | 
|  | if (!parse_float (p, len, | 
|  | putithere->typed_val_float.type, | 
|  | putithere->typed_val_float.val)) | 
|  | return ERROR; | 
|  | return FLOAT; | 
|  | } | 
|  |  | 
|  | /* Handle base-switching prefixes 0x, 0t, 0d, 0.  */ | 
|  | if (p[0] == '0' && len > 1) | 
|  | switch (p[1]) | 
|  | { | 
|  | case 'x': | 
|  | case 'X': | 
|  | if (len >= 3) | 
|  | { | 
|  | p += 2; | 
|  | base = 16; | 
|  | len -= 2; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'b': | 
|  | case 'B': | 
|  | if (len >= 3) | 
|  | { | 
|  | p += 2; | 
|  | base = 2; | 
|  | len -= 2; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 't': | 
|  | case 'T': | 
|  | case 'd': | 
|  | case 'D': | 
|  | if (len >= 3) | 
|  | { | 
|  | p += 2; | 
|  | base = 10; | 
|  | len -= 2; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | base = 8; | 
|  | break; | 
|  | } | 
|  |  | 
|  | while (len-- > 0) | 
|  | { | 
|  | c = *p++; | 
|  | if (c >= 'A' && c <= 'Z') | 
|  | c += 'a' - 'A'; | 
|  | if (c != 'l' && c != 'u') | 
|  | 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 | 
|  | return ERROR;	/* Char not a digit */ | 
|  | } | 
|  | if (i >= base) | 
|  | return ERROR;		/* Invalid digit in this base.  */ | 
|  |  | 
|  | if (c != 'l' && c != 'u') | 
|  | { | 
|  | /* Test for overflow.  */ | 
|  | if (n == 0 && prevn == 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 = !unsigned_p; | 
|  | bool have_int = long_p == 0; | 
|  | bool have_long = 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 && 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 && 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 (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; | 
|  |  | 
|  | return INT; | 
|  | } | 
|  |  | 
|  | /* 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 == '"') | 
|  | error (_("Unterminated string in expression.")); | 
|  | else | 
|  | error (_("Unmatched single quote.")); | 
|  | } | 
|  | ++tokptr; | 
|  |  | 
|  | value->type = (int) C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/ | 
|  | value->ptr = (char *) obstack_base (&tempbuf); | 
|  | value->length = obstack_object_size (&tempbuf); | 
|  |  | 
|  | *outptr = tokptr; | 
|  |  | 
|  | return quote == '\'' ? CHAR : STRING; | 
|  | } | 
|  |  | 
|  | struct token | 
|  | { | 
|  | const char *oper; | 
|  | int token; | 
|  | enum exp_opcode opcode; | 
|  | }; | 
|  |  | 
|  | static const struct token tokentab3[] = | 
|  | { | 
|  | {">>=", ASSIGN_MODIFY, BINOP_RSH}, | 
|  | {"<<=", ASSIGN_MODIFY, BINOP_LSH}, | 
|  | /*{"&^=", ASSIGN_MODIFY, BINOP_BITWISE_ANDNOT}, TODO */ | 
|  | {"...", DOTDOTDOT, OP_NULL}, | 
|  | }; | 
|  |  | 
|  | 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}, | 
|  | /*{"->", RIGHT_ARROW, OP_NULL}, Doesn't exist in Go.  */ | 
|  | {"<-", LEFT_ARROW, OP_NULL}, | 
|  | {"&&", ANDAND, OP_NULL}, | 
|  | {"||", OROR, OP_NULL}, | 
|  | {"<<", LSH, OP_NULL}, | 
|  | {">>", RSH, OP_NULL}, | 
|  | {"==", EQUAL, OP_NULL}, | 
|  | {"!=", NOTEQUAL, OP_NULL}, | 
|  | {"<=", LEQ, OP_NULL}, | 
|  | {">=", GEQ, OP_NULL}, | 
|  | /*{"&^", ANDNOT, OP_NULL}, TODO */ | 
|  | }; | 
|  |  | 
|  | /* Identifier-like tokens.  */ | 
|  | static const struct token ident_tokens[] = | 
|  | { | 
|  | {"true", TRUE_KEYWORD, OP_NULL}, | 
|  | {"false", FALSE_KEYWORD, OP_NULL}, | 
|  | {"nil", NIL_KEYWORD, OP_NULL}, | 
|  | {"const", CONST_KEYWORD, OP_NULL}, | 
|  | {"struct", STRUCT_KEYWORD, OP_NULL}, | 
|  | {"type", TYPE_KEYWORD, OP_NULL}, | 
|  | {"interface", INTERFACE_KEYWORD, OP_NULL}, | 
|  | {"chan", CHAN_KEYWORD, OP_NULL}, | 
|  | {"byte", BYTE_KEYWORD, OP_NULL}, /* An alias of uint8.  */ | 
|  | {"len", LEN_KEYWORD, OP_NULL}, | 
|  | {"cap", CAP_KEYWORD, OP_NULL}, | 
|  | {"new", NEW_KEYWORD, OP_NULL}, | 
|  | {"iota", IOTA_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 -- either '.' or ARROW.  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: | 
|  |  | 
|  | par_state->prev_lexptr = par_state->lexptr; | 
|  |  | 
|  | tokstart = par_state->lexptr; | 
|  | /* See if it is a special token of length 3.  */ | 
|  | for (const auto &token : tokentab3) | 
|  | if (strncmp (tokstart, token.oper, 3) == 0) | 
|  | { | 
|  | par_state->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) | 
|  | { | 
|  | par_state->lexptr += 2; | 
|  | yylval.opcode = token.opcode; | 
|  | /* NOTE: -> doesn't exist in Go, so we don't need to watch for | 
|  | setting last_was_structop here.  */ | 
|  | return token.token; | 
|  | } | 
|  |  | 
|  | switch (c = *tokstart) | 
|  | { | 
|  | case 0: | 
|  | 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': | 
|  | par_state->lexptr++; | 
|  | goto retry; | 
|  |  | 
|  | case '[': | 
|  | case '(': | 
|  | paren_depth++; | 
|  | par_state->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case ']': | 
|  | case ')': | 
|  | if (paren_depth == 0) | 
|  | return 0; | 
|  | paren_depth--; | 
|  | par_state->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case ',': | 
|  | if (pstate->comma_terminates | 
|  | && paren_depth == 0) | 
|  | return 0; | 
|  | par_state->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case '.': | 
|  | /* Might be a floating point number.  */ | 
|  | if (par_state->lexptr[1] < '0' || par_state->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) | 
|  | { | 
|  | /* This test includes !hex because 'e' is a valid hex digit | 
|  | and thus does not indicate a floating point number when | 
|  | the radix is hex.  */ | 
|  | if (!hex && !got_e && (*p == 'e' || *p == 'E')) | 
|  | got_dot = got_e = 1; | 
|  | /* This test does not include !hex, because a '.' always indicates | 
|  | a decimal floating point number regardless of the radix.  */ | 
|  | else if (!got_dot && *p == '.') | 
|  | got_dot = 1; | 
|  | else if (got_e && (p[-1] == 'e' || p[-1] == 'E') | 
|  | && (*p == '-' || *p == '+')) | 
|  | /* This is the sign of the exponent, not the end of the | 
|  | number.  */ | 
|  | continue; | 
|  | /* We will take any letters or digits.  parse_number will | 
|  | complain if past the radix, or if L or U are not final.  */ | 
|  | else if ((*p < '0' || *p > '9') | 
|  | && ((*p < 'a' || *p > 'z') | 
|  | && (*p < 'A' || *p > 'Z'))) | 
|  | break; | 
|  | } | 
|  | toktype = parse_number (par_state, tokstart, p - tokstart, | 
|  | got_dot|got_e, &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); | 
|  | } | 
|  | par_state->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] != '_') | 
|  | { | 
|  | par_state->lexptr = &p[len]; | 
|  | return ENTRY; | 
|  | } | 
|  | } | 
|  | /* FALLTHRU */ | 
|  | case '+': | 
|  | case '-': | 
|  | case '*': | 
|  | case '/': | 
|  | case '%': | 
|  | case '|': | 
|  | case '&': | 
|  | case '^': | 
|  | case '~': | 
|  | case '!': | 
|  | case '<': | 
|  | case '>': | 
|  | case '?': | 
|  | case ':': | 
|  | case '=': | 
|  | case '{': | 
|  | case '}': | 
|  | symbol: | 
|  | par_state->lexptr++; | 
|  | return c; | 
|  |  | 
|  | case '\'': | 
|  | case '"': | 
|  | case '`': | 
|  | { | 
|  | int host_len; | 
|  | int result = parse_string_or_char (tokstart, &par_state->lexptr, | 
|  | &yylval.tsval, &host_len); | 
|  | if (result == CHAR) | 
|  | { | 
|  | if (host_len == 0) | 
|  | error (_("Empty character constant.")); | 
|  | else if (host_len > 2 && c == '\'') | 
|  | { | 
|  | ++tokstart; | 
|  | namelen = par_state->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.  It doesn't count if it appears in the | 
|  | expansion of a macro.  */ | 
|  | 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. | 
|  | Handle abbreviations of these, similarly to | 
|  | breakpoint.c:find_condition_and_thread. | 
|  | TODO: Watch for "goroutine" here?  */ | 
|  | if (namelen >= 1 | 
|  | && strncmp (tokstart, "thread", 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; | 
|  | } | 
|  |  | 
|  | par_state->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; | 
|  |  | 
|  | if (pstate->parse_completion && *par_state->lexptr == '\0') | 
|  | saw_name_at_eof = 1; | 
|  | return NAME; | 
|  | } | 
|  |  | 
|  | /* An object of this type is pushed on a FIFO by the "outer" lexer.  */ | 
|  | struct token_and_value | 
|  | { | 
|  | int token; | 
|  | YYSTYPE value; | 
|  | }; | 
|  |  | 
|  | /* A FIFO of tokens that have been read but not yet returned to the | 
|  | parser.  */ | 
|  | static std::vector<token_and_value> token_fifo; | 
|  |  | 
|  | /* Non-zero if the lexer should return tokens from the FIFO.  */ | 
|  | static int popping; | 
|  |  | 
|  | /* Temporary storage for yylex; this holds symbol names as they are | 
|  | built up.  */ | 
|  | static auto_obstack name_obstack; | 
|  |  | 
|  | /* Build "package.name" in name_obstack. | 
|  | For convenience of the caller, the name is NUL-terminated, | 
|  | but the NUL is not included in the recorded length.  */ | 
|  |  | 
|  | static struct stoken | 
|  | build_packaged_name (const char *package, int package_len, | 
|  | const char *name, int name_len) | 
|  | { | 
|  | struct stoken result; | 
|  |  | 
|  | name_obstack.clear (); | 
|  | obstack_grow (&name_obstack, package, package_len); | 
|  | obstack_grow_str (&name_obstack, "."); | 
|  | obstack_grow (&name_obstack, name, name_len); | 
|  | obstack_grow (&name_obstack, "", 1); | 
|  | result.ptr = (char *) obstack_base (&name_obstack); | 
|  | result.length = obstack_object_size (&name_obstack) - 1; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /* Return non-zero if NAME is a package name. | 
|  | BLOCK is the scope in which to interpret NAME; this can be NULL | 
|  | to mean the global scope.  */ | 
|  |  | 
|  | static int | 
|  | package_name_p (const char *name, const struct block *block) | 
|  | { | 
|  | struct symbol *sym; | 
|  | struct field_of_this_result is_a_field_of_this; | 
|  |  | 
|  | sym = lookup_symbol (name, block, STRUCT_DOMAIN, &is_a_field_of_this).symbol; | 
|  |  | 
|  | if (sym | 
|  | && sym->aclass () == LOC_TYPEDEF | 
|  | && sym->type ()->code () == TYPE_CODE_MODULE) | 
|  | return 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Classify a (potential) function in the "unsafe" package. | 
|  | We fold these into "keywords" to keep things simple, at least until | 
|  | something more complex is warranted.  */ | 
|  |  | 
|  | static int | 
|  | classify_unsafe_function (struct stoken function_name) | 
|  | { | 
|  | std::string copy = copy_name (function_name); | 
|  |  | 
|  | if (copy == "Sizeof") | 
|  | { | 
|  | yylval.sval = function_name; | 
|  | return SIZEOF_KEYWORD; | 
|  | } | 
|  |  | 
|  | error (_("Unknown function in `unsafe' package: %s"), copy.c_str ()); | 
|  | } | 
|  |  | 
|  | /* Classify token(s) "name1.name2" where name1 is known to be a package. | 
|  | The contents of the token are in `yylval'. | 
|  | Updates yylval and returns the new token type. | 
|  |  | 
|  | The result is one of NAME, NAME_OR_INT, or TYPENAME.  */ | 
|  |  | 
|  | static int | 
|  | classify_packaged_name (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) | 
|  | { | 
|  | yylval.ssym.sym = sym; | 
|  | yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL; | 
|  | } | 
|  |  | 
|  | return NAME; | 
|  | } | 
|  |  | 
|  | /* 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. | 
|  |  | 
|  | The result is one of NAME, NAME_OR_INT, or TYPENAME.  */ | 
|  |  | 
|  | static int | 
|  | classify_name (struct parser_state *par_state, const struct block *block) | 
|  | { | 
|  | struct type *type; | 
|  | struct block_symbol sym; | 
|  | struct field_of_this_result is_a_field_of_this; | 
|  |  | 
|  | std::string copy = copy_name (yylval.sval); | 
|  |  | 
|  | /* Try primitive types first so they win over bad/weird debug info.  */ | 
|  | type = language_lookup_primitive_type (par_state->language (), | 
|  | par_state->gdbarch (), | 
|  | copy.c_str ()); | 
|  | if (type != NULL) | 
|  | { | 
|  | /* NOTE: We take advantage of the fact that yylval coming in was a | 
|  | NAME, and that struct ttype is a compatible extension of struct | 
|  | stoken, so yylval.tsym.stoken is already filled in.  */ | 
|  | yylval.tsym.type = type; | 
|  | return TYPENAME; | 
|  | } | 
|  |  | 
|  | /* TODO: What about other types?  */ | 
|  |  | 
|  | sym = lookup_symbol (copy.c_str (), block, VAR_DOMAIN, &is_a_field_of_this); | 
|  |  | 
|  | if (sym.symbol) | 
|  | { | 
|  | yylval.ssym.sym = sym; | 
|  | yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL; | 
|  | return NAME; | 
|  | } | 
|  |  | 
|  | /* If we didn't find a symbol, look again in the current package. | 
|  | This is to, e.g., make "p global_var" work without having to specify | 
|  | the package name.  We intentionally only looks for objects in the | 
|  | current package.  */ | 
|  |  | 
|  | { | 
|  | char *current_package_name = go_block_package_name (block); | 
|  |  | 
|  | if (current_package_name != NULL) | 
|  | { | 
|  | struct stoken sval = | 
|  | build_packaged_name (current_package_name, | 
|  | strlen (current_package_name), | 
|  | copy.c_str (), copy.size ()); | 
|  |  | 
|  | xfree (current_package_name); | 
|  | sym = lookup_symbol (sval.ptr, block, VAR_DOMAIN, | 
|  | &is_a_field_of_this); | 
|  | if (sym.symbol) | 
|  | { | 
|  | yylval.ssym.stoken = sval; | 
|  | yylval.ssym.sym = sym; | 
|  | yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL; | 
|  | return NAME; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* 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 ((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.symbol = NULL; | 
|  | yylval.ssym.sym.block = NULL; | 
|  | yylval.ssym.is_a_field_of_this = 0; | 
|  | return NAME_OR_INT; | 
|  | } | 
|  | } | 
|  |  | 
|  | yylval.ssym.sym.symbol = NULL; | 
|  | yylval.ssym.sym.block = NULL; | 
|  | yylval.ssym.is_a_field_of_this = 0; | 
|  | return NAME; | 
|  | } | 
|  |  | 
|  | /* This is taken from c-exp.y mostly to get something working. | 
|  | The basic structure has been kept because we may yet need some of it.  */ | 
|  |  | 
|  | static int | 
|  | yylex (void) | 
|  | { | 
|  | token_and_value current, next; | 
|  |  | 
|  | if (popping && !token_fifo.empty ()) | 
|  | { | 
|  | token_and_value tv = token_fifo[0]; | 
|  | token_fifo.erase (token_fifo.begin ()); | 
|  | yylval = tv.value; | 
|  | /* There's no need to fall through to handle package.name | 
|  | as that can never happen here.  In theory.  */ | 
|  | return tv.token; | 
|  | } | 
|  | popping = 0; | 
|  |  | 
|  | current.token = lex_one_token (pstate); | 
|  |  | 
|  | /* TODO: Need a way to force specifying name1 as a package. | 
|  | .name1.name2 ?  */ | 
|  |  | 
|  | if (current.token != NAME) | 
|  | return current.token; | 
|  |  | 
|  | /* See if we have "name1 . name2".  */ | 
|  |  | 
|  | current.value = yylval; | 
|  | next.token = lex_one_token (pstate); | 
|  | next.value = yylval; | 
|  |  | 
|  | if (next.token == '.') | 
|  | { | 
|  | token_and_value name2; | 
|  |  | 
|  | name2.token = lex_one_token (pstate); | 
|  | name2.value = yylval; | 
|  |  | 
|  | if (name2.token == NAME) | 
|  | { | 
|  | /* Ok, we have "name1 . name2".  */ | 
|  | std::string copy = copy_name (current.value.sval); | 
|  |  | 
|  | if (copy == "unsafe") | 
|  | { | 
|  | popping = 1; | 
|  | return classify_unsafe_function (name2.value.sval); | 
|  | } | 
|  |  | 
|  | if (package_name_p (copy.c_str (), pstate->expression_context_block)) | 
|  | { | 
|  | popping = 1; | 
|  | yylval.sval = build_packaged_name (current.value.sval.ptr, | 
|  | current.value.sval.length, | 
|  | name2.value.sval.ptr, | 
|  | name2.value.sval.length); | 
|  | return classify_packaged_name (pstate->expression_context_block); | 
|  | } | 
|  | } | 
|  |  | 
|  | token_fifo.push_back (next); | 
|  | token_fifo.push_back (name2); | 
|  | } | 
|  | else | 
|  | token_fifo.push_back (next); | 
|  |  | 
|  | /* If we arrive here we don't have a package-qualified name.  */ | 
|  |  | 
|  | popping = 1; | 
|  | yylval = current.value; | 
|  | return classify_name (pstate, pstate->expression_context_block); | 
|  | } | 
|  |  | 
|  | /* See language.h.  */ | 
|  |  | 
|  | int | 
|  | go_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; | 
|  |  | 
|  | scoped_restore restore_yydebug = make_scoped_restore (&yydebug, | 
|  | parser_debug); | 
|  |  | 
|  | /* 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); | 
|  | } |