aboutsummaryrefslogtreecommitdiff
path: root/gdb/go-exp.y
diff options
context:
space:
mode:
authorDoug Evans <dje@google.com>2012-04-25 14:07:23 +0000
committerDoug Evans <dje@google.com>2012-04-25 14:07:23 +0000
commita766d390bb857383a5f9ae80a102e1f8705f4c2e (patch)
tree056e6d8d57aee5d234d9ed4619a27306dfec3dd7 /gdb/go-exp.y
parente65f9ffb8e49431537404712a5b1eed310964cb0 (diff)
downloadgdb-a766d390bb857383a5f9ae80a102e1f8705f4c2e.zip
gdb-a766d390bb857383a5f9ae80a102e1f8705f4c2e.tar.gz
gdb-a766d390bb857383a5f9ae80a102e1f8705f4c2e.tar.bz2
Initial pass at Go language support.
* NEWS: Mention Go. * Makefile.in (SFILES): Add go-exp.y, go-lang.c, go-typeprint.c, go-valprint.c. (COMMON_OBS): Add go-lang.o, go-val.print.o, go-typeprint.o. (YYFILES): Add go-exp.c. (YYOBJ): Add go-exp.o. (local-maintainer-clean): Delete go-exp.c. * defs.h (enum language): Add language_go. * dwarf2read.c: #include "go-lang.h". (fixup_go_packaging): New function. (process_full_comp_unit): Call it when processing Go CUs. (dwarf2_physname): Add Go support. (read_file_scope): Handle missing language spec for GNU Go. (set_cu_language): Handle DW_LANG_Go. * go-exp.y: New file. * go-lang.h: New file. * go-lang.c: New file. * go-typeprint.c: New file. * go-valprint.c: New file. * symtab.c: #include "go-lang.h". (symbol_set_language): Handle language_go. (symbol_find_demangled_name, symbol_set_names): Ditto. (symbol_natural_name, demangle_for_lookup, find_main_name): Ditto. testsuite/ * configure.ac: Create gdb.go/Makefile. * configure: Regenerate. * gdb.base/default.exp: Add "go" to "set language" testing. * gdb.go/Makefile.in: New file. * gdb.go/basic-types.exp: New file. * gdb.go/chan.exp: New file. * gdb.go/chan.go: New file. * gdb.go/handcall.exp: New file. * gdb.go/handcall.go: New file. * gdb.go/hello.exp: New file. * gdb.go/hello.go: New file. * gdb.go/integers.exp: New file. * gdb.go/integers.go: New file. * gdb.go/methods.exp: New file. * gdb.go/methods.go: New file. * gdb.go/package.exp: New file. * gdb.go/package1.go: New file. * gdb.go/package2.go: New file. * gdb.go/print.exp: New file. * gdb.go/strings.exp: New file. * gdb.go/strings.go: New file. * gdb.go/types.exp: New file. * gdb.go/types.go: New file. * gdb.go/unsafe.exp: New file. * gdb.go/unsafe.go: New file. * lib/future.exp: Add Go support. (gdb_find_go, gdb_find_go_linker): New procs. (gdb_default_target_compile): Add Go support. * lib/gdb.exp (skip_go_tests): New proc. * lib/go.exp: New file. doc/ * gdb.texinfo (Supported Languages): Add Go. (Go): New node.
Diffstat (limited to 'gdb/go-exp.y')
-rw-r--r--gdb/go-exp.y1618
1 files changed, 1618 insertions, 0 deletions
diff --git a/gdb/go-exp.y b/gdb/go-exp.y
new file mode 100644
index 0000000..f720629
--- /dev/null
+++ b/gdb/go-exp.y
@@ -0,0 +1,1618 @@
+/* YACC parser for Go expressions, for GDB.
+
+ Copyright (C) 2012 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 "gdb_string.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"
+
+#define parse_type builtin_type (parse_gdbarch)
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
+ as well as gratuitiously global symbol names, so we can have multiple
+ yacc generated parsers in gdb. Note that these are only the variables
+ produced by yacc. If other parser generators (bison, byacc, etc) produce
+ additional global names that conflict at link time, then those parser
+ generators need to be fixed instead of adding those names to this list. */
+
+#define yymaxdepth go_maxdepth
+#define yyparse go_parse_internal
+#define yylex go_lex
+#define yyerror go_error
+#define yylval go_lval
+#define yychar go_char
+#define yydebug go_debug
+#define yypact go_pact
+#define yyr1 go_r1
+#define yyr2 go_r2
+#define yydef go_def
+#define yychk go_chk
+#define yypgo go_pgo
+#define yyact go_act
+#define yyexca go_exca
+#define yyerrflag go_errflag
+#define yynerrs go_nerrs
+#define yyps go_ps
+#define yypv go_pv
+#define yys go_s
+#define yy_yys go_yys
+#define yystate go_state
+#define yytmp go_tmp
+#define yyv go_v
+#define yy_yyv go_yyv
+#define yyval go_val
+#define yylloc go_lloc
+#define yyreds go_reds /* With YYDEBUG defined */
+#define yytoks go_toks /* With YYDEBUG defined */
+#define yyname go_name /* With YYDEBUG defined */
+#define yyrule go_rule /* With YYDEBUG defined */
+#define yylhs go_yylhs
+#define yylen go_yylen
+#define yydefred go_yydefred
+#define yydgoto go_yydgoto
+#define yysindex go_yysindex
+#define yyrindex go_yyrindex
+#define yygindex go_yygindex
+#define yytable go_yytable
+#define yycheck go_yycheck
+
+#ifndef YYDEBUG
+#define YYDEBUG 1 /* Default to yydebug support */
+#endif
+
+#define YYFPRINTF parser_fprintf
+
+int yyparse (void);
+
+static int yylex (void);
+
+void yyerror (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 {
+ DOUBLEST dval;
+ 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 (char *, int, int, YYSTYPE *);
+static int parse_go_float (struct gdbarch *gdbarch, const char *p, int len,
+ DOUBLEST *d, struct type **t);
+%}
+
+%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
+ { write_exp_elt_opcode(OP_TYPE);
+ write_exp_elt_type($1);
+ write_exp_elt_opcode(OP_TYPE); }
+ ;
+
+/* Expressions, including the comma operator. */
+exp1 : exp
+ | exp1 ',' exp
+ { write_exp_elt_opcode (BINOP_COMMA); }
+ ;
+
+/* Expressions, not including the comma operator. */
+exp : '*' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_IND); }
+ ;
+
+exp : '&' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_ADDR); }
+ ;
+
+exp : '-' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_NEG); }
+ ;
+
+exp : '+' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_PLUS); }
+ ;
+
+exp : '!' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_LOGICAL_NOT); }
+ ;
+
+exp : '^' exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_COMPLEMENT); }
+ ;
+
+exp : exp INCREMENT %prec UNARY
+ { write_exp_elt_opcode (UNOP_POSTINCREMENT); }
+ ;
+
+exp : exp DECREMENT %prec UNARY
+ { write_exp_elt_opcode (UNOP_POSTDECREMENT); }
+ ;
+
+/* foo->bar is not in Go. May want as a gdb extension. Later. */
+
+exp : exp '.' name_not_typename
+ { write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string ($3.stoken);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+ ;
+
+exp : exp '.' name_not_typename COMPLETE
+ { mark_struct_expression ();
+ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ write_exp_string ($3.stoken);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+ ;
+
+exp : exp '.' COMPLETE
+ { struct stoken s;
+ mark_struct_expression ();
+ write_exp_elt_opcode (STRUCTOP_STRUCT);
+ s.ptr = "";
+ s.length = 0;
+ write_exp_string (s);
+ write_exp_elt_opcode (STRUCTOP_STRUCT); }
+ ;
+
+exp : exp '[' exp1 ']'
+ { write_exp_elt_opcode (BINOP_SUBSCRIPT); }
+ ;
+
+exp : exp '('
+ /* This is to save the value of arglist_len
+ being accumulated by an outer function call. */
+ { start_arglist (); }
+ arglist ')' %prec LEFT_ARROW
+ { write_exp_elt_opcode (OP_FUNCALL);
+ write_exp_elt_longcst ((LONGEST) end_arglist ());
+ write_exp_elt_opcode (OP_FUNCALL); }
+ ;
+
+lcurly : '{'
+ { start_arglist (); }
+ ;
+
+arglist :
+ ;
+
+arglist : exp
+ { arglist_len = 1; }
+ ;
+
+arglist : arglist ',' exp %prec ABOVE_COMMA
+ { arglist_len++; }
+ ;
+
+rcurly : '}'
+ { $$ = end_arglist () - 1; }
+ ;
+
+exp : lcurly type rcurly exp %prec UNARY
+ { write_exp_elt_opcode (UNOP_MEMVAL);
+ write_exp_elt_type ($2);
+ write_exp_elt_opcode (UNOP_MEMVAL); }
+ ;
+
+exp : type '(' exp ')' %prec UNARY
+ { write_exp_elt_opcode (UNOP_CAST);
+ write_exp_elt_type ($1);
+ write_exp_elt_opcode (UNOP_CAST); }
+ ;
+
+exp : '(' exp1 ')'
+ { }
+ ;
+
+/* Binary operators in order of decreasing precedence. */
+
+exp : exp '@' exp
+ { write_exp_elt_opcode (BINOP_REPEAT); }
+ ;
+
+exp : exp '*' exp
+ { write_exp_elt_opcode (BINOP_MUL); }
+ ;
+
+exp : exp '/' exp
+ { write_exp_elt_opcode (BINOP_DIV); }
+ ;
+
+exp : exp '%' exp
+ { write_exp_elt_opcode (BINOP_REM); }
+ ;
+
+exp : exp '+' exp
+ { write_exp_elt_opcode (BINOP_ADD); }
+ ;
+
+exp : exp '-' exp
+ { write_exp_elt_opcode (BINOP_SUB); }
+ ;
+
+exp : exp LSH exp
+ { write_exp_elt_opcode (BINOP_LSH); }
+ ;
+
+exp : exp RSH exp
+ { write_exp_elt_opcode (BINOP_RSH); }
+ ;
+
+exp : exp EQUAL exp
+ { write_exp_elt_opcode (BINOP_EQUAL); }
+ ;
+
+exp : exp NOTEQUAL exp
+ { write_exp_elt_opcode (BINOP_NOTEQUAL); }
+ ;
+
+exp : exp LEQ exp
+ { write_exp_elt_opcode (BINOP_LEQ); }
+ ;
+
+exp : exp GEQ exp
+ { write_exp_elt_opcode (BINOP_GEQ); }
+ ;
+
+exp : exp '<' exp
+ { write_exp_elt_opcode (BINOP_LESS); }
+ ;
+
+exp : exp '>' exp
+ { write_exp_elt_opcode (BINOP_GTR); }
+ ;
+
+exp : exp '&' exp
+ { write_exp_elt_opcode (BINOP_BITWISE_AND); }
+ ;
+
+exp : exp '^' exp
+ { write_exp_elt_opcode (BINOP_BITWISE_XOR); }
+ ;
+
+exp : exp '|' exp
+ { write_exp_elt_opcode (BINOP_BITWISE_IOR); }
+ ;
+
+exp : exp ANDAND exp
+ { write_exp_elt_opcode (BINOP_LOGICAL_AND); }
+ ;
+
+exp : exp OROR exp
+ { write_exp_elt_opcode (BINOP_LOGICAL_OR); }
+ ;
+
+exp : exp '?' exp ':' exp %prec '?'
+ { write_exp_elt_opcode (TERNOP_COND); }
+ ;
+
+exp : exp '=' exp
+ { write_exp_elt_opcode (BINOP_ASSIGN); }
+ ;
+
+exp : exp ASSIGN_MODIFY exp
+ { write_exp_elt_opcode (BINOP_ASSIGN_MODIFY);
+ write_exp_elt_opcode ($2);
+ write_exp_elt_opcode (BINOP_ASSIGN_MODIFY); }
+ ;
+
+exp : INT
+ { write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type ($1.type);
+ write_exp_elt_longcst ((LONGEST)($1.val));
+ write_exp_elt_opcode (OP_LONG); }
+ ;
+
+exp : CHAR
+ {
+ struct stoken_vector vec;
+ vec.len = 1;
+ vec.tokens = &$1;
+ write_exp_string_vector ($1.type, &vec);
+ }
+ ;
+
+exp : NAME_OR_INT
+ { YYSTYPE val;
+ parse_number ($1.stoken.ptr, $1.stoken.length,
+ 0, &val);
+ write_exp_elt_opcode (OP_LONG);
+ write_exp_elt_type (val.typed_val_int.type);
+ write_exp_elt_longcst ((LONGEST)
+ val.typed_val_int.val);
+ write_exp_elt_opcode (OP_LONG);
+ }
+ ;
+
+
+exp : FLOAT
+ { write_exp_elt_opcode (OP_DOUBLE);
+ write_exp_elt_type ($1.type);
+ write_exp_elt_dblcst ($1.dval);
+ write_exp_elt_opcode (OP_DOUBLE); }
+ ;
+
+exp : variable
+ ;
+
+exp : DOLLAR_VARIABLE
+ {
+ write_dollar_variable ($1);
+ }
+ ;
+
+exp : SIZEOF_KEYWORD '(' type ')' %prec UNARY
+ {
+ /* TODO(dje): Go objects in structs. */
+ write_exp_elt_opcode (OP_LONG);
+ /* TODO(dje): What's the right type here? */
+ write_exp_elt_type (parse_type->builtin_unsigned_int);
+ CHECK_TYPEDEF ($3);
+ write_exp_elt_longcst ((LONGEST) TYPE_LENGTH ($3));
+ write_exp_elt_opcode (OP_LONG);
+ }
+ ;
+
+exp : SIZEOF_KEYWORD '(' exp ')' %prec UNARY
+ {
+ /* TODO(dje): Go objects in structs. */
+ write_exp_elt_opcode (UNOP_SIZEOF);
+ }
+
+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 = 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 = realloc ($$.tokens,
+ $$.len * sizeof (struct typed_stoken));
+
+ p = 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;
+
+ write_exp_string_vector (0 /*always utf8*/, &$1);
+ for (i = 0; i < $1.len; ++i)
+ free ($1.tokens[i].ptr);
+ free ($1.tokens);
+ }
+ ;
+
+exp : TRUE_KEYWORD
+ { write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_BOOL); }
+ ;
+
+exp : FALSE_KEYWORD
+ { write_exp_elt_opcode (OP_BOOL);
+ write_exp_elt_longcst ((LONGEST) $1);
+ write_exp_elt_opcode (OP_BOOL); }
+ ;
+
+variable: name_not_typename ENTRY
+ { struct symbol *sym = $1.sym;
+
+ if (sym == NULL
+ || !SYMBOL_IS_ARGUMENT (sym)
+ || !symbol_read_needs_frame (sym))
+ error (_("@entry can be used only for function "
+ "parameters, not for \"%s\""),
+ copy_name ($1.stoken));
+
+ write_exp_elt_opcode (OP_VAR_ENTRY_VALUE);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_ENTRY_VALUE);
+ }
+ ;
+
+variable: name_not_typename
+ { struct symbol *sym = $1.sym;
+
+ if (sym)
+ {
+ if (symbol_read_needs_frame (sym))
+ {
+ if (innermost_block == 0
+ || contained_in (block_found,
+ innermost_block))
+ innermost_block = block_found;
+ }
+
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ /* We want to use the selected frame, not
+ another more inner frame which happens to
+ be in the same block. */
+ write_exp_elt_block (NULL);
+ write_exp_elt_sym (sym);
+ write_exp_elt_opcode (OP_VAR_VALUE);
+ }
+ 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 minimal_symbol *msymbol;
+ char *arg = copy_name ($1.stoken);
+
+ msymbol =
+ lookup_minimal_symbol (arg, NULL, NULL);
+ if (msymbol != NULL)
+ write_exp_msymbol (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_name ($1.stoken));
+ }
+ }
+ ;
+
+/* 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 (parse_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
+*/
+ ;
+
+%%
+
+/* Wrapper on parse_c_float to get the type right for Go. */
+
+static int
+parse_go_float (struct gdbarch *gdbarch, const char *p, int len,
+ DOUBLEST *d, struct type **t)
+{
+ int result = parse_c_float (gdbarch, p, len, d, t);
+ const struct builtin_type *builtin_types = builtin_type (gdbarch);
+ const struct builtin_go_type *builtin_go_types = builtin_go_type (gdbarch);
+
+ if (*t == builtin_types->builtin_float)
+ *t = builtin_go_types->builtin_float32;
+ else if (*t == builtin_types->builtin_double)
+ *t = builtin_go_types->builtin_float64;
+
+ return result;
+}
+
+/* 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 (char *p, int len, int parsed_float, YYSTYPE *putithere)
+{
+ /* FIXME: Shouldn't these be unsigned? We don't deal with negative values
+ here, and we do kind of silly things like cast to unsigned. */
+ LONGEST n = 0;
+ LONGEST prevn = 0;
+ ULONGEST un;
+
+ 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;
+
+ ULONGEST high_bit;
+ struct type *signed_type;
+ struct type *unsigned_type;
+
+ if (parsed_float)
+ {
+ if (! parse_go_float (parse_gdbarch, p, len,
+ &putithere->typed_val_float.dval,
+ &putithere->typed_val_float.type))
+ return ERROR;
+ return FLOAT;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 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;
+
+ 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. */
+
+ /* Portably test for overflow (only works for nonzero values, so make
+ a second check for zero). FIXME: Can't we just make n and prevn
+ unsigned and avoid this? */
+ if (c != 'l' && c != 'u' && (prevn >= n) && n != 0)
+ unsigned_p = 1; /* Try something unsigned. */
+
+ /* Portably test for unsigned overflow.
+ FIXME: This check is wrong; for example it doesn't find overflow
+ on 0x123456789 when LONGEST is 32 bits. */
+ if (c != 'l' && c != 'u' && n != 0)
+ {
+ if ((unsigned_p && (ULONGEST) prevn >= (ULONGEST) 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. */
+
+ un = (ULONGEST)n >> 2;
+ if (long_p == 0
+ && (un >> (gdbarch_int_bit (parse_gdbarch) - 2)) == 0)
+ {
+ high_bit = ((ULONGEST)1) << (gdbarch_int_bit (parse_gdbarch) - 1);
+
+ /* A large decimal (not hex or octal) constant (between INT_MAX
+ and UINT_MAX) is a long or unsigned long, according to ANSI,
+ never an unsigned int, but this code treats it as unsigned
+ int. This probably should be fixed. GCC gives a warning on
+ such constants. */
+
+ unsigned_type = parse_type->builtin_unsigned_int;
+ signed_type = parse_type->builtin_int;
+ }
+ else if (long_p <= 1
+ && (un >> (gdbarch_long_bit (parse_gdbarch) - 2)) == 0)
+ {
+ high_bit = ((ULONGEST)1) << (gdbarch_long_bit (parse_gdbarch) - 1);
+ unsigned_type = parse_type->builtin_unsigned_long;
+ signed_type = parse_type->builtin_long;
+ }
+ else
+ {
+ int shift;
+ if (sizeof (ULONGEST) * HOST_CHAR_BIT
+ < gdbarch_long_long_bit (parse_gdbarch))
+ /* A long long does not fit in a LONGEST. */
+ shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1);
+ else
+ shift = (gdbarch_long_long_bit (parse_gdbarch) - 1);
+ high_bit = (ULONGEST) 1 << shift;
+ unsigned_type = parse_type->builtin_unsigned_long_long;
+ signed_type = parse_type->builtin_long_long;
+ }
+
+ putithere->typed_val_int.val = n;
+
+ /* If the high bit of the worked out type is set then this number
+ has to be unsigned. */
+
+ if (unsigned_p || (n & high_bit))
+ {
+ putithere->typed_val_int.type = unsigned_type;
+ }
+ else
+ {
+ putithere->typed_val_int.type = signed_type;
+ }
+
+ 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 (char *tokptr, 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 = C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/
+ value->ptr = obstack_base (&tempbuf);
+ value->length = obstack_object_size (&tempbuf);
+
+ *outptr = tokptr;
+
+ return quote == '\'' ? CHAR : STRING;
+}
+
+struct token
+{
+ char *operator;
+ 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, BINOP_END},
+ {"--", DECREMENT, BINOP_END},
+ /*{"->", RIGHT_ARROW, BINOP_END}, Doesn't exist in Go. */
+ {"<-", LEFT_ARROW, BINOP_END},
+ {"&&", ANDAND, BINOP_END},
+ {"||", OROR, BINOP_END},
+ {"<<", LSH, BINOP_END},
+ {">>", RSH, BINOP_END},
+ {"==", EQUAL, BINOP_END},
+ {"!=", NOTEQUAL, BINOP_END},
+ {"<=", LEQ, BINOP_END},
+ {">=", GEQ, BINOP_END},
+ /*{"&^", ANDNOT, BINOP_END}, 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;
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+lex_one_token (void)
+{
+ int c;
+ int namelen;
+ unsigned int i;
+ char *tokstart;
+ int saw_structop = last_was_structop;
+ char *copy;
+
+ last_was_structop = 0;
+
+ retry:
+
+ prev_lexptr = lexptr;
+
+ tokstart = lexptr;
+ /* See if it is a special token of length 3. */
+ for (i = 0; i < sizeof (tokentab3) / sizeof (tokentab3[0]); i++)
+ if (strncmp (tokstart, tokentab3[i].operator, 3) == 0)
+ {
+ lexptr += 3;
+ yylval.opcode = tokentab3[i].opcode;
+ return tokentab3[i].token;
+ }
+
+ /* See if it is a special token of length 2. */
+ for (i = 0; i < sizeof (tokentab2) / sizeof (tokentab2[0]); i++)
+ if (strncmp (tokstart, tokentab2[i].operator, 2) == 0)
+ {
+ lexptr += 2;
+ yylval.opcode = tokentab2[i].opcode;
+ /* NOTE: -> doesn't exist in Go, so we don't need to watch for
+ setting last_was_structop here. */
+ return tokentab2[i].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':
+ lexptr++;
+ goto retry;
+
+ case '[':
+ case '(':
+ paren_depth++;
+ lexptr++;
+ return c;
+
+ case ']':
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ lexptr++;
+ return c;
+
+ case ',':
+ if (comma_terminates
+ && paren_depth == 0)
+ return 0;
+ lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (lexptr[1] < '0' || lexptr[1] > '9')
+ {
+ if (in_parse_field)
+ last_was_structop = 1;
+ goto symbol; /* Nope, must be a symbol. */
+ }
+ /* FALL THRU into number case. */
+
+ 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;
+ 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 (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);
+ }
+ lexptr = p;
+ return toktype;
+ }
+
+ case '@':
+ {
+ char *p = &tokstart[1];
+ size_t len = strlen ("entry");
+
+ while (isspace (*p))
+ p++;
+ if (strncmp (p, "entry", len) == 0 && !isalnum (p[len])
+ && p[len] != '_')
+ {
+ lexptr = &p[len];
+ return ENTRY;
+ }
+ }
+ /* FALLTHRU */
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '<':
+ case '>':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ lexptr++;
+ return c;
+
+ case '\'':
+ case '"':
+ case '`':
+ {
+ int host_len;
+ int result = parse_string_or_char (tokstart, &lexptr, &yylval.tsval,
+ &host_len);
+ if (result == CHAR)
+ {
+ if (host_len == 0)
+ error (_("Empty character constant."));
+ else if (host_len > 2 && c == '\'')
+ {
+ ++tokstart;
+ namelen = 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'))
+ {
+ char *p = tokstart + namelen + 1;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p >= '0' && *p <= '9')
+ return 0;
+ }
+
+ lexptr += namelen;
+
+ tryname:
+
+ yylval.sval.ptr = tokstart;
+ yylval.sval.length = namelen;
+
+ /* Catch specific keywords. */
+ copy = copy_name (yylval.sval);
+ for (i = 0; i < sizeof (ident_tokens) / sizeof (ident_tokens[0]); i++)
+ if (strcmp (copy, ident_tokens[i].operator) == 0)
+ {
+ /* It is ok to always set this, even though we don't always
+ strictly need to. */
+ yylval.opcode = ident_tokens[i].opcode;
+ return ident_tokens[i].token;
+ }
+
+ if (*tokstart == '$')
+ return DOLLAR_VARIABLE;
+
+ if (in_parse_field && *lexptr == '\0')
+ saw_name_at_eof = 1;
+ return NAME;
+}
+
+/* An object of this type is pushed on a FIFO by the "outer" lexer. */
+typedef struct
+{
+ int token;
+ YYSTYPE value;
+} token_and_value;
+
+DEF_VEC_O (token_and_value);
+
+/* A FIFO of tokens that have been read but not yet returned to the
+ parser. */
+static VEC (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 struct 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;
+
+ obstack_free (&name_obstack, obstack_base (&name_obstack));
+ 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 = 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, struct block *block)
+{
+ struct symbol *sym;
+ int is_a_field_of_this;
+
+ sym = lookup_symbol (name, block, STRUCT_DOMAIN, &is_a_field_of_this);
+
+ if (sym
+ && SYMBOL_CLASS (sym) == LOC_TYPEDEF
+ && TYPE_CODE (SYMBOL_TYPE (sym)) == 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)
+{
+ char *copy = copy_name (function_name);
+
+ if (strcmp (copy, "Sizeof") == 0)
+ {
+ yylval.sval = function_name;
+ return SIZEOF_KEYWORD;
+ }
+
+ error (_("Unknown function in `unsafe' package: %s"), copy);
+}
+
+/* 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 (struct block *block)
+{
+ char *copy;
+ struct symbol *sym;
+ int is_a_field_of_this = 0;
+
+ copy = copy_name (yylval.sval);
+
+ sym = lookup_symbol (copy, block, VAR_DOMAIN, &is_a_field_of_this);
+
+ if (sym)
+ {
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ }
+
+ 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 block *block)
+{
+ struct type *type;
+ struct symbol *sym;
+ char *copy;
+ int is_a_field_of_this = 0;
+
+ copy = copy_name (yylval.sval);
+
+ /* Try primitive types first so they win over bad/weird debug info. */
+ type = language_lookup_primitive_type_by_name (parse_language,
+ parse_gdbarch, copy);
+ 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, block, VAR_DOMAIN, &is_a_field_of_this);
+
+ if (sym)
+ {
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ 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, strlen (copy));
+
+ xfree (current_package_name);
+ sym = lookup_symbol (sval.ptr, block, VAR_DOMAIN,
+ &is_a_field_of_this);
+ if (sym)
+ {
+ yylval.sval = sval;
+ yylval.ssym.sym = sym;
+ yylval.ssym.is_a_field_of_this = is_a_field_of_this;
+ 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 (copy, yylval.sval.length, 0, &newlval);
+ if (hextype == INT)
+ return NAME_OR_INT;
+ }
+
+ 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 && !VEC_empty (token_and_value, token_fifo))
+ {
+ token_and_value tv = *VEC_index (token_and_value, token_fifo, 0);
+ VEC_ordered_remove (token_and_value, token_fifo, 0);
+ 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 ();
+
+ /* 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 ();
+ next.value = yylval;
+
+ if (next.token == '.')
+ {
+ token_and_value name2;
+
+ name2.token = lex_one_token ();
+ name2.value = yylval;
+
+ if (name2.token == NAME)
+ {
+ /* Ok, we have "name1 . name2". */
+ int token;
+ char *copy;
+
+ copy = copy_name (current.value.sval);
+
+ if (strcmp (copy, "unsafe") == 0)
+ {
+ popping = 1;
+ return classify_unsafe_function (name2.value.sval);
+ }
+
+ if (package_name_p (copy, 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 (expression_context_block);
+ }
+ }
+
+ VEC_safe_push (token_and_value, token_fifo, &next);
+ VEC_safe_push (token_and_value, token_fifo, &name2);
+ }
+ else
+ {
+ VEC_safe_push (token_and_value, token_fifo, &next);
+ }
+
+ /* If we arrive here we don't have a package-qualified name. */
+
+ popping = 1;
+ yylval = current.value;
+ return classify_name (expression_context_block);
+}
+
+int
+go_parse (void)
+{
+ int result;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ make_cleanup_restore_integer (&yydebug);
+ yydebug = parser_debug;
+
+ /* Initialize some state used by the lexer. */
+ last_was_structop = 0;
+ saw_name_at_eof = 0;
+
+ VEC_free (token_and_value, token_fifo);
+ popping = 0;
+ obstack_init (&name_obstack);
+ make_cleanup_obstack_free (&name_obstack);
+
+ result = yyparse ();
+ do_cleanups (back_to);
+ return result;
+}
+
+void
+yyerror (char *msg)
+{
+ if (prev_lexptr)
+ lexptr = prev_lexptr;
+
+ error (_("A %s in expression, near `%s'."), (msg ? msg : "error"), lexptr);
+}