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 v2 14/19] gdb: move d-exp-parser.y's support code to d-exp-parser.c
Date: Sat,  5 Sep 2026 00:23:17 -0400	[thread overview]
Message-ID: <20260905042353.1702204-15-simon.marchi@polymtl.ca> (raw)
In-Reply-To: <20260905042353.1702204-1-simon.marchi@polymtl.ca>

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

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

One wrinkle that was not in previous commits is the type_stack global.
Once it moves into the d_exp_parser namespace and is brought in with a
using-directive, its name clashes with the struct type_stack type.  Use a
using declaration specifically for it in the .y prologue to restore the
original name hiding, so the grammar actions can work unmodified.

Put the parser support code inside the d_exp_parser namespace.

Change-Id: I8d45f1ba606fa8748976f34f39808703c8f6843d
---
 gdb/Makefile.in    |    2 +
 gdb/d-exp-parser.c | 1050 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/d-exp-parser.h |   83 ++++
 gdb/d-exp-parser.y | 1043 +------------------------------------------
 gdb/d-lang.c       |    1 +
 gdb/d-lang.h       |    4 -
 6 files changed, 1142 insertions(+), 1041 deletions(-)
 create mode 100644 gdb/d-exp-parser.c
 create mode 100644 gdb/d-exp-parser.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d2cfecb0def9..f861b1f53261 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1084,6 +1084,7 @@ COMMON_SFILES = \
 	cp-support.c \
 	cp-valprint.c \
 	ctfread.c \
+	d-exp-parser.c \
 	d-lang.c \
 	d-namespace.c \
 	d-valprint.c \
@@ -1395,6 +1396,7 @@ HFILES_NO_SRCDIR = \
 	disasm.h \
 	disasm-selftests.h \
 	displaced-stepping.h \
+	d-exp-parser.h \
 	d-lang.h \
 	dummy-frame.h \
 	dwarf2/abbrev.h \
diff --git a/gdb/d-exp-parser.c b/gdb/d-exp-parser.c
new file mode 100644
index 000000000000..e6b5f3af8498
--- /dev/null
+++ b/gdb/d-exp-parser.c
@@ -0,0 +1,1050 @@
+/* Support code for the D expression parser, for GDB.
+
+   Copyright (C) 2014-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 "d-exp-parser.h"
+#include "d-exp-parser-gen.h"
+#include "block.h"
+#include "c-exp-parser.h"
+#include "c-lang.h"
+#include "charset.h"
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+   d-exp-parser-gen.c.  Bison produces a declaration for d_yyparse in
+   d-exp-parser-gen.h, but byacc does not, hence this declaration.  */
+
+int d_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for d_yydebug.  */
+
+extern int d_yydebug;
+
+namespace d_exp_parser
+{
+
+/* See d-exp-parser.h.  */
+
+parser_state *pstate;
+
+/* See d-exp-parser.h.  */
+
+struct type_stack *type_stack;
+
+/* See d-exp-parser.h.  */
+
+int
+type_aggregate_p (struct type *type)
+{
+  return (type->code () == TYPE_CODE_STRUCT
+	  || type->code () == TYPE_CODE_UNION
+	  || type->code () == TYPE_CODE_MODULE
+	  || (type->code () == TYPE_CODE_ENUM
+	      && type->is_declared_class ()));
+}
+
+/* See d-exp-parser.h.  */
+
+int
+parse_number (struct parser_state *ps, const char *p,
+	      int len, int parsed_float, d_exp_parser_YYSTYPE *putithere)
+{
+  ULONGEST n = 0;
+  ULONGEST prevn = 0;
+  ULONGEST un;
+
+  int i = 0;
+  int c;
+  int base = input_radix;
+  int unsigned_p = 0;
+  int long_p = 0;
+
+  /* We have found a "L" or "U" suffix.  */
+  int found_suffix = 0;
+
+  ULONGEST high_bit;
+  struct type *signed_type;
+  struct type *unsigned_type;
+
+  if (parsed_float)
+    {
+      char *s, *sp;
+
+      /* Strip out all embedded '_' before passing to parse_float.  */
+      s = (char *) alloca (len + 1);
+      sp = s;
+      while (len-- > 0)
+	{
+	  if (*p != '_')
+	    *sp++ = *p;
+	  p++;
+	}
+      *sp = '\0';
+      len = strlen (s);
+
+      /* Check suffix for `i' , `fi' or `li' (idouble, ifloat or ireal).  */
+      if (len >= 1 && c_tolower (s[len - 1]) == 'i')
+	{
+	  if (len >= 2 && c_tolower (s[len - 2]) == 'f')
+	    {
+	      putithere->typed_val_float.type
+		= parse_d_type (ps)->builtin_ifloat;
+	      len -= 2;
+	    }
+	  else if (len >= 2 && c_tolower (s[len - 2]) == 'l')
+	    {
+	      putithere->typed_val_float.type
+		= parse_d_type (ps)->builtin_ireal;
+	      len -= 2;
+	    }
+	  else
+	    {
+	      putithere->typed_val_float.type
+		= parse_d_type (ps)->builtin_idouble;
+	      len -= 1;
+	    }
+	}
+      /* Check suffix for `f' or `l'' (float or real).  */
+      else if (len >= 1 && c_tolower (s[len - 1]) == 'f')
+	{
+	  putithere->typed_val_float.type
+	    = parse_d_type (ps)->builtin_float;
+	  len -= 1;
+	}
+      else if (len >= 1 && c_tolower (s[len - 1]) == 'l')
+	{
+	  putithere->typed_val_float.type
+	    = parse_d_type (ps)->builtin_real;
+	  len -= 1;
+	}
+      /* Default type if no suffix.  */
+      else
+	{
+	  putithere->typed_val_float.type
+	    = parse_d_type (ps)->builtin_double;
+	}
+
+      if (!parse_float (s, len,
+			putithere->typed_val_float.type,
+			putithere->typed_val_float.val))
+	return ERROR;
+
+      return FLOAT_LITERAL;
+    }
+
+  /* Handle base-switching prefixes 0x, 0b, 0 */
+  if (p[0] == '0')
+    switch (p[1])
+      {
+      case 'x':
+      case 'X':
+	if (len >= 3)
+	  {
+	    p += 2;
+	    base = 16;
+	    len -= 2;
+	  }
+	break;
+
+      case 'b':
+      case 'B':
+	if (len >= 3)
+	  {
+	    p += 2;
+	    base = 2;
+	    len -= 2;
+	  }
+	break;
+
+      default:
+	base = 8;
+	break;
+      }
+
+  while (len-- > 0)
+    {
+      c = *p++;
+      if (c == '_')
+	continue;	/* Ignore embedded '_'.  */
+      if (c >= 'A' && c <= 'Z')
+	c += 'a' - 'A';
+      if (c != 'l' && c != 'u')
+	n *= base;
+      if (c >= '0' && c <= '9')
+	{
+	  if (found_suffix)
+	    return ERROR;
+	  n += i = c - '0';
+	}
+      else
+	{
+	  if (base > 10 && c >= 'a' && c <= 'f')
+	    {
+	      if (found_suffix)
+		return ERROR;
+	      n += i = c - 'a' + 10;
+	    }
+	  else if (c == 'l' && long_p == 0)
+	    {
+	      long_p = 1;
+	      found_suffix = 1;
+	    }
+	  else if (c == 'u' && unsigned_p == 0)
+	    {
+	      unsigned_p = 1;
+	      found_suffix = 1;
+	    }
+	  else
+	    return ERROR;	/* Char not a digit */
+	}
+      if (i >= base)
+	return ERROR;		/* Invalid digit in this base.  */
+      /* Portably test for integer overflow.  */
+      if (c != 'l' && c != 'u')
+	{
+	  ULONGEST n2 = prevn * base;
+	  if ((n2 / base != prevn) || (n2 + i < prevn))
+	    error (_("Numeric constant too large."));
+	}
+      prevn = n;
+    }
+
+  /* An integer constant is an int or a long.  An L suffix forces it to
+     be long, and a U suffix forces it to be unsigned.  To figure out
+     whether it fits, we shift it right and see whether anything remains.
+     Note that we can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or
+     more in one operation, because many compilers will warn about such a
+     shift (which always produces a zero result).  To deal with the case
+     where it is we just always shift the value more than once, with fewer
+     bits each time.  */
+  un = (ULONGEST) n >> 2;
+  if (long_p == 0 && (un >> 30) == 0)
+    {
+      high_bit = ((ULONGEST) 1) << 31;
+      signed_type = parse_d_type (ps)->builtin_int;
+      /* For decimal notation, keep the sign of the worked out type.  */
+      if (base == 10 && !unsigned_p)
+	unsigned_type = parse_d_type (ps)->builtin_long;
+      else
+	unsigned_type = parse_d_type (ps)->builtin_uint;
+    }
+  else
+    {
+      int shift;
+      if (sizeof (ULONGEST) * HOST_CHAR_BIT < 64)
+	/* A long long does not fit in a LONGEST.  */
+	shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1);
+      else
+	shift = 63;
+      high_bit = (ULONGEST) 1 << shift;
+      signed_type = parse_d_type (ps)->builtin_long;
+      unsigned_type = parse_d_type (ps)->builtin_ulong;
+    }
+
+  putithere->typed_val_int.val = n;
+
+  /* If the high bit of the worked out type is set then this number
+     has to be unsigned_type.  */
+  if (unsigned_p || (n & high_bit))
+    putithere->typed_val_int.type = unsigned_type;
+  else
+    putithere->typed_val_int.type = signed_type;
+
+  return INTEGER_LITERAL;
+}
+
+/* Temporary obstack used for holding strings.  */
+static struct obstack tempbuf;
+static int tempbuf_init;
+
+/* Parse a string or character literal from TOKPTR.  The string or
+   character may be wide or unicode.  *OUTPTR is set to just after the
+   end of the literal in the input string.  The resulting token is
+   stored in VALUE.  This returns a token value, either STRING or
+   CHAR, depending on what was parsed.  *HOST_CHARS is set to the
+   number of host characters in the literal.  */
+
+static int
+parse_string_or_char (const char *tokptr, const char **outptr,
+		      struct typed_stoken *value, int *host_chars)
+{
+  int quote;
+
+  /* Build the gdb internal form of the input string in tempbuf.  Note
+     that the buffer is null byte terminated *only* for the
+     convenience of debugging gdb itself and printing the buffer
+     contents when the buffer contains no embedded nulls.  Gdb does
+     not depend upon the buffer being null byte terminated, it uses
+     the length string instead.  This allows gdb to handle C strings
+     (as well as strings in other languages) with embedded null
+     bytes */
+
+  if (!tempbuf_init)
+    tempbuf_init = 1;
+  else
+    obstack_free (&tempbuf, NULL);
+  obstack_init (&tempbuf);
+
+  /* Skip the quote.  */
+  quote = *tokptr;
+  ++tokptr;
+
+  *host_chars = 0;
+
+  while (*tokptr)
+    {
+      char c = *tokptr;
+      if (c == '\\')
+	{
+	   ++tokptr;
+	   *host_chars += c_parse_escape (&tokptr, &tempbuf);
+	}
+      else if (c == quote)
+	break;
+      else
+	{
+	  obstack_1grow (&tempbuf, c);
+	  ++tokptr;
+	  /* FIXME: this does the wrong thing with multi-byte host
+	     characters.  We could use mbrlen here, but that would
+	     make "set host-charset" a bit less useful.  */
+	  ++*host_chars;
+	}
+    }
+
+  if (*tokptr != quote)
+    {
+      if (quote == '"' || quote == '`')
+	error (_("Unterminated string in expression."));
+      else
+	error (_("Unmatched single quote."));
+    }
+  ++tokptr;
+
+  /* FIXME: should instead use own language string_type enum
+     and handle D-specific string suffixes here. */
+  if (quote == '\'')
+    value->type = C_CHAR;
+  else
+    value->type = C_STRING;
+
+  value->ptr = (char *) obstack_base (&tempbuf);
+  value->length = obstack_object_size (&tempbuf);
+
+  *outptr = tokptr;
+
+  return quote == '\'' ? CHARACTER_LITERAL : STRING_LITERAL;
+}
+
+struct d_token
+{
+  const char *oper;
+  int token;
+  enum exp_opcode opcode;
+};
+
+static const struct d_token tokentab3[] =
+  {
+    {"^^=", ASSIGN_MODIFY, BINOP_EXP},
+    {"<<=", ASSIGN_MODIFY, BINOP_LSH},
+    {">>=", ASSIGN_MODIFY, BINOP_RSH},
+  };
+
+static const struct d_token tokentab2[] =
+  {
+    {"+=", ASSIGN_MODIFY, BINOP_ADD},
+    {"-=", ASSIGN_MODIFY, BINOP_SUB},
+    {"*=", ASSIGN_MODIFY, BINOP_MUL},
+    {"/=", ASSIGN_MODIFY, BINOP_DIV},
+    {"%=", ASSIGN_MODIFY, BINOP_REM},
+    {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
+    {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
+    {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
+    {"++", INCREMENT, OP_NULL},
+    {"--", DECREMENT, OP_NULL},
+    {"&&", ANDAND, OP_NULL},
+    {"||", OROR, OP_NULL},
+    {"^^", HATHAT, OP_NULL},
+    {"<<", LSH, OP_NULL},
+    {">>", RSH, OP_NULL},
+    {"==", EQUAL, OP_NULL},
+    {"!=", NOTEQUAL, OP_NULL},
+    {"<=", LEQ, OP_NULL},
+    {">=", GEQ, OP_NULL},
+    {"..", DOTDOT, OP_NULL},
+  };
+
+/* Identifier-like tokens.  */
+static const struct d_token ident_tokens[] =
+  {
+    {"is", IDENTITY, OP_NULL},
+    {"!is", NOTIDENTITY, OP_NULL},
+
+    {"cast", CAST_KEYWORD, OP_NULL},
+    {"const", CONST_KEYWORD, OP_NULL},
+    {"immutable", IMMUTABLE_KEYWORD, OP_NULL},
+    {"shared", SHARED_KEYWORD, OP_NULL},
+    {"super", SUPER_KEYWORD, OP_NULL},
+
+    {"null", NULL_KEYWORD, OP_NULL},
+    {"true", TRUE_KEYWORD, OP_NULL},
+    {"false", FALSE_KEYWORD, OP_NULL},
+
+    {"init", INIT_KEYWORD, OP_NULL},
+    {"sizeof", SIZEOF_KEYWORD, OP_NULL},
+    {"typeof", TYPEOF_KEYWORD, OP_NULL},
+    {"typeid", TYPEID_KEYWORD, OP_NULL},
+
+    {"delegate", DELEGATE_KEYWORD, OP_NULL},
+    {"function", FUNCTION_KEYWORD, OP_NULL},
+    {"struct", STRUCT_KEYWORD, OP_NULL},
+    {"union", UNION_KEYWORD, OP_NULL},
+    {"class", CLASS_KEYWORD, OP_NULL},
+    {"interface", INTERFACE_KEYWORD, OP_NULL},
+    {"enum", ENUM_KEYWORD, OP_NULL},
+    {"template", TEMPLATE_KEYWORD, OP_NULL},
+  };
+
+/* This is set if a NAME token appeared at the very end of the input
+   string, with no whitespace separating the name from the EOF.  This
+   is used only when parsing to do field name completion.  */
+static int saw_name_at_eof;
+
+/* This is set if the previously-returned token was a structure operator.
+   This is used only when parsing to do field name completion.  */
+static int last_was_structop;
+
+/* Depth of parentheses.  */
+static int paren_depth;
+
+/* Read one token, getting characters through lexptr.  */
+
+static int
+lex_one_token (struct parser_state *par_state)
+{
+  int c;
+  int namelen;
+  const char *tokstart;
+  int saw_structop = last_was_structop;
+
+  last_was_structop = 0;
+
+ retry:
+
+  pstate->prev_lexptr = pstate->lexptr;
+
+  tokstart = pstate->lexptr;
+  /* See if it is a special token of length 3.  */
+  for (const auto &token : tokentab3)
+    if (strncmp (tokstart, token.oper, 3) == 0)
+      {
+	pstate->lexptr += 3;
+	d_yylval.opcode = token.opcode;
+	return token.token;
+      }
+
+  /* See if it is a special token of length 2.  */
+  for (const auto &token : tokentab2)
+    if (strncmp (tokstart, token.oper, 2) == 0)
+      {
+	pstate->lexptr += 2;
+	d_yylval.opcode = token.opcode;
+	return token.token;
+      }
+
+  switch (c = *tokstart)
+    {
+    case 0:
+      /* If we're parsing for field name completion, and the previous
+	 token allows such completion, return a COMPLETE token.
+	 Otherwise, we were already scanning the original text, and
+	 we're really done.  */
+      if (saw_name_at_eof)
+	{
+	  saw_name_at_eof = 0;
+	  return COMPLETE;
+	}
+      else if (saw_structop)
+	return COMPLETE;
+      else
+	return 0;
+
+    case ' ':
+    case '\t':
+    case '\n':
+      pstate->lexptr++;
+      goto retry;
+
+    case '[':
+    case '(':
+      paren_depth++;
+      pstate->lexptr++;
+      return c;
+
+    case ']':
+    case ')':
+      if (paren_depth == 0)
+	return 0;
+      paren_depth--;
+      pstate->lexptr++;
+      return c;
+
+    case ',':
+      if (pstate->comma_terminates && paren_depth == 0)
+	return 0;
+      pstate->lexptr++;
+      return c;
+
+    case '.':
+      /* Might be a floating point number.  */
+      if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
+	{
+	  if (pstate->parse_completion)
+	    last_was_structop = 1;
+	  goto symbol;		/* Nope, must be a symbol.  */
+	}
+      [[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)
+	  {
+	    /* Hex exponents start with 'p', because 'e' is a valid hex
+	       digit and thus does not indicate a floating point number
+	       when the radix is hex.  */
+	    if ((!hex && !got_e && c_tolower (p[0]) == 'e')
+		|| (hex && !got_e && c_tolower (p[0] == 'p')))
+	      got_dot = got_e = 1;
+	    /* A '.' always indicates a decimal floating point number
+	       regardless of the radix.  If we have a '..' then its the
+	       end of the number and the beginning of a slice.  */
+	    else if (!got_dot && (p[0] == '.' && p[1] != '.'))
+		got_dot = 1;
+	    /* This is the sign of the exponent, not the end of the number.  */
+	    else if (got_e && (c_tolower (p[-1]) == 'e'
+			       || c_tolower (p[-1]) == 'p')
+		     && (*p == '-' || *p == '+'))
+	      continue;
+	    /* We will take any letters or digits, ignoring any embedded '_'.
+	       parse_number will complain if past the radix, or if L or U are
+	       not final.  */
+	    else if ((*p < '0' || *p > '9') && (*p != '_')
+		     && ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z')))
+	      break;
+	  }
+
+	toktype = parse_number (par_state, tokstart, p - tokstart,
+				got_dot|got_e, &d_yylval);
+	if (toktype == ERROR)
+	  error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+		 tokstart);
+	pstate->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] != '_')
+	  {
+	    pstate->lexptr = &p[len];
+	    return ENTRY;
+	  }
+      }
+      [[fallthrough]];
+    case '+':
+    case '-':
+    case '*':
+    case '/':
+    case '%':
+    case '|':
+    case '&':
+    case '^':
+    case '~':
+    case '!':
+    case '<':
+    case '>':
+    case '?':
+    case ':':
+    case '=':
+    case '{':
+    case '}':
+    symbol:
+      pstate->lexptr++;
+      return c;
+
+    case '\'':
+    case '"':
+    case '`':
+      {
+	int host_len;
+	int result = parse_string_or_char (tokstart, &pstate->lexptr,
+					   &d_yylval.tsval, &host_len);
+	if (result == CHARACTER_LITERAL)
+	  {
+	    if (host_len == 0)
+	      error (_("Empty character constant."));
+	    else if (host_len > 2 && c == '\'')
+	      {
+		++tokstart;
+		namelen = pstate->lexptr - tokstart - 1;
+		goto tryname;
+	      }
+	    else if (host_len > 1)
+	      error (_("Invalid character constant."));
+	  }
+	return result;
+      }
+    }
+
+  if (!(c == '_' || c == '$'
+	|| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+    /* We must have come across a bad character (e.g. ';').  */
+    error (_("Invalid character '%c' in expression"), c);
+
+  /* It's a name.  See how long it is.  */
+  namelen = 0;
+  for (c = tokstart[namelen];
+       (c == '_' || c == '$' || (c >= '0' && c <= '9')
+	|| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));)
+    c = tokstart[++namelen];
+
+  /* The token "if" terminates the expression and is NOT
+     removed from the input stream.  */
+  if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+    return 0;
+
+  /* For the same reason (breakpoint conditions), "thread N"
+     terminates the expression.  "thread" could be an identifier, but
+     an identifier is never followed by a number without intervening
+     punctuation.  "task" is similar.  Handle abbreviations of these,
+     similarly to breakpoint.c:find_condition_and_thread.  */
+  if (namelen >= 1
+      && (strncmp (tokstart, "thread", namelen) == 0
+	  || strncmp (tokstart, "task", namelen) == 0)
+      && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t'))
+    {
+      const char *p = skip_spaces (tokstart + namelen + 1);
+      if (*p >= '0' && *p <= '9')
+	return 0;
+    }
+
+  pstate->lexptr += namelen;
+
+ tryname:
+
+  d_yylval.sval.ptr = tokstart;
+  d_yylval.sval.length = namelen;
+
+  /* Catch specific keywords.  */
+  std::string copy = copy_name (d_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.  */
+	d_yylval.opcode = token.opcode;
+	return token.token;
+      }
+
+  if (*tokstart == '$')
+    return DOLLAR_VARIABLE;
+
+  d_yylval.tsym.type
+    = language_lookup_primitive_type (par_state->language (),
+				      par_state->gdbarch (), copy.c_str ());
+  if (d_yylval.tsym.type != NULL)
+    return TYPENAME;
+
+  /* Input names that aren't symbols but ARE valid hex numbers,
+     when the input radix permits them, can be names or numbers
+     depending on the parse.  Note we support radixes > 16 here.  */
+  if ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
+      || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10))
+    {
+      d_exp_parser_YYSTYPE newlval;	/* Its value is ignored.  */
+      int hextype = parse_number (par_state, tokstart, namelen, 0, &newlval);
+      if (hextype == INTEGER_LITERAL)
+	return NAME_OR_INT;
+    }
+
+  if (pstate->parse_completion && *pstate->lexptr == '\0')
+    saw_name_at_eof = 1;
+
+  return IDENTIFIER;
+}
+
+/* An object of this type is pushed on a FIFO by the "outer" lexer.  */
+struct d_token_and_value
+{
+  int token;
+  d_exp_parser_YYSTYPE value;
+};
+
+
+/* A FIFO of tokens that have been read but not yet returned to the
+   parser.  */
+static std::vector<d_token_and_value> token_fifo;
+
+/* Non-zero if the lexer should return tokens from the FIFO.  */
+static int popping;
+
+/* Temporary storage for yylex; this holds symbol names as they are
+   built up.  */
+static auto_obstack name_obstack;
+
+/* Classify an IDENTIFIER token.  The contents of the token are in `yylval'.
+   Updates yylval and returns the new token type.  BLOCK is the block
+   in which lookups start; this can be NULL to mean the global scope.  */
+
+static int
+classify_name (struct parser_state *par_state, const struct block *block)
+{
+  struct block_symbol sym;
+  struct field_of_this_result is_a_field_of_this;
+
+  std::string copy = copy_name (d_yylval.sval);
+
+  sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
+  if (sym.symbol && sym.symbol->loc_class () == LOC_TYPEDEF)
+    {
+      d_yylval.tsym.type = sym.symbol->type ();
+      return TYPENAME;
+    }
+  else if (sym.symbol == NULL)
+    {
+      /* Look-up first for a module name, then a type.  */
+      sym = lookup_symbol (copy.c_str (), block, SEARCH_MODULE_DOMAIN,
+			   nullptr);
+      if (sym.symbol == NULL)
+	sym = lookup_symbol (copy.c_str (), block, SEARCH_STRUCT_DOMAIN,
+			     nullptr);
+
+      if (sym.symbol != NULL)
+	{
+	  d_yylval.tsym.type = sym.symbol->type ();
+	  return TYPENAME;
+	}
+
+      return UNKNOWN_NAME;
+    }
+
+  return IDENTIFIER;
+}
+
+/* Like classify_name, but used by the inner loop of the lexer, when a
+   name might have already been seen.  CONTEXT is the context type, or
+   NULL if this is the first component of a name.  */
+
+static int
+classify_inner_name (struct parser_state *par_state,
+		     const struct block *block, struct type *context)
+{
+  struct type *type;
+
+  if (context == NULL)
+    return classify_name (par_state, block);
+
+  type = check_typedef (context);
+  if (!type_aggregate_p (type))
+    return ERROR;
+
+  std::string copy = copy_name (d_yylval.ssym.stoken);
+  d_yylval.ssym.sym = d_lookup_nested_symbol (type, copy.c_str (), block);
+
+  if (d_yylval.ssym.sym.symbol == NULL)
+    return ERROR;
+
+  if (d_yylval.ssym.sym.symbol->loc_class () == LOC_TYPEDEF)
+    {
+      d_yylval.tsym.type = d_yylval.ssym.sym.symbol->type ();
+      return TYPENAME;
+    }
+
+  return IDENTIFIER;
+}
+
+/* See d-exp-parser.h.  */
+
+int
+d_yylex (void)
+{
+  d_token_and_value current;
+  int last_was_dot;
+  struct type *context_type = NULL;
+  int last_to_examine, next_to_examine, checkpoint;
+  const struct block *search_block;
+
+  if (popping && !token_fifo.empty ())
+    goto do_pop;
+  popping = 0;
+
+  /* Read the first token and decide what to do.  */
+  current.token = lex_one_token (pstate);
+  if (current.token != IDENTIFIER && current.token != '.')
+    return current.token;
+
+  /* Read any sequence of alternating "." and identifier tokens into
+     the token FIFO.  */
+  current.value = d_yylval;
+  token_fifo.push_back (current);
+  last_was_dot = current.token == '.';
+
+  while (1)
+    {
+      current.token = lex_one_token (pstate);
+      current.value = d_yylval;
+      token_fifo.push_back (current);
+
+      if ((last_was_dot && current.token != IDENTIFIER)
+	  || (!last_was_dot && current.token != '.'))
+	break;
+
+      last_was_dot = !last_was_dot;
+    }
+  popping = 1;
+
+  /* We always read one extra token, so compute the number of tokens
+     to examine accordingly.  */
+  last_to_examine = token_fifo.size () - 2;
+  next_to_examine = 0;
+
+  current = token_fifo[next_to_examine];
+  ++next_to_examine;
+
+  /* If we are not dealing with a typename, now is the time to find out.  */
+  if (current.token == IDENTIFIER)
+    {
+      d_yylval = current.value;
+      current.token = classify_name (pstate, pstate->expression_context_block);
+      current.value = d_yylval;
+    }
+
+  /* If the IDENTIFIER is not known, it could be a package symbol,
+     first try building up a name until we find the qualified module.  */
+  if (current.token == UNKNOWN_NAME)
+    {
+      name_obstack.clear ();
+      obstack_grow (&name_obstack, current.value.sval.ptr,
+		    current.value.sval.length);
+
+      last_was_dot = 0;
+
+      while (next_to_examine <= last_to_examine)
+	{
+	  d_token_and_value next;
+
+	  next = token_fifo[next_to_examine];
+	  ++next_to_examine;
+
+	  if (next.token == IDENTIFIER && last_was_dot)
+	    {
+	      /* Update the partial name we are constructing.  */
+	      obstack_grow_str (&name_obstack, ".");
+	      obstack_grow (&name_obstack, next.value.sval.ptr,
+			    next.value.sval.length);
+
+	      d_yylval.sval.ptr = (char *) obstack_base (&name_obstack);
+	      d_yylval.sval.length = obstack_object_size (&name_obstack);
+
+	      current.token = classify_name (pstate,
+					     pstate->expression_context_block);
+	      current.value = d_yylval;
+
+	      /* We keep going until we find a TYPENAME.  */
+	      if (current.token == TYPENAME)
+		{
+		  /* Install it as the first token in the FIFO.  */
+		  token_fifo[0] = current;
+		  token_fifo.erase (token_fifo.begin () + 1,
+				    token_fifo.begin () + next_to_examine);
+		  break;
+		}
+	    }
+	  else if (next.token == '.' && !last_was_dot)
+	    last_was_dot = 1;
+	  else
+	    {
+	      /* We've reached the end of the name.  */
+	      break;
+	    }
+	}
+
+      /* Reset our current token back to the start, if we found nothing
+	 this means that we will just jump to do pop.  */
+      current = token_fifo[0];
+      next_to_examine = 1;
+    }
+  if (current.token != TYPENAME && current.token != '.')
+    goto do_pop;
+
+  name_obstack.clear ();
+  checkpoint = 0;
+  if (current.token == '.')
+    search_block = NULL;
+  else
+    {
+      gdb_assert (current.token == TYPENAME);
+      search_block = pstate->expression_context_block;
+      obstack_grow (&name_obstack, current.value.sval.ptr,
+		    current.value.sval.length);
+      context_type = current.value.tsym.type;
+      checkpoint = 1;
+    }
+
+  last_was_dot = current.token == '.';
+
+  while (next_to_examine <= last_to_examine)
+    {
+      d_token_and_value next;
+
+      next = token_fifo[next_to_examine];
+      ++next_to_examine;
+
+      if (next.token == IDENTIFIER && last_was_dot)
+	{
+	  int classification;
+
+	  d_yylval = next.value;
+	  classification = classify_inner_name (pstate, search_block,
+						context_type);
+	  /* We keep going until we either run out of names, or until
+	     we have a qualified name which is not a type.  */
+	  if (classification != TYPENAME && classification != IDENTIFIER)
+	    break;
+
+	  /* Accept up to this token.  */
+	  checkpoint = next_to_examine;
+
+	  /* Update the partial name we are constructing.  */
+	  if (context_type != NULL)
+	    {
+	      /* We don't want to put a leading "." into the name.  */
+	      obstack_grow_str (&name_obstack, ".");
+	    }
+	  obstack_grow (&name_obstack, next.value.sval.ptr,
+			next.value.sval.length);
+
+	  d_yylval.sval.ptr = (char *) obstack_base (&name_obstack);
+	  d_yylval.sval.length = obstack_object_size (&name_obstack);
+	  current.value = d_yylval;
+	  current.token = classification;
+
+	  last_was_dot = 0;
+
+	  if (classification == IDENTIFIER)
+	    break;
+
+	  context_type = d_yylval.tsym.type;
+	}
+      else if (next.token == '.' && !last_was_dot)
+	last_was_dot = 1;
+      else
+	{
+	  /* We've reached the end of the name.  */
+	  break;
+	}
+    }
+
+  /* If we have a replacement token, install it as the first token in
+     the FIFO, and delete the other constituent tokens.  */
+  if (checkpoint > 0)
+    {
+      token_fifo[0] = current;
+      if (checkpoint > 1)
+	token_fifo.erase (token_fifo.begin () + 1,
+			  token_fifo.begin () + checkpoint);
+    }
+
+ do_pop:
+  current = token_fifo[0];
+  token_fifo.erase (token_fifo.begin ());
+  d_yylval = current.value;
+  return current.token;
+}
+
+/* See d-exp-parser.h.  */
+
+void
+d_yyerror (const char *msg)
+{
+  pstate->parse_error (msg);
+}
+
+} /* namespace d_exp_parser */
+
+/* See d-exp-parser.h.  */
+
+int
+d_parse (struct parser_state *par_state)
+{
+  using namespace d_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 (&d_yydebug,
+							par_state->debug);
+
+  struct type_stack stack;
+  scoped_restore restore_type_stack
+    = make_scoped_restore (&d_exp_parser::type_stack, &stack);
+
+  /* Initialize some state used by the lexer.  */
+  last_was_structop = 0;
+  saw_name_at_eof = 0;
+  paren_depth = 0;
+
+  token_fifo.clear ();
+  popping = 0;
+  name_obstack.clear ();
+
+  int result = d_yyparse ();
+  if (!result)
+    pstate->set_operation (pstate->pop ());
+  return result;
+}
diff --git a/gdb/d-exp-parser.h b/gdb/d-exp-parser.h
new file mode 100644
index 000000000000..d73575eb83b9
--- /dev/null
+++ b/gdb/d-exp-parser.h
@@ -0,0 +1,83 @@
+/* Support code for the D expression parser, for GDB.
+
+   Copyright (C) 2014-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_D_EXP_PARSER_H
+#define GDB_D_EXP_PARSER_H
+
+#include "parser-defs.h"
+#include "type-stack.h"
+#include "d-lang.h"
+
+union d_exp_parser_YYSTYPE;
+
+namespace d_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+   expression.  */
+
+extern parser_state *pstate;
+
+/* The current type stack.  */
+
+extern struct type_stack *type_stack;
+
+/* Return the D type table for the architecture associated to PS.  */
+
+static inline const struct builtin_d_type *
+parse_d_type (parser_state *ps)
+{
+  return builtin_d_type (ps->gdbarch ());
+}
+
+/* Return true if the type is aggregate-like.  */
+
+int type_aggregate_p (struct type *type);
+
+/* Take care of parsing a number (anything that starts with a digit).
+   Set yylval and return the token type; update lexptr.
+   LEN is the number of characters in it.  */
+
+/*** Needs some error checking for the float case ***/
+
+int parse_number (struct parser_state *ps, const char *p, int len,
+		  int parsed_float, d_exp_parser_YYSTYPE *putithere);
+
+/* The outer level of a two-level lexer.  This calls the inner lexer
+   to return tokens.  It then either returns these tokens, or
+   aggregates them into a larger token.  This lets us work around a
+   problem in our parsing approach, where the parser could not
+   distinguish between qualified names and qualified types at the
+   right point.  */
+
+int d_yylex ();
+
+/* The error handler invoked by the generated parser.  Report MSG as a
+   parse error on the current parser state.  */
+
+void d_yyerror (const char *msg);
+
+} /* namespace d_exp_parser */
+
+/* Parse a D 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 d_parse (struct parser_state *par_state);
+
+#endif /* GDB_D_EXP_PARSER_H */
diff --git a/gdb/d-exp-parser.y b/gdb/d-exp-parser.y
index c35d78b83140..a83b1aaa4ec4 100644
--- a/gdb/d-exp-parser.y
+++ b/gdb/d-exp-parser.y
@@ -42,41 +42,20 @@
 #include "value.h"
 #include "parser-defs.h"
 #include "language.h"
-#include "c-lang.h"
-#include "c-exp-parser.h"
 #include "d-lang.h"
-#include "charset.h"
+#include "d-exp-parser.h"
 #include "block.h"
 #include "type-stack.h"
 #include "expop.h"
 #include "cli/cli-style.h"
 
-/* The state of the parser, used internally when we are parsing the
-   expression.  */
-
-static struct parser_state *pstate = NULL;
-
-/* The current type stack.  */
-static struct type_stack *type_stack;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
-
-static int type_aggregate_p (struct type *);
-
-/* Return the D type table for the architecture associated to PS.  */
-
-static inline const struct builtin_d_type *
-parse_d_type (parser_state *ps)
-{
-  return builtin_d_type (ps->gdbarch ());
-}
-
+using namespace d_exp_parser;
 using namespace expr;
 
+/* Bring the d_exp_parser::type_stack global into this scope, so that it hides
+   the struct type_stack type name.  */
+using d_exp_parser::type_stack;
+
 %}
 
 /* Although the yacc "value" of an expression is not used,
@@ -105,12 +84,6 @@ using namespace expr;
     struct stoken_vector svec;
   }
 
-%{
-/* YYSTYPE gets defined by %union */
-static int parse_number (struct parser_state *, const char *,
-			 int, int, YYSTYPE *);
-%}
-
 %token <sval> IDENTIFIER UNKNOWN_NAME
 %token <tsym> TYPENAME
 %token <voidval> COMPLETE
@@ -625,1007 +598,3 @@ BasicType:
 	TYPENAME
 		{ $$ = $1.type; }
 ;
-
-%%
-
-/* Return true if the type is aggregate-like.  */
-
-static int
-type_aggregate_p (struct type *type)
-{
-  return (type->code () == TYPE_CODE_STRUCT
-	  || type->code () == TYPE_CODE_UNION
-	  || type->code () == TYPE_CODE_MODULE
-	  || (type->code () == TYPE_CODE_ENUM
-	      && type->is_declared_class ()));
-}
-
-/* Take care of parsing a number (anything that starts with a digit).
-   Set yylval and return the token type; update lexptr.
-   LEN is the number of characters in it.  */
-
-/*** Needs some error checking for the float case ***/
-
-static int
-parse_number (struct parser_state *ps, const char *p,
-	      int len, int parsed_float, YYSTYPE *putithere)
-{
-  ULONGEST n = 0;
-  ULONGEST prevn = 0;
-  ULONGEST un;
-
-  int i = 0;
-  int c;
-  int base = input_radix;
-  int unsigned_p = 0;
-  int long_p = 0;
-
-  /* We have found a "L" or "U" suffix.  */
-  int found_suffix = 0;
-
-  ULONGEST high_bit;
-  struct type *signed_type;
-  struct type *unsigned_type;
-
-  if (parsed_float)
-    {
-      char *s, *sp;
-
-      /* Strip out all embedded '_' before passing to parse_float.  */
-      s = (char *) alloca (len + 1);
-      sp = s;
-      while (len-- > 0)
-	{
-	  if (*p != '_')
-	    *sp++ = *p;
-	  p++;
-	}
-      *sp = '\0';
-      len = strlen (s);
-
-      /* Check suffix for `i' , `fi' or `li' (idouble, ifloat or ireal).  */
-      if (len >= 1 && c_tolower (s[len - 1]) == 'i')
-	{
-	  if (len >= 2 && c_tolower (s[len - 2]) == 'f')
-	    {
-	      putithere->typed_val_float.type
-		= parse_d_type (ps)->builtin_ifloat;
-	      len -= 2;
-	    }
-	  else if (len >= 2 && c_tolower (s[len - 2]) == 'l')
-	    {
-	      putithere->typed_val_float.type
-		= parse_d_type (ps)->builtin_ireal;
-	      len -= 2;
-	    }
-	  else
-	    {
-	      putithere->typed_val_float.type
-		= parse_d_type (ps)->builtin_idouble;
-	      len -= 1;
-	    }
-	}
-      /* Check suffix for `f' or `l'' (float or real).  */
-      else if (len >= 1 && c_tolower (s[len - 1]) == 'f')
-	{
-	  putithere->typed_val_float.type
-	    = parse_d_type (ps)->builtin_float;
-	  len -= 1;
-	}
-      else if (len >= 1 && c_tolower (s[len - 1]) == 'l')
-	{
-	  putithere->typed_val_float.type
-	    = parse_d_type (ps)->builtin_real;
-	  len -= 1;
-	}
-      /* Default type if no suffix.  */
-      else
-	{
-	  putithere->typed_val_float.type
-	    = parse_d_type (ps)->builtin_double;
-	}
-
-      if (!parse_float (s, len,
-			putithere->typed_val_float.type,
-			putithere->typed_val_float.val))
-	return ERROR;
-
-      return FLOAT_LITERAL;
-    }
-
-  /* Handle base-switching prefixes 0x, 0b, 0 */
-  if (p[0] == '0')
-    switch (p[1])
-      {
-      case 'x':
-      case 'X':
-	if (len >= 3)
-	  {
-	    p += 2;
-	    base = 16;
-	    len -= 2;
-	  }
-	break;
-
-      case 'b':
-      case 'B':
-	if (len >= 3)
-	  {
-	    p += 2;
-	    base = 2;
-	    len -= 2;
-	  }
-	break;
-
-      default:
-	base = 8;
-	break;
-      }
-
-  while (len-- > 0)
-    {
-      c = *p++;
-      if (c == '_')
-	continue;	/* Ignore embedded '_'.  */
-      if (c >= 'A' && c <= 'Z')
-	c += 'a' - 'A';
-      if (c != 'l' && c != 'u')
-	n *= base;
-      if (c >= '0' && c <= '9')
-	{
-	  if (found_suffix)
-	    return ERROR;
-	  n += i = c - '0';
-	}
-      else
-	{
-	  if (base > 10 && c >= 'a' && c <= 'f')
-	    {
-	      if (found_suffix)
-		return ERROR;
-	      n += i = c - 'a' + 10;
-	    }
-	  else if (c == 'l' && long_p == 0)
-	    {
-	      long_p = 1;
-	      found_suffix = 1;
-	    }
-	  else if (c == 'u' && unsigned_p == 0)
-	    {
-	      unsigned_p = 1;
-	      found_suffix = 1;
-	    }
-	  else
-	    return ERROR;	/* Char not a digit */
-	}
-      if (i >= base)
-	return ERROR;		/* Invalid digit in this base.  */
-      /* Portably test for integer overflow.  */
-      if (c != 'l' && c != 'u')
-	{
-	  ULONGEST n2 = prevn * base;
-	  if ((n2 / base != prevn) || (n2 + i < prevn))
-	    error (_("Numeric constant too large."));
-	}
-      prevn = n;
-    }
-
-  /* An integer constant is an int or a long.  An L suffix forces it to
-     be long, and a U suffix forces it to be unsigned.  To figure out
-     whether it fits, we shift it right and see whether anything remains.
-     Note that we can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or
-     more in one operation, because many compilers will warn about such a
-     shift (which always produces a zero result).  To deal with the case
-     where it is we just always shift the value more than once, with fewer
-     bits each time.  */
-  un = (ULONGEST) n >> 2;
-  if (long_p == 0 && (un >> 30) == 0)
-    {
-      high_bit = ((ULONGEST) 1) << 31;
-      signed_type = parse_d_type (ps)->builtin_int;
-      /* For decimal notation, keep the sign of the worked out type.  */
-      if (base == 10 && !unsigned_p)
-	unsigned_type = parse_d_type (ps)->builtin_long;
-      else
-	unsigned_type = parse_d_type (ps)->builtin_uint;
-    }
-  else
-    {
-      int shift;
-      if (sizeof (ULONGEST) * HOST_CHAR_BIT < 64)
-	/* A long long does not fit in a LONGEST.  */
-	shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1);
-      else
-	shift = 63;
-      high_bit = (ULONGEST) 1 << shift;
-      signed_type = parse_d_type (ps)->builtin_long;
-      unsigned_type = parse_d_type (ps)->builtin_ulong;
-    }
-
-  putithere->typed_val_int.val = n;
-
-  /* If the high bit of the worked out type is set then this number
-     has to be unsigned_type.  */
-  if (unsigned_p || (n & high_bit))
-    putithere->typed_val_int.type = unsigned_type;
-  else
-    putithere->typed_val_int.type = signed_type;
-
-  return INTEGER_LITERAL;
-}
-
-/* Temporary obstack used for holding strings.  */
-static struct obstack tempbuf;
-static int tempbuf_init;
-
-/* Parse a string or character literal from TOKPTR.  The string or
-   character may be wide or unicode.  *OUTPTR is set to just after the
-   end of the literal in the input string.  The resulting token is
-   stored in VALUE.  This returns a token value, either STRING or
-   CHAR, depending on what was parsed.  *HOST_CHARS is set to the
-   number of host characters in the literal.  */
-
-static int
-parse_string_or_char (const char *tokptr, const char **outptr,
-		      struct typed_stoken *value, int *host_chars)
-{
-  int quote;
-
-  /* Build the gdb internal form of the input string in tempbuf.  Note
-     that the buffer is null byte terminated *only* for the
-     convenience of debugging gdb itself and printing the buffer
-     contents when the buffer contains no embedded nulls.  Gdb does
-     not depend upon the buffer being null byte terminated, it uses
-     the length string instead.  This allows gdb to handle C strings
-     (as well as strings in other languages) with embedded null
-     bytes */
-
-  if (!tempbuf_init)
-    tempbuf_init = 1;
-  else
-    obstack_free (&tempbuf, NULL);
-  obstack_init (&tempbuf);
-
-  /* Skip the quote.  */
-  quote = *tokptr;
-  ++tokptr;
-
-  *host_chars = 0;
-
-  while (*tokptr)
-    {
-      char c = *tokptr;
-      if (c == '\\')
-	{
-	   ++tokptr;
-	   *host_chars += c_parse_escape (&tokptr, &tempbuf);
-	}
-      else if (c == quote)
-	break;
-      else
-	{
-	  obstack_1grow (&tempbuf, c);
-	  ++tokptr;
-	  /* FIXME: this does the wrong thing with multi-byte host
-	     characters.  We could use mbrlen here, but that would
-	     make "set host-charset" a bit less useful.  */
-	  ++*host_chars;
-	}
-    }
-
-  if (*tokptr != quote)
-    {
-      if (quote == '"' || quote == '`')
-	error (_("Unterminated string in expression."));
-      else
-	error (_("Unmatched single quote."));
-    }
-  ++tokptr;
-
-  /* FIXME: should instead use own language string_type enum
-     and handle D-specific string suffixes here. */
-  if (quote == '\'')
-    value->type = C_CHAR;
-  else
-    value->type = C_STRING;
-
-  value->ptr = (char *) obstack_base (&tempbuf);
-  value->length = obstack_object_size (&tempbuf);
-
-  *outptr = tokptr;
-
-  return quote == '\'' ? CHARACTER_LITERAL : STRING_LITERAL;
-}
-
-struct d_token
-{
-  const char *oper;
-  int token;
-  enum exp_opcode opcode;
-};
-
-static const struct d_token tokentab3[] =
-  {
-    {"^^=", ASSIGN_MODIFY, BINOP_EXP},
-    {"<<=", ASSIGN_MODIFY, BINOP_LSH},
-    {">>=", ASSIGN_MODIFY, BINOP_RSH},
-  };
-
-static const struct d_token tokentab2[] =
-  {
-    {"+=", ASSIGN_MODIFY, BINOP_ADD},
-    {"-=", ASSIGN_MODIFY, BINOP_SUB},
-    {"*=", ASSIGN_MODIFY, BINOP_MUL},
-    {"/=", ASSIGN_MODIFY, BINOP_DIV},
-    {"%=", ASSIGN_MODIFY, BINOP_REM},
-    {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
-    {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
-    {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
-    {"++", INCREMENT, OP_NULL},
-    {"--", DECREMENT, OP_NULL},
-    {"&&", ANDAND, OP_NULL},
-    {"||", OROR, OP_NULL},
-    {"^^", HATHAT, OP_NULL},
-    {"<<", LSH, OP_NULL},
-    {">>", RSH, OP_NULL},
-    {"==", EQUAL, OP_NULL},
-    {"!=", NOTEQUAL, OP_NULL},
-    {"<=", LEQ, OP_NULL},
-    {">=", GEQ, OP_NULL},
-    {"..", DOTDOT, OP_NULL},
-  };
-
-/* Identifier-like tokens.  */
-static const struct d_token ident_tokens[] =
-  {
-    {"is", IDENTITY, OP_NULL},
-    {"!is", NOTIDENTITY, OP_NULL},
-
-    {"cast", CAST_KEYWORD, OP_NULL},
-    {"const", CONST_KEYWORD, OP_NULL},
-    {"immutable", IMMUTABLE_KEYWORD, OP_NULL},
-    {"shared", SHARED_KEYWORD, OP_NULL},
-    {"super", SUPER_KEYWORD, OP_NULL},
-
-    {"null", NULL_KEYWORD, OP_NULL},
-    {"true", TRUE_KEYWORD, OP_NULL},
-    {"false", FALSE_KEYWORD, OP_NULL},
-
-    {"init", INIT_KEYWORD, OP_NULL},
-    {"sizeof", SIZEOF_KEYWORD, OP_NULL},
-    {"typeof", TYPEOF_KEYWORD, OP_NULL},
-    {"typeid", TYPEID_KEYWORD, OP_NULL},
-
-    {"delegate", DELEGATE_KEYWORD, OP_NULL},
-    {"function", FUNCTION_KEYWORD, OP_NULL},
-    {"struct", STRUCT_KEYWORD, OP_NULL},
-    {"union", UNION_KEYWORD, OP_NULL},
-    {"class", CLASS_KEYWORD, OP_NULL},
-    {"interface", INTERFACE_KEYWORD, OP_NULL},
-    {"enum", ENUM_KEYWORD, OP_NULL},
-    {"template", TEMPLATE_KEYWORD, OP_NULL},
-  };
-
-/* This is set if a NAME token appeared at the very end of the input
-   string, with no whitespace separating the name from the EOF.  This
-   is used only when parsing to do field name completion.  */
-static int saw_name_at_eof;
-
-/* This is set if the previously-returned token was a structure operator.
-   This is used only when parsing to do field name completion.  */
-static int last_was_structop;
-
-/* Depth of parentheses.  */
-static int paren_depth;
-
-/* Read one token, getting characters through lexptr.  */
-
-static int
-lex_one_token (struct parser_state *par_state)
-{
-  int c;
-  int namelen;
-  const char *tokstart;
-  int saw_structop = last_was_structop;
-
-  last_was_structop = 0;
-
- retry:
-
-  pstate->prev_lexptr = pstate->lexptr;
-
-  tokstart = pstate->lexptr;
-  /* See if it is a special token of length 3.  */
-  for (const auto &token : tokentab3)
-    if (strncmp (tokstart, token.oper, 3) == 0)
-      {
-	pstate->lexptr += 3;
-	yylval.opcode = token.opcode;
-	return token.token;
-      }
-
-  /* See if it is a special token of length 2.  */
-  for (const auto &token : tokentab2)
-    if (strncmp (tokstart, token.oper, 2) == 0)
-      {
-	pstate->lexptr += 2;
-	yylval.opcode = token.opcode;
-	return token.token;
-      }
-
-  switch (c = *tokstart)
-    {
-    case 0:
-      /* If we're parsing for field name completion, and the previous
-	 token allows such completion, return a COMPLETE token.
-	 Otherwise, we were already scanning the original text, and
-	 we're really done.  */
-      if (saw_name_at_eof)
-	{
-	  saw_name_at_eof = 0;
-	  return COMPLETE;
-	}
-      else if (saw_structop)
-	return COMPLETE;
-      else
-	return 0;
-
-    case ' ':
-    case '\t':
-    case '\n':
-      pstate->lexptr++;
-      goto retry;
-
-    case '[':
-    case '(':
-      paren_depth++;
-      pstate->lexptr++;
-      return c;
-
-    case ']':
-    case ')':
-      if (paren_depth == 0)
-	return 0;
-      paren_depth--;
-      pstate->lexptr++;
-      return c;
-
-    case ',':
-      if (pstate->comma_terminates && paren_depth == 0)
-	return 0;
-      pstate->lexptr++;
-      return c;
-
-    case '.':
-      /* Might be a floating point number.  */
-      if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
-	{
-	  if (pstate->parse_completion)
-	    last_was_structop = 1;
-	  goto symbol;		/* Nope, must be a symbol.  */
-	}
-      [[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)
-	  {
-	    /* Hex exponents start with 'p', because 'e' is a valid hex
-	       digit and thus does not indicate a floating point number
-	       when the radix is hex.  */
-	    if ((!hex && !got_e && c_tolower (p[0]) == 'e')
-		|| (hex && !got_e && c_tolower (p[0] == 'p')))
-	      got_dot = got_e = 1;
-	    /* A '.' always indicates a decimal floating point number
-	       regardless of the radix.  If we have a '..' then its the
-	       end of the number and the beginning of a slice.  */
-	    else if (!got_dot && (p[0] == '.' && p[1] != '.'))
-		got_dot = 1;
-	    /* This is the sign of the exponent, not the end of the number.  */
-	    else if (got_e && (c_tolower (p[-1]) == 'e'
-			       || c_tolower (p[-1]) == 'p')
-		     && (*p == '-' || *p == '+'))
-	      continue;
-	    /* We will take any letters or digits, ignoring any embedded '_'.
-	       parse_number will complain if past the radix, or if L or U are
-	       not final.  */
-	    else if ((*p < '0' || *p > '9') && (*p != '_')
-		     && ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z')))
-	      break;
-	  }
-
-	toktype = parse_number (par_state, tokstart, p - tokstart,
-				got_dot|got_e, &yylval);
-	if (toktype == ERROR)
-	  error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
-		 tokstart);
-	pstate->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] != '_')
-	  {
-	    pstate->lexptr = &p[len];
-	    return ENTRY;
-	  }
-      }
-      [[fallthrough]];
-    case '+':
-    case '-':
-    case '*':
-    case '/':
-    case '%':
-    case '|':
-    case '&':
-    case '^':
-    case '~':
-    case '!':
-    case '<':
-    case '>':
-    case '?':
-    case ':':
-    case '=':
-    case '{':
-    case '}':
-    symbol:
-      pstate->lexptr++;
-      return c;
-
-    case '\'':
-    case '"':
-    case '`':
-      {
-	int host_len;
-	int result = parse_string_or_char (tokstart, &pstate->lexptr,
-					   &yylval.tsval, &host_len);
-	if (result == CHARACTER_LITERAL)
-	  {
-	    if (host_len == 0)
-	      error (_("Empty character constant."));
-	    else if (host_len > 2 && c == '\'')
-	      {
-		++tokstart;
-		namelen = pstate->lexptr - tokstart - 1;
-		goto tryname;
-	      }
-	    else if (host_len > 1)
-	      error (_("Invalid character constant."));
-	  }
-	return result;
-      }
-    }
-
-  if (!(c == '_' || c == '$'
-	|| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
-    /* We must have come across a bad character (e.g. ';').  */
-    error (_("Invalid character '%c' in expression"), c);
-
-  /* It's a name.  See how long it is.  */
-  namelen = 0;
-  for (c = tokstart[namelen];
-       (c == '_' || c == '$' || (c >= '0' && c <= '9')
-	|| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));)
-    c = tokstart[++namelen];
-
-  /* The token "if" terminates the expression and is NOT
-     removed from the input stream.  */
-  if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
-    return 0;
-
-  /* For the same reason (breakpoint conditions), "thread N"
-     terminates the expression.  "thread" could be an identifier, but
-     an identifier is never followed by a number without intervening
-     punctuation.  "task" is similar.  Handle abbreviations of these,
-     similarly to breakpoint.c:find_condition_and_thread.  */
-  if (namelen >= 1
-      && (strncmp (tokstart, "thread", namelen) == 0
-	  || strncmp (tokstart, "task", namelen) == 0)
-      && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t'))
-    {
-      const char *p = skip_spaces (tokstart + namelen + 1);
-      if (*p >= '0' && *p <= '9')
-	return 0;
-    }
-
-  pstate->lexptr += namelen;
-
- tryname:
-
-  yylval.sval.ptr = tokstart;
-  yylval.sval.length = namelen;
-
-  /* Catch specific keywords.  */
-  std::string copy = copy_name (yylval.sval);
-  for (const auto &token : ident_tokens)
-    if (copy == token.oper)
-      {
-	/* It is ok to always set this, even though we don't always
-	   strictly need to.  */
-	yylval.opcode = token.opcode;
-	return token.token;
-      }
-
-  if (*tokstart == '$')
-    return DOLLAR_VARIABLE;
-
-  yylval.tsym.type
-    = language_lookup_primitive_type (par_state->language (),
-				      par_state->gdbarch (), copy.c_str ());
-  if (yylval.tsym.type != NULL)
-    return TYPENAME;
-
-  /* Input names that aren't symbols but ARE valid hex numbers,
-     when the input radix permits them, can be names or numbers
-     depending on the parse.  Note we support radixes > 16 here.  */
-  if ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
-      || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10))
-    {
-      YYSTYPE newlval;	/* Its value is ignored.  */
-      int hextype = parse_number (par_state, tokstart, namelen, 0, &newlval);
-      if (hextype == INTEGER_LITERAL)
-	return NAME_OR_INT;
-    }
-
-  if (pstate->parse_completion && *pstate->lexptr == '\0')
-    saw_name_at_eof = 1;
-
-  return IDENTIFIER;
-}
-
-/* An object of this type is pushed on a FIFO by the "outer" lexer.  */
-struct d_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<d_token_and_value> token_fifo;
-
-/* Non-zero if the lexer should return tokens from the FIFO.  */
-static int popping;
-
-/* Temporary storage for yylex; this holds symbol names as they are
-   built up.  */
-static auto_obstack name_obstack;
-
-/* Classify an IDENTIFIER token.  The contents of the token are in `yylval'.
-   Updates yylval and returns the new token type.  BLOCK is the block
-   in which lookups start; this can be NULL to mean the global scope.  */
-
-static int
-classify_name (struct parser_state *par_state, const struct block *block)
-{
-  struct block_symbol sym;
-  struct field_of_this_result is_a_field_of_this;
-
-  std::string copy = copy_name (yylval.sval);
-
-  sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
-  if (sym.symbol && sym.symbol->loc_class () == LOC_TYPEDEF)
-    {
-      yylval.tsym.type = sym.symbol->type ();
-      return TYPENAME;
-    }
-  else if (sym.symbol == NULL)
-    {
-      /* Look-up first for a module name, then a type.  */
-      sym = lookup_symbol (copy.c_str (), block, SEARCH_MODULE_DOMAIN,
-			   nullptr);
-      if (sym.symbol == NULL)
-	sym = lookup_symbol (copy.c_str (), block, SEARCH_STRUCT_DOMAIN,
-			     nullptr);
-
-      if (sym.symbol != NULL)
-	{
-	  yylval.tsym.type = sym.symbol->type ();
-	  return TYPENAME;
-	}
-
-      return UNKNOWN_NAME;
-    }
-
-  return IDENTIFIER;
-}
-
-/* Like classify_name, but used by the inner loop of the lexer, when a
-   name might have already been seen.  CONTEXT is the context type, or
-   NULL if this is the first component of a name.  */
-
-static int
-classify_inner_name (struct parser_state *par_state,
-		     const struct block *block, struct type *context)
-{
-  struct type *type;
-
-  if (context == NULL)
-    return classify_name (par_state, block);
-
-  type = check_typedef (context);
-  if (!type_aggregate_p (type))
-    return ERROR;
-
-  std::string copy = copy_name (yylval.ssym.stoken);
-  yylval.ssym.sym = d_lookup_nested_symbol (type, copy.c_str (), block);
-
-  if (yylval.ssym.sym.symbol == NULL)
-    return ERROR;
-
-  if (yylval.ssym.sym.symbol->loc_class () == LOC_TYPEDEF)
-    {
-      yylval.tsym.type = yylval.ssym.sym.symbol->type ();
-      return TYPENAME;
-    }
-
-  return IDENTIFIER;
-}
-
-/* The outer level of a two-level lexer.  This calls the inner lexer
-   to return tokens.  It then either returns these tokens, or
-   aggregates them into a larger token.  This lets us work around a
-   problem in our parsing approach, where the parser could not
-   distinguish between qualified names and qualified types at the
-   right point.  */
-
-static int
-yylex (void)
-{
-  d_token_and_value current;
-  int last_was_dot;
-  struct type *context_type = NULL;
-  int last_to_examine, next_to_examine, checkpoint;
-  const struct block *search_block;
-
-  if (popping && !token_fifo.empty ())
-    goto do_pop;
-  popping = 0;
-
-  /* Read the first token and decide what to do.  */
-  current.token = lex_one_token (pstate);
-  if (current.token != IDENTIFIER && current.token != '.')
-    return current.token;
-
-  /* Read any sequence of alternating "." and identifier tokens into
-     the token FIFO.  */
-  current.value = yylval;
-  token_fifo.push_back (current);
-  last_was_dot = current.token == '.';
-
-  while (1)
-    {
-      current.token = lex_one_token (pstate);
-      current.value = yylval;
-      token_fifo.push_back (current);
-
-      if ((last_was_dot && current.token != IDENTIFIER)
-	  || (!last_was_dot && current.token != '.'))
-	break;
-
-      last_was_dot = !last_was_dot;
-    }
-  popping = 1;
-
-  /* We always read one extra token, so compute the number of tokens
-     to examine accordingly.  */
-  last_to_examine = token_fifo.size () - 2;
-  next_to_examine = 0;
-
-  current = token_fifo[next_to_examine];
-  ++next_to_examine;
-
-  /* If we are not dealing with a typename, now is the time to find out.  */
-  if (current.token == IDENTIFIER)
-    {
-      yylval = current.value;
-      current.token = classify_name (pstate, pstate->expression_context_block);
-      current.value = yylval;
-    }
-
-  /* If the IDENTIFIER is not known, it could be a package symbol,
-     first try building up a name until we find the qualified module.  */
-  if (current.token == UNKNOWN_NAME)
-    {
-      name_obstack.clear ();
-      obstack_grow (&name_obstack, current.value.sval.ptr,
-		    current.value.sval.length);
-
-      last_was_dot = 0;
-
-      while (next_to_examine <= last_to_examine)
-	{
-	  d_token_and_value next;
-
-	  next = token_fifo[next_to_examine];
-	  ++next_to_examine;
-
-	  if (next.token == IDENTIFIER && last_was_dot)
-	    {
-	      /* Update the partial name we are constructing.  */
-	      obstack_grow_str (&name_obstack, ".");
-	      obstack_grow (&name_obstack, next.value.sval.ptr,
-			    next.value.sval.length);
-
-	      yylval.sval.ptr = (char *) obstack_base (&name_obstack);
-	      yylval.sval.length = obstack_object_size (&name_obstack);
-
-	      current.token = classify_name (pstate,
-					     pstate->expression_context_block);
-	      current.value = yylval;
-
-	      /* We keep going until we find a TYPENAME.  */
-	      if (current.token == TYPENAME)
-		{
-		  /* Install it as the first token in the FIFO.  */
-		  token_fifo[0] = current;
-		  token_fifo.erase (token_fifo.begin () + 1,
-				    token_fifo.begin () + next_to_examine);
-		  break;
-		}
-	    }
-	  else if (next.token == '.' && !last_was_dot)
-	    last_was_dot = 1;
-	  else
-	    {
-	      /* We've reached the end of the name.  */
-	      break;
-	    }
-	}
-
-      /* Reset our current token back to the start, if we found nothing
-	 this means that we will just jump to do pop.  */
-      current = token_fifo[0];
-      next_to_examine = 1;
-    }
-  if (current.token != TYPENAME && current.token != '.')
-    goto do_pop;
-
-  name_obstack.clear ();
-  checkpoint = 0;
-  if (current.token == '.')
-    search_block = NULL;
-  else
-    {
-      gdb_assert (current.token == TYPENAME);
-      search_block = pstate->expression_context_block;
-      obstack_grow (&name_obstack, current.value.sval.ptr,
-		    current.value.sval.length);
-      context_type = current.value.tsym.type;
-      checkpoint = 1;
-    }
-
-  last_was_dot = current.token == '.';
-
-  while (next_to_examine <= last_to_examine)
-    {
-      d_token_and_value next;
-
-      next = token_fifo[next_to_examine];
-      ++next_to_examine;
-
-      if (next.token == IDENTIFIER && last_was_dot)
-	{
-	  int classification;
-
-	  yylval = next.value;
-	  classification = classify_inner_name (pstate, search_block,
-						context_type);
-	  /* We keep going until we either run out of names, or until
-	     we have a qualified name which is not a type.  */
-	  if (classification != TYPENAME && classification != IDENTIFIER)
-	    break;
-
-	  /* Accept up to this token.  */
-	  checkpoint = next_to_examine;
-
-	  /* Update the partial name we are constructing.  */
-	  if (context_type != NULL)
-	    {
-	      /* We don't want to put a leading "." into the name.  */
-	      obstack_grow_str (&name_obstack, ".");
-	    }
-	  obstack_grow (&name_obstack, next.value.sval.ptr,
-			next.value.sval.length);
-
-	  yylval.sval.ptr = (char *) obstack_base (&name_obstack);
-	  yylval.sval.length = obstack_object_size (&name_obstack);
-	  current.value = yylval;
-	  current.token = classification;
-
-	  last_was_dot = 0;
-
-	  if (classification == IDENTIFIER)
-	    break;
-
-	  context_type = yylval.tsym.type;
-	}
-      else if (next.token == '.' && !last_was_dot)
-	last_was_dot = 1;
-      else
-	{
-	  /* We've reached the end of the name.  */
-	  break;
-	}
-    }
-
-  /* If we have a replacement token, install it as the first token in
-     the FIFO, and delete the other constituent tokens.  */
-  if (checkpoint > 0)
-    {
-      token_fifo[0] = current;
-      if (checkpoint > 1)
-	token_fifo.erase (token_fifo.begin () + 1,
-			  token_fifo.begin () + checkpoint);
-    }
-
- do_pop:
-  current = token_fifo[0];
-  token_fifo.erase (token_fifo.begin ());
-  yylval = current.value;
-  return current.token;
-}
-
-int
-d_parse (struct parser_state *par_state)
-{
-  /* Setting up the parser state.  */
-  scoped_restore pstate_restore = make_scoped_restore (&pstate);
-  gdb_assert (par_state != NULL);
-  pstate = par_state;
-
-  scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
-							par_state->debug);
-
-  struct type_stack stack;
-  scoped_restore restore_type_stack = make_scoped_restore (&type_stack,
-							   &stack);
-
-  /* Initialize some state used by the lexer.  */
-  last_was_structop = 0;
-  saw_name_at_eof = 0;
-  paren_depth = 0;
-
-  token_fifo.clear ();
-  popping = 0;
-  name_obstack.clear ();
-
-  int result = yyparse ();
-  if (!result)
-    pstate->set_operation (pstate->pop ());
-  return result;
-}
-
-static void
-yyerror (const char *msg)
-{
-  pstate->parse_error (msg);
-}
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index dc1f41512a26..4793eb8826ed 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -21,6 +21,7 @@
 #include "language.h"
 #include "varobj.h"
 #include "d-lang.h"
+#include "d-exp-parser.h"
 #include "c-lang.h"
 #include "demangle.h"
 #include "cp-support.h"
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index e8752ac95306..9d424c06f223 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -54,10 +54,6 @@ struct builtin_d_type
   struct type *builtin_dchar = nullptr;
 };
 
-/* Defined in d-exp-parser.y.  */
-
-extern int d_parse (struct parser_state *);
-
 /* Defined in d-lang.c  */
 
 extern const char *d_main_name (void);
-- 
2.55.0


  parent reply	other threads:[~2026-09-05  4:35 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-05  4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
2026-09-05  4:23 ` [PATCH v2 01/19] gdb/ada-exp-parser: remove name_info struct simon.marchi
2026-09-05  4:23 ` [PATCH v2 02/19] gdb: replace parse_type macros with functions simon.marchi
2026-09-05  4:23 ` [PATCH v2 03/19] gdb: suffix flex/bison output files with -gen.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 04/19] gdb: remove YY_NULL to YY_NULLPTR substitution simon.marchi
2026-09-05  4:23 ` [PATCH v2 05/19] gdb: move parser output post-processing to a script simon.marchi
2026-09-05  4:23 ` [PATCH v2 06/19] gdb: let the parser and lexer generators prefix their symbols simon.marchi
2026-09-05  4:23 ` [PATCH v2 07/19] gdb: separate cp-name-parser's symbol prefix with an underscore simon.marchi
2026-09-05  4:23 ` [PATCH v2 08/19] gdb: make $(YACC) and $(FLEX) generate headers simon.marchi
2026-09-05  4:23 ` [PATCH v2 09/19] gdb: add check for stale build generated files simon.marchi
2026-09-05  4:23 ` [PATCH v2 10/19] gdb: move cp-name-parser.y's support code to cp-name-parser.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 11/19] gdb: rename LANG-exp.y to LANG-exp-parser.y simon.marchi
2026-09-05  4:23 ` [PATCH v2 12/19] gdb: move c-exp-parser.y's support code to c-exp-parser.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c simon.marchi
2026-09-08 18:28   ` Kevin Buettner
2026-09-05  4:23 ` simon.marchi [this message]
2026-09-05  4:23 ` [PATCH v2 15/19] gdb: move f-exp-parser.y's support code to f-exp-parser.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 16/19] gdb: move go-exp-parser.y's support code to go-exp-parser.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 17/19] gdb: move m2-exp-parser.y's support code to m2-exp-parser.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 18/19] gdb: move p-exp-parser.y's support code to p-exp-parser.c simon.marchi
2026-09-05  4:23 ` [PATCH v2 19/19] gdb: honor "set debug parser" in the Modula-2 and Pascal parsers simon.marchi

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=20260905042353.1702204-15-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