Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: simon.marchi@polymtl.ca
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@polymtl.ca>
Subject: [PATCH 15/17] gdb: move go-exp-parser.y's support code to go-exp-parser.c
Date: Fri,  4 Sep 2026 12:56:47 -0400	[thread overview]
Message-ID: <20260904170338.1643894-16-simon.marchi@polymtl.ca> (raw)
In-Reply-To: <20260904170338.1643894-1-simon.marchi@polymtl.ca>

From: Simon Marchi <simon.marchi@polymtl.ca>

Similar to the previous commits, but for the Go expression parser.

Like the Fortran parser, the Go parser is entered through the
go_language::parser method rather than a free function, so add a free
function go_parse as the entry point (like the other parsers) and turn
go_language::parser into a thin wrapper around it, defined in go-lang.c.

Put the parser support code inside the go_exp_parser namespace.

Change-Id: Icbe5290d86e783d979e4301ce55ef256d59fbea1
---
 gdb/Makefile.in     |   2 +
 gdb/go-exp-parser.c | 955 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/go-exp-parser.h |  59 +++
 gdb/go-exp-parser.y | 936 +------------------------------------------
 gdb/go-lang.c       |   9 +
 5 files changed, 1028 insertions(+), 933 deletions(-)
 create mode 100644 gdb/go-exp-parser.c
 create mode 100644 gdb/go-exp-parser.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0a35f9507def..4289c5151fd0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1120,6 +1120,7 @@ COMMON_SFILES = \
 	gmp-utils.c \
 	gnu-v2-abi.c \
 	gnu-v3-abi.c \
+	go-exp-parser.c \
 	go-lang.c \
 	go-typeprint.c \
 	go-valprint.c \
@@ -1482,6 +1483,7 @@ HFILES_NO_SRCDIR = \
 	gmp-utils.h \
 	gnu-nat.h \
 	gnu-nat-mig.h \
+	go-exp-parser.h \
 	go-lang.h \
 	gregset.h \
 	guile/guile.h \
diff --git a/gdb/go-exp-parser.c b/gdb/go-exp-parser.c
new file mode 100644
index 000000000000..96418dfae4fc
--- /dev/null
+++ b/gdb/go-exp-parser.c
@@ -0,0 +1,955 @@
+/* YACC parser support code for Go expressions, for GDB.
+
+   Copyright (C) 2012-2026 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/>.  */
+
+#include "go-exp-parser.h"
+#include "go-exp-parser-gen.h"
+#include "block.h"
+#include "c-exp-parser.h"
+#include "c-lang.h"
+#include "charset.h"
+#include "go-lang.h"
+#include "language.h"
+#include "parser-defs.h"
+#include "value.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+   go-exp-parser-gen.c.  Bison produces a declaration for go_yyparse in
+   go-exp-parser-gen.h, but byacc does not, hence this declaration.  */
+
+int go_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for go_yydebug.  */
+
+extern int go_yydebug;
+
+namespace go_exp_parser
+{
+
+/* See go-exp-parser.h.  */
+
+parser_state *pstate;
+
+/* See go-exp-parser.h.  */
+
+int
+parse_number (struct parser_state *par_state,
+	      const char *p, int len, int parsed_float,
+	      go_exp_parser_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 && c_tolower (p[len - 1]) == 'f')
+	{
+	  putithere->typed_val_float.type
+	    = builtin_go_types->builtin_float32;
+	  len--;
+	}
+      else if (len >= 1 && c_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 go_token
+{
+  const char *oper;
+  int token;
+  enum exp_opcode opcode;
+};
+
+static const struct go_token tokentab3[] =
+  {
+    {">>=", ASSIGN_MODIFY, BINOP_RSH},
+    {"<<=", ASSIGN_MODIFY, BINOP_LSH},
+    /*{"&^=", ASSIGN_MODIFY, BINOP_BITWISE_ANDNOT}, TODO */
+    {"...", DOTDOTDOT, OP_NULL},
+  };
+
+static const struct go_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 go_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;
+	go_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;
+	go_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. */
+	}
+      [[fallthrough]];
+
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      {
+	/* It's a number.  */
+	int got_dot = 0, got_e = 0, 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, &go_yylval);
+	if (toktype == ERROR)
+	  error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+		 tokstart);
+	par_state->lexptr = p;
+	return toktype;
+      }
+
+    case '@':
+      {
+	const char *p = &tokstart[1];
+	size_t len = strlen ("entry");
+
+	while (c_isspace (*p))
+	  p++;
+	if (strncmp (p, "entry", len) == 0 && !c_isalnum (p[len])
+	    && p[len] != '_')
+	  {
+	    par_state->lexptr = &p[len];
+	    return ENTRY;
+	  }
+      }
+      [[fallthrough]];
+    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,
+					   &go_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 = skip_spaces (tokstart + namelen + 1);
+      if (*p >= '0' && *p <= '9')
+	return 0;
+    }
+
+  par_state->lexptr += namelen;
+
+  tryname:
+
+  go_yylval.sval.ptr = tokstart;
+  go_yylval.sval.length = namelen;
+
+  /* Catch specific keywords.  */
+  std::string copy = copy_name (go_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.  */
+	go_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 go_token_and_value
+{
+  int token;
+  go_exp_parser_YYSTYPE value;
+};
+
+/* A FIFO of tokens that have been read but not yet returned to the
+   parser.  */
+static std::vector<go_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, SEARCH_TYPE_DOMAIN,
+		       &is_a_field_of_this).symbol;
+
+  if (sym
+      && sym->loc_class () == 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")
+    {
+      go_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 (go_yylval.sval);
+
+  sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
+
+  if (sym.symbol)
+    {
+      go_yylval.ssym.sym = sym;
+      go_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 (go_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.  */
+      go_yylval.tsym.type = type;
+      return TYPENAME;
+    }
+
+  /* TODO: What about other types?  */
+
+  sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
+
+  if (sym.symbol)
+    {
+      go_yylval.ssym.sym = sym;
+      go_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.  */
+
+  {
+    gdb::unique_xmalloc_ptr<char> current_package_name
+      = go_block_package_name (block);
+
+    if (current_package_name != NULL)
+      {
+	struct stoken sval =
+	  build_packaged_name (current_package_name.get (),
+			       strlen (current_package_name.get ()),
+			       copy.c_str (), copy.size ());
+
+	sym = lookup_symbol (sval.ptr, block, SEARCH_VFT,
+			     &is_a_field_of_this);
+	if (sym.symbol)
+	  {
+	    go_yylval.ssym.stoken = sval;
+	    go_yylval.ssym.sym = sym;
+	    go_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))
+    {
+      go_exp_parser_YYSTYPE newlval;	/* Its value is ignored.  */
+      int hextype = parse_number (par_state, copy.c_str (),
+				  go_yylval.sval.length, 0, &newlval);
+      if (hextype == INT)
+	{
+	  go_yylval.ssym.sym.symbol = NULL;
+	  go_yylval.ssym.sym.block = NULL;
+	  go_yylval.ssym.is_a_field_of_this = 0;
+	  return NAME_OR_INT;
+	}
+    }
+
+  go_yylval.ssym.sym.symbol = NULL;
+  go_yylval.ssym.sym.block = NULL;
+  go_yylval.ssym.is_a_field_of_this = 0;
+  return NAME;
+}
+
+/* See go-exp-parser.h.  */
+
+int
+go_yylex (void)
+{
+  go_token_and_value current, next;
+
+  if (popping && !token_fifo.empty ())
+    {
+      go_token_and_value tv = token_fifo[0];
+      token_fifo.erase (token_fifo.begin ());
+      go_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 = go_yylval;
+  next.token = lex_one_token (pstate);
+  next.value = go_yylval;
+
+  if (next.token == '.')
+    {
+      go_token_and_value name2;
+
+      name2.token = lex_one_token (pstate);
+      name2.value = go_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;
+	      go_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;
+  go_yylval = current.value;
+  return classify_name (pstate, pstate->expression_context_block);
+}
+
+/* See go-exp-parser.h.  */
+
+void
+go_yyerror (const char *msg)
+{
+  pstate->parse_error (msg);
+}
+
+} /* namespace go_exp_parser */
+
+/* See go-exp-parser.h.  */
+
+int
+go_parse (struct parser_state *par_state)
+{
+  using namespace go_exp_parser;
+
+  /* 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 (&go_yydebug,
+							par_state->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 = go_yyparse ();
+  if (!result)
+    pstate->set_operation (pstate->pop ());
+  return result;
+}
diff --git a/gdb/go-exp-parser.h b/gdb/go-exp-parser.h
new file mode 100644
index 000000000000..525f02d7f792
--- /dev/null
+++ b/gdb/go-exp-parser.h
@@ -0,0 +1,59 @@
+/* YACC parser support code for Go expressions, for GDB.
+
+   Copyright (C) 2012-2026 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/>.  */
+
+#ifndef GDB_GO_EXP_PARSER_H
+#define GDB_GO_EXP_PARSER_H
+
+#include "parser-defs.h"
+
+union go_exp_parser_YYSTYPE;
+
+namespace go_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+   expression.  */
+
+extern parser_state *pstate;
+
+/* 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.  */
+
+int parse_number (struct parser_state *par_state, const char *p, int len,
+		  int parsed_float, go_exp_parser_YYSTYPE *putithere);
+
+/* This is taken from c-exp-parser.y mostly to get something working.
+   The basic structure has been kept because we may yet need some of it.  */
+
+int go_yylex ();
+
+/* The error handler invoked by the generated parser.  Report MSG as a
+   parse error on the current parser state.  */
+
+void go_yyerror (const char *msg);
+
+} /* namespace go_exp_parser */
+
+/* Parse a Go expression using the lexer input and context held in
+   PAR_STATE.  On success, return 0 and leave the resulting operation set
+   on PAR_STATE.  On failure, return non-zero.  */
+
+int go_parse (struct parser_state *par_state);
+
+#endif /* GDB_GO_EXP_PARSER_H */
diff --git a/gdb/go-exp-parser.y b/gdb/go-exp-parser.y
index 2ae357081b36..c7f4e4f2da2e 100644
--- a/gdb/go-exp-parser.y
+++ b/gdb/go-exp-parser.y
@@ -55,23 +55,13 @@
 #include "value.h"
 #include "parser-defs.h"
 #include "language.h"
-#include "c-lang.h"
-#include "c-exp-parser.h"
 #include "go-lang.h"
-#include "charset.h"
+#include "go-exp-parser.h"
 #include "block.h"
 #include "expop.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 *);
+using namespace go_exp_parser;
+using namespace expr;
 
 %}
 
@@ -101,14 +91,6 @@ static void yyerror (const char *);
     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
@@ -619,915 +601,3 @@ name_not_typename
 	|	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-parser.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-parser.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 && c_tolower (p[len - 1]) == 'f')
-	{
-	  putithere->typed_val_float.type
-	    = builtin_go_types->builtin_float32;
-	  len--;
-	}
-      else if (len >= 1 && c_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 go_token
-{
-  const char *oper;
-  int token;
-  enum exp_opcode opcode;
-};
-
-static const struct go_token tokentab3[] =
-  {
-    {">>=", ASSIGN_MODIFY, BINOP_RSH},
-    {"<<=", ASSIGN_MODIFY, BINOP_LSH},
-    /*{"&^=", ASSIGN_MODIFY, BINOP_BITWISE_ANDNOT}, TODO */
-    {"...", DOTDOTDOT, OP_NULL},
-  };
-
-static const struct go_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 go_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. */
-	}
-      [[fallthrough]];
-
-    case '0':
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
-    case '8':
-    case '9':
-      {
-	/* It's a number.  */
-	int got_dot = 0, got_e = 0, 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)
-	  error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
-		 tokstart);
-	par_state->lexptr = p;
-	return toktype;
-      }
-
-    case '@':
-      {
-	const char *p = &tokstart[1];
-	size_t len = strlen ("entry");
-
-	while (c_isspace (*p))
-	  p++;
-	if (strncmp (p, "entry", len) == 0 && !c_isalnum (p[len])
-	    && p[len] != '_')
-	  {
-	    par_state->lexptr = &p[len];
-	    return ENTRY;
-	  }
-      }
-      [[fallthrough]];
-    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 = skip_spaces (tokstart + namelen + 1);
-      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 go_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<go_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, SEARCH_TYPE_DOMAIN,
-		       &is_a_field_of_this).symbol;
-
-  if (sym
-      && sym->loc_class () == 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, SEARCH_VFT, &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, SEARCH_VFT, &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.  */
-
-  {
-    gdb::unique_xmalloc_ptr<char> current_package_name
-      = go_block_package_name (block);
-
-    if (current_package_name != NULL)
-      {
-	struct stoken sval =
-	  build_packaged_name (current_package_name.get (),
-			       strlen (current_package_name.get ()),
-			       copy.c_str (), copy.size ());
-
-	sym = lookup_symbol (sval.ptr, block, SEARCH_VFT,
-			     &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-parser.y mostly to get something working.
-   The basic structure has been kept because we may yet need some of it.  */
-
-static int
-yylex (void)
-{
-  go_token_and_value current, next;
-
-  if (popping && !token_fifo.empty ())
-    {
-      go_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 == '.')
-    {
-      go_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,
-							par_state->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)
-{
-  pstate->parse_error (msg);
-}
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 3b388c961fab..618e51cb40dd 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -37,6 +37,7 @@
 #include "language.h"
 #include "varobj.h"
 #include "go-lang.h"
+#include "go-exp-parser.h"
 #include "c-lang.h"
 #include "parser-defs.h"
 #include "gdbarch.h"
@@ -456,6 +457,14 @@ go_block_package_name (const struct block *block)
 
 /* See language.h.  */
 
+int
+go_language::parser (struct parser_state *ps) const
+{
+  return go_parse (ps);
+}
+
+/* See language.h.  */
+
 void
 go_language::language_arch_info (struct gdbarch *gdbarch,
 				 struct language_arch_info *lai) const
-- 
2.55.0


  parent reply	other threads:[~2026-09-04 17:16 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-04 16:56 [PATCH 00/17] Move C++ support code out of .y files simon.marchi
2026-09-04 16:56 ` [PATCH 01/17] gdb/ada-exp-parser: remove name_info struct simon.marchi
2026-09-04 16:56 ` [PATCH 02/17] gdb: replace parse_type macros with functions simon.marchi
2026-09-04 16:56 ` [PATCH 03/17] gdb: suffix flex/bison output files with -gen.c simon.marchi
2026-09-04 16:56 ` [PATCH 04/17] gdb: move parser output post-processing to a script simon.marchi
2026-09-05  0:38   ` Kevin Buettner
2026-09-05  3:59     ` Simon Marchi
2026-09-04 16:56 ` [PATCH 05/17] gdb: let the parser and lexer generators prefix their symbols simon.marchi
2026-09-04 16:56 ` [PATCH 06/17] gdb: separate cp-name-parser's symbol prefix with an underscore simon.marchi
2026-09-04 16:56 ` [PATCH 07/17] gdb: make $(YACC) and $(FLEX) generate headers simon.marchi
2026-09-04 16:56 ` [PATCH 08/17] gdb: add check for stale build generated files simon.marchi
2026-09-04 16:56 ` [PATCH 09/17] gdb: move cp-name-parser.y's support code to cp-name-parser.c simon.marchi
2026-09-04 16:56 ` [PATCH 10/17] gdb: rename LANG-exp.y to LANG-exp-parser.y simon.marchi
2026-09-04 16:56 ` [PATCH 11/17] gdb: move c-exp-parser.y's support code to c-exp-parser.c simon.marchi
2026-09-04 16:56 ` [PATCH 12/17] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c simon.marchi
2026-09-05  0:16   ` Kevin Buettner
2026-09-04 16:56 ` [PATCH 13/17] gdb: move d-exp-parser.y's support code to d-exp-parser.c simon.marchi
2026-09-04 16:56 ` [PATCH 14/17] gdb: move f-exp-parser.y's support code to f-exp-parser.c simon.marchi
2026-09-04 16:56 ` simon.marchi [this message]
2026-09-04 16:56 ` [PATCH 16/17] gdb: move m2-exp-parser.y's support code to m2-exp-parser.c simon.marchi
2026-09-05  0:30   ` Kevin Buettner
2026-09-05  4:04     ` Simon Marchi
2026-09-04 16:56 ` [PATCH 17/17] gdb: move p-exp-parser.y's support code to p-exp-parser.c simon.marchi
2026-09-05  0:29   ` Kevin Buettner
2026-09-05  0:50 ` [PATCH 00/17] Move C++ support code out of .y files Kevin Buettner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260904170338.1643894-16-simon.marchi@polymtl.ca \
    --to=simon.marchi@polymtl.ca \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox