From: simon.marchi@polymtl.ca
To: gdb-patches@sourceware.org
Cc: Simon Marchi <simon.marchi@efficios.com>
Subject: [PATCH 11/17] gdb: move c-exp-parser.y's support code to c-exp-parser.c
Date: Fri, 4 Sep 2026 12:56:43 -0400 [thread overview]
Message-ID: <20260904170338.1643894-12-simon.marchi@polymtl.ca> (raw)
In-Reply-To: <20260904170338.1643894-1-simon.marchi@polymtl.ca>
From: Simon Marchi <simon.marchi@efficios.com>
This patch moves the C++ code defined at the bottom of c-exp-parser.y to a
new file c-exp-parser.c. The reason for this is that I find it hard to
read and maintain complex code in a .y file, where standard C++ tooling
doesn't work.
This leaves c-exp-parser.y with just a bit of prologue and the grammar
rules themselves. The code is moved as-is, with some exceptions:
- struct c_parse_state and struct qualified_name_token move to the
c-exp-parser.h header, so that both c-exp-parser-gen.c and
c-exp-parser.c can see them.
- The pstate and cpstate globals move to c-exp-parser.c and are no longer
static. They are declared in c-exp-parser.h, so that
c-exp-parser-gen.c, which references them in the grammar rules' actions,
can see them.
- The old yylex and yyerror are renamed explicitly to c_yylex and
c_yyerror. They used to be effectively named that, thanks to the
parser generator's -p flag, but now that they live in c-exp-parser.c,
they just have that name. They are declared in c-exp-parser.h, so
that c-exp-parser-gen.c can see them.
- The malloc call in operator_stoken becomes an xmalloc call. It used to
be rewritten to xmalloc by post-process-parser-output.sh when the code
was part of the generated parser, but now needs to be an xmalloc call
directly.
As explained by the comment, c-exp-parser.c needs to include some
declarations for c_yyparse and c_yyerror, which byacc does not provide in
the generated header file for some reason.
To avoid symbol collisions, I wrapped most of c-exp-parser.{c,h} in
namespace `c_exp_parser`. By using `using namespace c_exp_parser` in the
.y file, the code of the rules can stay the same. The only things not in
the namespace are the declarations of c_parse and c_parse_escape, the two
entry points for this translation unit, the declarations of which I moved
from c-lang.h to c-exp-parser.h. Note that the c_parse_escape is a
freestanding function, it does not use the bison-generated parser at all
(as far as I know).
This patch establishes the patterns and conventions used in the subsequent
patches that update the other parsers.
Change-Id: Ie772d0f7db95974161b1dc3213f5616da4386517
---
gdb/Makefile.in | 2 +
gdb/c-exp-parser.c | 1696 +++++++++++++++++++++++++++++++++++++++++
gdb/c-exp-parser.h | 182 +++++
gdb/c-exp-parser.y | 1767 +------------------------------------------
gdb/c-lang.h | 6 -
gdb/d-exp-parser.y | 1 +
gdb/go-exp-parser.y | 1 +
gdb/language.c | 1 +
gdb/macroexp.c | 6 +-
9 files changed, 1887 insertions(+), 1775 deletions(-)
create mode 100644 gdb/c-exp-parser.c
create mode 100644 gdb/c-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 1871ef255694..4cd503e2175f 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1064,6 +1064,7 @@ COMMON_SFILES = \
buffered-streams.c \
build-id.c \
buildsym.c \
+ c-exp-parser.c \
c-lang.c \
c-typeprint.c \
c-valprint.c \
@@ -1343,6 +1344,7 @@ HFILES_NO_SRCDIR = \
buffered-streams.h \
build-id.h \
buildsym.h \
+ c-exp-parser.h \
c-exp.h \
cgen-remap.h \
charset.h \
diff --git a/gdb/c-exp-parser.c b/gdb/c-exp-parser.c
new file mode 100644
index 000000000000..64bf455e7220
--- /dev/null
+++ b/gdb/c-exp-parser.c
@@ -0,0 +1,1696 @@
+/* Support code for the C expression parser, for GDB.
+
+ Copyright (C) 1986-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 "c-exp-parser.h"
+#include "block.h"
+#include "c-exp-parser-gen.h"
+#include "c-support.h"
+#include "charset.h"
+#include "cp-support.h"
+#include "macroexp.h"
+#include "macroscope.h"
+#include "objc-lang.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ c-exp-parser-gen.c. Bison produces a declaration for c_yyparse in
+ c-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int c_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for c_yydebug. */
+
+extern int c_yydebug;
+
+namespace c_exp_parser
+{
+
+/* See c-exp-parser.h. */
+
+c_parse_state *cpstate;
+
+/* See c-exp-parser.h. */
+
+parser_state *pstate;
+
+/* See c-exp-parser.h. */
+
+struct stoken
+operator_stoken (const char *op)
+{
+ struct stoken st = { NULL, 0 };
+ char *buf;
+
+ st.length = CP_OPERATOR_LEN + strlen (op);
+ buf = (char *) xmalloc (st.length + 1);
+ strcpy (buf, CP_OPERATOR_STR);
+ strcat (buf, op);
+ st.ptr = buf;
+
+ /* The toplevel (c_parse) will free the memory allocated here. */
+ cpstate->strings.emplace_back (buf);
+ return st;
+};
+
+/* See c-exp-parser.h. */
+
+qualified_name_token
+typename_stoken (const char *type)
+{
+ return qualified_name_token { nullptr, type, false };
+};
+
+/* See c-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_NAMESPACE
+ || (type->code () == TYPE_CODE_ENUM
+ && type->is_declared_class ()));
+}
+
+/* See c-exp-parser.h. */
+
+void
+check_parameter_typelist (std::vector<struct type *> *params)
+{
+ struct type *type;
+ int ix;
+
+ for (ix = 0; ix < params->size (); ++ix)
+ {
+ type = (*params)[ix];
+ if (type != NULL && check_typedef (type)->code () == TYPE_CODE_VOID)
+ {
+ if (ix == 0)
+ {
+ if (params->size () == 1)
+ {
+ /* Ok. */
+ break;
+ }
+ error (_("parameter types following 'void'"));
+ }
+ else
+ error (_("'void' invalid as parameter type"));
+ }
+ }
+}
+
+/* See c-exp-parser.h. */
+
+int
+parse_number (struct parser_state *par_state, const char *buf, int len,
+ int parsed_float, c_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;
+
+ /* Imaginary number. */
+ bool imaginary_p = false;
+
+ /* We have found a "L" or "U" (or "i") suffix. */
+ int found_suffix = 0;
+
+ if (parsed_float)
+ {
+ if (len >= 1 && buf[len - 1] == 'i')
+ {
+ imaginary_p = true;
+ --len;
+ }
+
+ /* Handle suffixes for decimal floating-point: "df", "dd" or "dl". */
+ if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'f')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_decfloat;
+ len -= 2;
+ }
+ else if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'd')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_decdouble;
+ len -= 2;
+ }
+ else if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'l')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_declong;
+ len -= 2;
+ }
+ /* Handle suffixes: 'f' for float, 'l' for long double. */
+ else if (len >= 1 && c_tolower (buf[len - 1]) == 'f')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_float;
+ len -= 1;
+ }
+ else if (len >= 1 && c_tolower (buf[len - 1]) == 'l')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_long_double;
+ len -= 1;
+ }
+ /* Default type for floating-point literals is double. */
+ else
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_double;
+ }
+
+ if (!parse_float (buf, len,
+ putithere->typed_val_float.type,
+ putithere->typed_val_float.val))
+ return ERROR;
+
+ if (imaginary_p)
+ putithere->typed_val_float.type
+ = init_complex_type (nullptr, putithere->typed_val_float.type);
+
+ return imaginary_p ? COMPLEX_FLOAT : FLOAT;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
+ if (buf[0] == '0' && len > 1)
+ switch (buf[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ buf += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 'b':
+ case 'B':
+ if (len >= 3)
+ {
+ buf += 2;
+ base = 2;
+ len -= 2;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ case 'd':
+ case 'D':
+ if (len >= 3)
+ {
+ buf += 2;
+ base = 10;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *buf++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != 'l' && c != 'u' && c != 'i')
+ 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 if (c == 'i')
+ {
+ imaginary_p = true;
+ 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' && c != 'i')
+ {
+ /* Test for overflow. */
+ if (prevn == 0 && n == 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
+ /* No 'u' suffix. */
+ = !unsigned_p;
+ bool have_unsigned
+ = ((/* 'u' suffix. */
+ unsigned_p)
+ || (/* Not a decimal. */
+ base != 10)
+ || (/* Allowed as a convenience, in case decimal doesn't fit in largest
+ signed type. */
+ !fits_in_type (1, n, long_long_bits, true)));
+ bool have_int
+ /* No 'l' or 'll' suffix. */
+ = long_p == 0;
+ bool have_long
+ /* No 'll' suffix. */
+ = 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 && have_unsigned && 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 && have_unsigned && 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 (have_unsigned && 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;
+
+ if (imaginary_p)
+ putithere->typed_val_int.type
+ = init_complex_type (nullptr, putithere->typed_val_int.type);
+
+ return imaginary_p ? COMPLEX_INT : 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;
+ c_string_type type;
+ int is_objc = 0;
+
+ /* 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);
+
+ /* Record the string type. */
+ if (*tokptr == 'L')
+ {
+ type = C_WIDE_STRING;
+ ++tokptr;
+ }
+ else if (*tokptr == 'u')
+ {
+ type = C_STRING_16;
+ ++tokptr;
+ }
+ else if (*tokptr == 'U')
+ {
+ type = C_STRING_32;
+ ++tokptr;
+ }
+ else if (*tokptr == '@')
+ {
+ /* An Objective C string. */
+ is_objc = 1;
+ type = C_STRING;
+ ++tokptr;
+ }
+ else
+ type = C_STRING;
+
+ /* Skip the quote. */
+ quote = *tokptr;
+ if (quote == '\'')
+ type |= C_CHAR;
+ ++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 = type;
+ value->ptr = (char *) obstack_base (&tempbuf);
+ value->length = obstack_object_size (&tempbuf);
+
+ *outptr = tokptr;
+
+ return quote == '"' ? (is_objc ? NSSTRING : STRING) : CHAR;
+}
+
+/* This is used to associate some attributes with a token. */
+
+enum token_flag
+{
+ /* If this bit is set, the token is C++-only. */
+
+ FLAG_CXX = 1,
+
+ /* If this bit is set, the token is C-only. */
+
+ FLAG_C = 2,
+
+ /* If this bit is set, the token is conditional: if there is a
+ symbol of the same name, then the token is a symbol; otherwise,
+ the token is a keyword. */
+
+ FLAG_SHADOW = 4
+};
+DEF_ENUM_FLAGS_TYPE (enum token_flag, token_flags);
+
+struct c_token
+{
+ const char *oper;
+ int token;
+ enum exp_opcode opcode;
+ token_flags flags;
+};
+
+static const struct c_token tokentab3[] =
+ {
+ {">>=", ASSIGN_MODIFY, BINOP_RSH, 0},
+ {"<<=", ASSIGN_MODIFY, BINOP_LSH, 0},
+ {"->*", ARROW_STAR, OP_NULL, FLAG_CXX},
+ {"...", DOTDOTDOT, OP_NULL, 0}
+ };
+
+static const struct c_token tokentab2[] =
+ {
+ {"+=", ASSIGN_MODIFY, BINOP_ADD, 0},
+ {"-=", ASSIGN_MODIFY, BINOP_SUB, 0},
+ {"*=", ASSIGN_MODIFY, BINOP_MUL, 0},
+ {"/=", ASSIGN_MODIFY, BINOP_DIV, 0},
+ {"%=", ASSIGN_MODIFY, BINOP_REM, 0},
+ {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR, 0},
+ {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND, 0},
+ {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR, 0},
+ {"++", INCREMENT, OP_NULL, 0},
+ {"--", DECREMENT, OP_NULL, 0},
+ {"->", ARROW, OP_NULL, 0},
+ {"&&", ANDAND, OP_NULL, 0},
+ {"||", OROR, OP_NULL, 0},
+ /* "::" is *not* only C++: gdb overrides its meaning in several
+ different ways, e.g., 'filename'::func, function::variable. */
+ {"::", COLONCOLON, OP_NULL, 0},
+ {"<<", LSH, OP_NULL, 0},
+ {">>", RSH, OP_NULL, 0},
+ {"==", EQUAL, OP_NULL, 0},
+ {"!=", NOTEQUAL, OP_NULL, 0},
+ {"<=", LEQ, OP_NULL, 0},
+ {">=", GEQ, OP_NULL, 0},
+ {".*", DOT_STAR, OP_NULL, FLAG_CXX}
+ };
+
+/* Identifier-like tokens. Only type-specifiers than can appear in
+ multi-word type names (for example 'double' can appear in 'long
+ double') need to be listed here. type-specifiers that are only ever
+ single word (like 'char') are handled by the classify_name function. */
+static const struct c_token ident_tokens[] =
+ {
+ {"unsigned", UNSIGNED, OP_NULL, 0},
+ {"template", TEMPLATE, OP_NULL, FLAG_CXX},
+ {"volatile", VOLATILE_KEYWORD, OP_NULL, 0},
+ {"struct", STRUCT, OP_NULL, 0},
+ {"signed", SIGNED_KEYWORD, OP_NULL, 0},
+ {"sizeof", SIZEOF, OP_NULL, 0},
+ {"_Alignof", ALIGNOF, OP_NULL, 0},
+ {"alignof", ALIGNOF, OP_NULL, FLAG_CXX},
+ {"double", DOUBLE_KEYWORD, OP_NULL, 0},
+ {"float", FLOAT_KEYWORD, OP_NULL, 0},
+ {"false", FALSEKEYWORD, OP_NULL, FLAG_CXX},
+ {"class", CLASS, OP_NULL, FLAG_CXX},
+ {"union", UNION, OP_NULL, 0},
+ {"short", SHORT, OP_NULL, 0},
+ {"const", CONST_KEYWORD, OP_NULL, 0},
+ {"restrict", RESTRICT, OP_NULL, FLAG_C | FLAG_SHADOW},
+ {"__restrict__", RESTRICT, OP_NULL, 0},
+ {"__restrict", RESTRICT, OP_NULL, 0},
+ {"_Atomic", ATOMIC, OP_NULL, 0},
+ {"enum", ENUM, OP_NULL, 0},
+ {"long", LONG, OP_NULL, 0},
+ {"_Complex", COMPLEX, OP_NULL, 0},
+ {"__complex__", COMPLEX, OP_NULL, 0},
+
+ {"true", TRUEKEYWORD, OP_NULL, FLAG_CXX},
+ {"int", INT_KEYWORD, OP_NULL, 0},
+ {"new", NEW, OP_NULL, FLAG_CXX},
+ {"delete", DELETE, OP_NULL, FLAG_CXX},
+ {"operator", OPERATOR, OP_NULL, FLAG_CXX},
+
+ {"and", ANDAND, OP_NULL, FLAG_CXX},
+ {"and_eq", ASSIGN_MODIFY, BINOP_BITWISE_AND, FLAG_CXX},
+ {"bitand", '&', OP_NULL, FLAG_CXX},
+ {"bitor", '|', OP_NULL, FLAG_CXX},
+ {"compl", '~', OP_NULL, FLAG_CXX},
+ {"not", '!', OP_NULL, FLAG_CXX},
+ {"not_eq", NOTEQUAL, OP_NULL, FLAG_CXX},
+ {"or", OROR, OP_NULL, FLAG_CXX},
+ {"or_eq", ASSIGN_MODIFY, BINOP_BITWISE_IOR, FLAG_CXX},
+ {"xor", '^', OP_NULL, FLAG_CXX},
+ {"xor_eq", ASSIGN_MODIFY, BINOP_BITWISE_XOR, FLAG_CXX},
+
+ {"const_cast", CONST_CAST, OP_NULL, FLAG_CXX },
+ {"dynamic_cast", DYNAMIC_CAST, OP_NULL, FLAG_CXX },
+ {"static_cast", STATIC_CAST, OP_NULL, FLAG_CXX },
+ {"reinterpret_cast", REINTERPRET_CAST, OP_NULL, FLAG_CXX },
+
+ {"__typeof__", TYPEOF, OP_TYPEOF, 0 },
+ {"__typeof", TYPEOF, OP_TYPEOF, 0 },
+ {"typeof", TYPEOF, OP_TYPEOF, FLAG_SHADOW },
+ {"__decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX },
+ {"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW },
+
+ {"typeid", TYPEID, OP_TYPEID, FLAG_CXX}
+ };
+
+
+static void
+scan_macro_expansion (const char *expansion)
+{
+ /* We'd better not be trying to push the stack twice. */
+ gdb_assert (! cpstate->macro_original_text);
+
+ /* Copy to the obstack. */
+ const char *copy = obstack_strdup (&cpstate->expansion_obstack, expansion);
+
+ /* Save the old lexptr value, so we can return to it when we're done
+ parsing the expanded text. */
+ cpstate->macro_original_text = pstate->lexptr;
+ pstate->lexptr = copy;
+}
+
+static int
+scanning_macro_expansion (void)
+{
+ return cpstate->macro_original_text != 0;
+}
+
+static void
+finished_macro_expansion (void)
+{
+ /* There'd better be something to pop back to. */
+ gdb_assert (cpstate->macro_original_text);
+
+ /* Pop back to the original text. */
+ pstate->lexptr = cpstate->macro_original_text;
+ cpstate->macro_original_text = 0;
+}
+
+/* Return true iff the token represents a C++ cast operator. */
+
+static int
+is_cast_operator (const char *token, int len)
+{
+ return (! strncmp (token, "dynamic_cast", len)
+ || ! strncmp (token, "static_cast", len)
+ || ! strncmp (token, "reinterpret_cast", len)
+ || ! strncmp (token, "const_cast", len));
+}
+
+/* The scope used for macro expansion. */
+static struct macro_scope *expression_macro_scope;
+
+/* 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. */
+static bool last_was_structop;
+
+/* Depth of parentheses. */
+static int paren_depth;
+
+/* Lex an Objective-C @selector. Return true if lexed. In this case,
+ sets the resulting token and updates the lex pointer. Otherwise
+ returns false and updates nothing. */
+
+static bool
+lex_selector (const char **lex_ptr, struct stoken *token)
+{
+ const char *p = *lex_ptr;
+
+ if (!startswith (p, "selector"))
+ return false;
+
+ p += strlen ("selector");
+ p = skip_spaces (p);
+ if (*p != '(')
+ return false;
+ ++p;
+
+ /* The selector name matches [A-Za-z0-9:_-]+. We could probably be
+ a bit more refined but meh. */
+ const char *start = p;
+ while (c_isalnum (*p) || *p == ':' || *p == '_' || *p == '-')
+ ++p;
+ if (p == start)
+ return false;
+ const char *end = p;
+
+ p = skip_spaces (p);
+ if (*p != ')')
+ return false;
+ ++p;
+
+ *lex_ptr = p;
+ *token = { start, (int) (end - start) };
+ return true;
+}
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+lex_one_token (struct parser_state *par_state, bool *is_quoted_name)
+{
+ int c;
+ int namelen;
+ const char *tokstart;
+ bool saw_structop = last_was_structop;
+
+ last_was_structop = false;
+ *is_quoted_name = false;
+
+ retry:
+
+ /* Check if this is a macro invocation that we need to expand. */
+ if (! scanning_macro_expansion ())
+ {
+ gdb::unique_xmalloc_ptr<char> expanded
+ = macro_expand_next (&pstate->lexptr, *expression_macro_scope);
+
+ if (expanded != nullptr)
+ scan_macro_expansion (expanded.get ());
+ }
+
+ 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)
+ {
+ if ((token.flags & FLAG_CXX) != 0
+ && par_state->language ()->la_language != language_cplus)
+ break;
+ gdb_assert ((token.flags & FLAG_C) == 0);
+
+ pstate->lexptr += 3;
+ c_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)
+ {
+ if ((token.flags & FLAG_CXX) != 0
+ && par_state->language ()->la_language != language_cplus)
+ break;
+ gdb_assert ((token.flags & FLAG_C) == 0);
+
+ pstate->lexptr += 2;
+ c_yylval.opcode = token.opcode;
+ if (token.token == ARROW)
+ last_was_structop = 1;
+ return token.token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ /* If we were just scanning the result of a macro expansion,
+ then we need to resume scanning the original text.
+ 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 (scanning_macro_expansion ())
+ {
+ finished_macro_expansion ();
+ goto retry;
+ }
+ else if (saw_name_at_eof)
+ {
+ saw_name_at_eof = 0;
+ return COMPLETE;
+ }
+ else if (par_state->parse_completion && saw_structop)
+ return COMPLETE;
+ else
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ pstate->lexptr++;
+ goto retry;
+
+ case '[':
+ case '(':
+ paren_depth++;
+ pstate->lexptr++;
+ if (par_state->language ()->la_language == language_objc
+ && c == '[')
+ return OBJC_LBRAC;
+ return c;
+
+ case ']':
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ pstate->lexptr++;
+ return c;
+
+ case ',':
+ if (pstate->comma_terminates
+ && paren_depth == 0
+ && ! scanning_macro_expansion ())
+ return 0;
+ pstate->lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
+ {
+ last_was_structop = true;
+ 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, got_p = 0, toktype;
+ const char *p = tokstart;
+ int hex = input_radix > 10;
+
+ if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+ {
+ p += 2;
+ hex = 1;
+ }
+ else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D'))
+ {
+ p += 2;
+ hex = 0;
+ }
+
+ /* If the token includes the C++14 digits separator, we make a
+ copy so that we don't have to handle the separator in
+ parse_number. */
+ std::optional<std::string> no_tick;
+ 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 && !got_p && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ else if (!got_e && !got_p && (*p == 'p' || *p == 'P'))
+ got_dot = got_p = 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'))
+ || (got_p && (p[-1] == 'p' || p[-1] == 'P')))
+ && (*p == '-' || *p == '+'))
+ {
+ /* This is the sign of the exponent, not the end of
+ the number. */
+ }
+ else if (*p == '\'')
+ {
+ if (!no_tick.has_value ())
+ no_tick.emplace (tokstart, p);
+ 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;
+ if (no_tick.has_value ())
+ no_tick->push_back (*p);
+ }
+ if (no_tick.has_value ())
+ toktype = parse_number (par_state, no_tick->c_str (),
+ no_tick->length (),
+ got_dot | got_e | got_p, &c_yylval);
+ else
+ toktype = parse_number (par_state, tokstart, p - tokstart,
+ got_dot | got_e | got_p, &c_yylval);
+ if (toktype == ERROR)
+ error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+ tokstart);
+ pstate->lexptr = p;
+ return toktype;
+ }
+
+ case '@':
+ {
+ const char *p = &tokstart[1];
+
+ if (par_state->language ()->la_language == language_objc)
+ {
+ struct stoken sel_token;
+ if (lex_selector (&p, &sel_token))
+ {
+ pstate->lexptr = p;
+ c_yylval.sval = sel_token;
+ return SELECTOR;
+ }
+ else if (*p == '"')
+ goto parse_string;
+ }
+
+ while (c_isspace (*p))
+ p++;
+ size_t len = strlen ("entry");
+ if (strncmp (p, "entry", len) == 0 && !c_ident_is_alnum (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 'L':
+ case 'u':
+ case 'U':
+ if (tokstart[1] != '"' && tokstart[1] != '\'')
+ break;
+ [[fallthrough]];
+ case '\'':
+ case '"':
+
+ parse_string:
+ {
+ int host_len;
+ int result = parse_string_or_char (tokstart, &pstate->lexptr,
+ &c_yylval.tsval, &host_len);
+ if (result == CHAR)
+ {
+ if (host_len == 0)
+ error (_("Empty character constant."));
+ else if (host_len > 2 && c == '\'')
+ {
+ ++tokstart;
+ namelen = pstate->lexptr - tokstart - 1;
+ *is_quoted_name = true;
+
+ goto tryname;
+ }
+ else if (host_len > 1)
+ error (_("Invalid character constant."));
+ }
+ return result;
+ }
+ }
+
+ if (!(c == '_' || c == '$' || c_ident_is_alpha (c)))
+ /* 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_ident_is_alnum (c) || c == '<');)
+ {
+ /* Template parameter lists are part of the name.
+ FIXME: This mishandles `print $a<4&&$a>3'. */
+
+ if (c == '<')
+ {
+ if (! is_cast_operator (tokstart, namelen))
+ {
+ /* Scan ahead to get rest of the template specification. Note
+ that we look ahead only when the '<' adjoins non-whitespace
+ characters; for comparison expressions, e.g. "a < b > c",
+ there must be spaces before the '<', etc. */
+ const char *p = find_template_name_end (tokstart + namelen);
+
+ if (p)
+ namelen = p - tokstart;
+ }
+ break;
+ }
+ 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'
+ && ! scanning_macro_expansion ())
+ {
+ 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')
+ && ! scanning_macro_expansion ())
+ {
+ const char *p = skip_spaces (tokstart + namelen + 1);
+ if (*p >= '0' && *p <= '9')
+ return 0;
+ }
+
+ pstate->lexptr += namelen;
+
+ tryname:
+
+ c_yylval.sval.ptr = tokstart;
+ c_yylval.sval.length = namelen;
+
+ /* Catch specific keywords. */
+ std::string copy = copy_name (c_yylval.sval);
+ for (const auto &token : ident_tokens)
+ if (copy == token.oper)
+ {
+ if ((token.flags & FLAG_CXX) != 0
+ && par_state->language ()->la_language != language_cplus)
+ break;
+ if ((token.flags & FLAG_C) != 0
+ && par_state->language ()->la_language != language_c
+ && par_state->language ()->la_language != language_objc)
+ break;
+
+ if ((token.flags & FLAG_SHADOW) != 0)
+ {
+ struct field_of_this_result is_a_field_of_this;
+
+ if (lookup_symbol (copy.c_str (),
+ pstate->expression_context_block,
+ SEARCH_VFT, &is_a_field_of_this).symbol
+ != NULL)
+ {
+ /* The keyword is shadowed. */
+ break;
+ }
+ }
+
+ /* It is ok to always set this, even though we don't always
+ strictly need to. */
+ c_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ if (*tokstart == '$')
+ return DOLLAR_VARIABLE;
+
+ if (pstate->parse_completion && *pstate->lexptr == '\0')
+ saw_name_at_eof = 1;
+
+ c_yylval.ssym.stoken = c_yylval.sval;
+ c_yylval.ssym.sym.symbol = NULL;
+ c_yylval.ssym.sym.block = NULL;
+ c_yylval.ssym.is_a_field_of_this = 0;
+ return NAME;
+}
+
+/* An object of this type is pushed on a FIFO by the "outer" lexer. */
+struct c_token_and_value
+{
+ int token;
+ c_exp_parser_YYSTYPE value;
+};
+
+/* A FIFO of tokens that have been read but not yet returned to the
+ parser. */
+static std::vector<c_token_and_value> token_fifo;
+
+/* Non-zero if the lexer should return tokens from the FIFO. */
+static int popping;
+
+/* Temporary storage for c_lex; this holds symbol names as they are
+ built up. */
+static auto_obstack name_obstack;
+
+/* 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.
+ IS_QUOTED_NAME is non-zero if the name token was originally quoted
+ in single quotes. IS_AFTER_STRUCTOP is true if this name follows
+ a structure operator -- either '.' or ARROW */
+
+static int
+classify_name (struct parser_state *par_state, const struct block *block,
+ bool is_quoted_name, bool is_after_structop)
+{
+ struct block_symbol bsym;
+ struct field_of_this_result is_a_field_of_this;
+
+ std::string copy = copy_name (c_yylval.sval);
+
+ bsym = lookup_symbol (copy.c_str (), block, SEARCH_VFT,
+ &is_a_field_of_this);
+
+ if (bsym.symbol && bsym.symbol->loc_class () == LOC_BLOCK)
+ {
+ c_yylval.ssym.sym = bsym;
+ c_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ return BLOCKNAME;
+ }
+ else if (!bsym.symbol)
+ {
+ /* If we found a field of 'this', we might have erroneously
+ found a constructor where we wanted a type name. Handle this
+ case by noticing that we found a constructor and then look up
+ the type tag instead. */
+ if (is_a_field_of_this.type != NULL
+ && is_a_field_of_this.fn_field != NULL
+ && TYPE_FN_FIELD_CONSTRUCTOR (is_a_field_of_this.fn_field->fn_fields,
+ 0))
+ {
+ struct field_of_this_result inner_is_a_field_of_this;
+
+ bsym = lookup_symbol (copy.c_str (), block, SEARCH_STRUCT_DOMAIN,
+ &inner_is_a_field_of_this);
+ if (bsym.symbol != NULL)
+ {
+ c_yylval.tsym.type = bsym.symbol->type ();
+ return TYPENAME;
+ }
+ }
+
+ /* If we found a field on the "this" object, or we are looking
+ up a field on a struct, then we want to prefer it over a
+ filename. However, if the name was quoted, then it is better
+ to check for a filename or a block, since this is the only
+ way the user has of requiring the extension to be used. */
+ if ((is_a_field_of_this.type == NULL && !is_after_structop)
+ || is_quoted_name)
+ {
+ /* See if it's a file name. */
+ if (auto symtab = lookup_symtab (current_program_space, copy.c_str ());
+ symtab != nullptr)
+ {
+ c_yylval.bval
+ = symtab->compunit ().blockvector ()->static_block ();
+
+ return FILENAME;
+ }
+ }
+ }
+
+ if (bsym.symbol && bsym.symbol->loc_class () == LOC_TYPEDEF)
+ {
+ c_yylval.tsym.type = bsym.symbol->type ();
+ return TYPENAME;
+ }
+
+ /* See if it's an ObjC classname. */
+ if (par_state->language ()->la_language == language_objc && !bsym.symbol)
+ {
+ CORE_ADDR Class = lookup_objc_class (par_state->gdbarch (),
+ copy.c_str ());
+ if (Class)
+ {
+ struct symbol *sym;
+
+ c_yylval.theclass.theclass = Class;
+ sym = lookup_struct_noerr (copy.c_str (),
+ par_state->expression_context_block);
+ if (sym)
+ c_yylval.theclass.type = sym->type ();
+ return CLASSNAME;
+ }
+ }
+
+ /* 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 (!bsym.symbol
+ && ((copy[0] >= 'a' && copy[0] < 'a' + input_radix - 10)
+ || (copy[0] >= 'A' && copy[0] < 'A' + input_radix - 10)))
+ {
+ c_exp_parser_YYSTYPE newlval; /* Its value is ignored. */
+ int hextype = parse_number (par_state, copy.c_str (), c_yylval.sval.length,
+ 0, &newlval);
+
+ if (hextype == INT)
+ {
+ c_yylval.ssym.sym = bsym;
+ c_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ return NAME_OR_INT;
+ }
+ }
+
+ /* Any other kind of symbol */
+ c_yylval.ssym.sym = bsym;
+ c_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+
+ if (bsym.symbol == NULL
+ && par_state->language ()->la_language == language_cplus
+ && is_a_field_of_this.type == NULL
+ && lookup_minimal_symbol (current_program_space, copy.c_str ()).minsym == nullptr)
+ return UNKNOWN_CPP_NAME;
+
+ return NAME;
+}
+
+/* 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, false, false);
+
+ type = check_typedef (context);
+ if (!type_aggregate_p (type))
+ return ERROR;
+
+ std::string copy = copy_name (c_yylval.ssym.stoken);
+ /* N.B. We assume the symbol can only be in VAR_DOMAIN. */
+ c_yylval.ssym.sym = cp_lookup_nested_symbol (type, copy.c_str (), block,
+ SEARCH_VFT);
+
+ /* If no symbol was found, search for a matching base class named
+ COPY. This will allow users to enter qualified names of class members
+ relative to the `this' pointer. */
+ if (c_yylval.ssym.sym.symbol == NULL)
+ {
+ struct type *base_type = cp_find_type_baseclass_by_name (type,
+ copy.c_str ());
+
+ if (base_type != NULL)
+ {
+ c_yylval.tsym.type = base_type;
+ return TYPENAME;
+ }
+
+ return ERROR;
+ }
+
+ switch (c_yylval.ssym.sym.symbol->loc_class ())
+ {
+ case LOC_BLOCK:
+ case LOC_LABEL:
+ /* cp_lookup_nested_symbol might have accidentally found a constructor
+ named COPY when we really wanted a base class of the same name.
+ Double-check this case by looking for a base class. */
+ {
+ struct type *base_type
+ = cp_find_type_baseclass_by_name (type, copy.c_str ());
+
+ if (base_type != NULL)
+ {
+ c_yylval.tsym.type = base_type;
+ return TYPENAME;
+ }
+ }
+ return ERROR;
+
+ case LOC_TYPEDEF:
+ c_yylval.tsym.type = c_yylval.ssym.sym.symbol->type ();
+ return TYPENAME;
+
+ default:
+ return NAME;
+ }
+ internal_error (_("not reached"));
+}
+
+/* See c-exp-parser.h. */
+
+void
+handle_qualified_field_name (qualified_name_token token)
+{
+ struct type *type = nullptr;
+ std::string accum;
+ for (const auto name : split_name (token.prefix, split_style::CXX))
+ {
+ std::string current (name);
+
+ if (accum.empty ())
+ accum = name;
+ else
+ accum = accum + "::" + current;
+
+ c_yylval.ssym.stoken.ptr = current.c_str ();
+ c_yylval.ssym.stoken.length = current.size ();
+ c_yylval.ssym.sym = {};
+ c_yylval.ssym.is_a_field_of_this = 0;
+
+ int kind = classify_inner_name (pstate,
+ pstate->expression_context_block,
+ type);
+ if (kind != TYPENAME)
+ error (_("could not find type '%s'"), accum.c_str ());
+
+ type = c_yylval.tsym.type;
+ }
+
+ type = check_typedef (type);
+ if (!type_aggregate_p (type))
+ error (_("`%s' is not defined as an aggregate type."),
+ type->safe_name ());
+ if (token.name[0] == '~')
+ destructor_name_p (token.name, type);
+ pstate->push_new<expr::scope_operation> (type, token.name);
+}
+
+/* See c-exp-parser.h. */
+
+int
+c_yylex ()
+{
+ c_token_and_value current;
+ int first_was_coloncolon, last_was_coloncolon;
+ struct type *context_type = NULL;
+ int last_to_examine, next_to_examine, checkpoint;
+ const struct block *search_block;
+ bool is_quoted_name, last_lex_was_structop;
+
+ if (popping && !token_fifo.empty ())
+ goto do_pop;
+ popping = 0;
+
+ last_lex_was_structop = last_was_structop;
+
+ /* Read the first token and decide what to do. Most of the
+ subsequent code is C++-only; but also depends on seeing a "::" or
+ name-like token. */
+ current.token = lex_one_token (pstate, &is_quoted_name);
+ if (cpstate->assume_classification == TYPE_CODE_UNDEF
+ && current.token == NAME)
+ current.token = classify_name (pstate, pstate->expression_context_block,
+ is_quoted_name, last_lex_was_structop);
+ if (pstate->language ()->la_language != language_cplus
+ || (current.token != TYPENAME && current.token != COLONCOLON
+ && current.token != FILENAME
+ && (cpstate->assume_classification == TYPE_CODE_UNDEF
+ || current.token != NAME))
+ || cpstate->assume_classification == TYPE_CODE_VOID)
+ return current.token;
+
+ /* Read any sequence of alternating "::" and name-like tokens into
+ the token FIFO. */
+ current.value = c_yylval;
+ token_fifo.push_back (current);
+ last_was_coloncolon = current.token == COLONCOLON;
+ while (1)
+ {
+ bool ignore;
+
+ /* We ignore quoted names other than the very first one.
+ Subsequent ones do not have any special meaning. */
+ current.token = lex_one_token (pstate, &ignore);
+ current.value = c_yylval;
+ token_fifo.push_back (current);
+
+ if ((last_was_coloncolon && current.token != NAME)
+ || (!last_was_coloncolon && current.token != COLONCOLON))
+ break;
+ last_was_coloncolon = !last_was_coloncolon;
+ }
+ 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;
+
+ name_obstack.clear ();
+ checkpoint = 0;
+ if (current.token == FILENAME)
+ search_block = current.value.bval;
+ else if (current.token == COLONCOLON)
+ search_block = NULL;
+ else
+ {
+ gdb_assert (current.token == TYPENAME
+ || cpstate->assume_classification != TYPE_CODE_UNDEF);
+ 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;
+ }
+
+ first_was_coloncolon = current.token == COLONCOLON;
+ last_was_coloncolon = first_was_coloncolon;
+
+ while (next_to_examine <= last_to_examine)
+ {
+ c_token_and_value next;
+
+ next = token_fifo[next_to_examine];
+ ++next_to_examine;
+
+ if (next.token == NAME && last_was_coloncolon)
+ {
+ int classification;
+
+ c_yylval = next.value;
+ if (cpstate->assume_classification != TYPE_CODE_UNDEF)
+ classification = NAME;
+ else
+ 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 != NAME)
+ break;
+
+ /* Accept up to this token. */
+ checkpoint = next_to_examine;
+
+ /* Update the partial name we are constructing. */
+ if (next_to_examine > 1)
+ {
+ /* 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);
+
+ c_yylval.sval.ptr = (const char *) obstack_base (&name_obstack);
+ c_yylval.sval.length = obstack_object_size (&name_obstack);
+ current.value = c_yylval;
+ current.token = classification;
+
+ last_was_coloncolon = 0;
+
+ if (cpstate->assume_classification == TYPE_CODE_UNDEF
+ && classification == NAME)
+ break;
+
+ context_type = c_yylval.tsym.type;
+ }
+ else if (next.token == COLONCOLON && !last_was_coloncolon)
+ last_was_coloncolon = 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)
+ {
+ current.value.sval.ptr
+ = obstack_strndup (&cpstate->expansion_obstack,
+ current.value.sval.ptr,
+ current.value.sval.length);
+
+ 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 ());
+ c_yylval = current.value;
+ return current.token;
+}
+
+/* See c-exp-parser.h. */
+
+void
+c_yyerror (const char *msg)
+{
+ pstate->parse_error (msg);
+}
+
+
+} /* namespace c_exp_parser */
+
+/* See c-exp-parser.h. */
+
+int
+c_parse (struct parser_state *par_state)
+{
+ using namespace c_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate);
+ gdb_assert (par_state != NULL);
+ pstate = par_state;
+
+ c_parse_state cstate;
+ scoped_restore cstate_restore = make_scoped_restore (&cpstate, &cstate);
+
+ macro_scope macro_scope;
+
+ if (par_state->expression_context_block)
+ macro_scope
+ = sal_macro_scope (find_sal_for_pc (par_state->expression_context_pc, 0));
+ else
+ macro_scope = default_macro_scope ();
+ if (!macro_scope.is_valid ())
+ macro_scope = user_macro_scope ();
+
+ scoped_restore restore_macro_scope
+ = make_scoped_restore (&expression_macro_scope, ¯o_scope);
+
+ scoped_restore restore_yydebug = make_scoped_restore (&c_yydebug,
+ par_state->debug);
+
+ /* Initialize some state used by the lexer. */
+ last_was_structop = false;
+ saw_name_at_eof = 0;
+ paren_depth = 0;
+
+ token_fifo.clear ();
+ popping = 0;
+ name_obstack.clear ();
+
+ int result = c_yyparse ();
+ if (!result)
+ pstate->set_operation (pstate->pop ());
+ return result;
+}
+
+/* See c-exp-parser.h. */
+
+int
+c_parse_escape (const char **ptr, struct obstack *output)
+{
+ const char *tokptr = *ptr;
+ int result = 1;
+
+ /* Some escape sequences undergo character set conversion. Those we
+ translate here. */
+ switch (*tokptr)
+ {
+ /* Hex escapes do not undergo character set conversion, so keep
+ the escape sequence for later. */
+ case 'x':
+ if (output)
+ obstack_grow_str (output, "\\x");
+ ++tokptr;
+ if (!c_isxdigit (*tokptr))
+ error (_("\\x escape without a following hex digit"));
+ while (c_isxdigit (*tokptr))
+ {
+ if (output)
+ obstack_1grow (output, *tokptr);
+ ++tokptr;
+ }
+ break;
+
+ /* Octal escapes do not undergo character set conversion, so
+ keep the escape sequence for later. */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int i;
+ if (output)
+ obstack_grow_str (output, "\\");
+ for (i = 0;
+ i < 3 && c_isdigit (*tokptr) && *tokptr != '8' && *tokptr != '9';
+ ++i)
+ {
+ if (output)
+ obstack_1grow (output, *tokptr);
+ ++tokptr;
+ }
+ }
+ break;
+
+ /* We handle UCNs later. We could handle them here, but that
+ would mean a spurious error in the case where the UCN could
+ be converted to the target charset but not the host
+ charset. */
+ case 'u':
+ case 'U':
+ {
+ char c = *tokptr;
+ int i, len = c == 'U' ? 8 : 4;
+ if (output)
+ {
+ obstack_1grow (output, '\\');
+ obstack_1grow (output, *tokptr);
+ }
+ ++tokptr;
+ if (!c_isxdigit (*tokptr))
+ error (_("\\%c escape without a following hex digit"), c);
+ for (i = 0; i < len && c_isxdigit (*tokptr); ++i)
+ {
+ if (output)
+ obstack_1grow (output, *tokptr);
+ ++tokptr;
+ }
+ }
+ break;
+
+ /* We must pass backslash through so that it does not
+ cause quoting during the second expansion. */
+ case '\\':
+ if (output)
+ obstack_grow_str (output, "\\\\");
+ ++tokptr;
+ break;
+
+ /* Escapes which undergo conversion. */
+ case 'a':
+ if (output)
+ obstack_1grow (output, '\a');
+ ++tokptr;
+ break;
+ case 'b':
+ if (output)
+ obstack_1grow (output, '\b');
+ ++tokptr;
+ break;
+ case 'f':
+ if (output)
+ obstack_1grow (output, '\f');
+ ++tokptr;
+ break;
+ case 'n':
+ if (output)
+ obstack_1grow (output, '\n');
+ ++tokptr;
+ break;
+ case 'r':
+ if (output)
+ obstack_1grow (output, '\r');
+ ++tokptr;
+ break;
+ case 't':
+ if (output)
+ obstack_1grow (output, '\t');
+ ++tokptr;
+ break;
+ case 'v':
+ if (output)
+ obstack_1grow (output, '\v');
+ ++tokptr;
+ break;
+
+ /* GCC extension. */
+ case 'e':
+ if (output)
+ obstack_1grow (output, HOST_ESCAPE_CHAR);
+ ++tokptr;
+ break;
+
+ /* Backslash-newline expands to nothing at all. */
+ case '\n':
+ ++tokptr;
+ result = 0;
+ break;
+
+ /* A few escapes just expand to the character itself. */
+ case '\'':
+ case '\"':
+ case '?':
+ /* GCC extensions. */
+ case '(':
+ case '{':
+ case '[':
+ case '%':
+ /* Unrecognized escapes turn into the character itself. */
+ default:
+ if (output)
+ obstack_1grow (output, *tokptr);
+ ++tokptr;
+ break;
+ }
+ *ptr = tokptr;
+ return result;
+}
diff --git a/gdb/c-exp-parser.h b/gdb/c-exp-parser.h
new file mode 100644
index 000000000000..5128da1f19ae
--- /dev/null
+++ b/gdb/c-exp-parser.h
@@ -0,0 +1,182 @@
+/* Support code for the C expression parser, for GDB.
+
+ Copyright (C) 1986-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_C_EXP_PARSER_H
+#define GDB_C_EXP_PARSER_H
+
+#include "gdbsupport/gdb_obstack.h"
+#include "parser-defs.h"
+#include "type-stack.h"
+
+union c_exp_parser_YYSTYPE;
+
+namespace c_exp_parser {
+
+/* Data that must be held for the duration of a parse. */
+
+struct c_parse_state
+{
+ /* These are used to hold type lists and type stacks that are
+ allocated during the parse. */
+ std::vector<std::unique_ptr<std::vector<struct type *>>> type_lists;
+ std::vector<std::unique_ptr<struct type_stack>> type_stacks;
+
+ /* Storage for some strings allocated during the parse. */
+ std::vector<gdb::unique_xmalloc_ptr<char>> strings;
+
+ /* When we find that lexptr (the global var defined in parse.c) is
+ pointing at a macro invocation, we expand the invocation, and call
+ scan_macro_expansion to save the old lexptr here and point lexptr
+ into the expanded text. When we reach the end of that, we call
+ end_macro_expansion to pop back to the value we saved here. The
+ macro expansion code promises to return only fully-expanded text,
+ so we don't need to "push" more than one level.
+
+ This is disgusting, of course. It would be cleaner to do all macro
+ expansion beforehand, and then hand that to lexptr. But we don't
+ really know where the expression ends. Remember, in a command like
+
+ (gdb) break *ADDRESS if CONDITION
+
+ we evaluate ADDRESS in the scope of the current frame, but we
+ evaluate CONDITION in the scope of the breakpoint's location. So
+ it's simply wrong to try to macro-expand the whole thing at once. */
+ const char *macro_original_text = nullptr;
+
+ /* We save all intermediate macro expansions on this obstack for the
+ duration of a single parse. The expansion text may sometimes have
+ to live past the end of the expansion, due to yacc lookahead.
+ Rather than try to be clever about saving the data for a single
+ token, we simply keep it all and delete it after parsing has
+ completed. */
+ auto_obstack expansion_obstack;
+
+ /* The type stack. */
+ struct type_stack type_stack;
+
+ /* When set, a name token is not looked up. This can be useful when
+ the search domain is known by context. TYPE_CODE_UNDEF is used
+ to mean "unset" here -- typically only types with tags (enum,
+ struct, class, union) use this feature, but TYPE_CODE_VOID is
+ also used to avoid the lookup for field names. */
+ type_code assume_classification = TYPE_CODE_UNDEF;
+};
+
+/* Used for field names, which skip name lookup. */
+struct qualified_name_token
+{
+ /* The prefix, if any. This can be nullptr. */
+ const char *prefix;
+ /* The field name itself. */
+ const char *name;
+ /* True if the COMPLETE token was seen. */
+ bool complete;
+};
+
+/* This is set and cleared in c_parse. */
+
+extern c_parse_state *cpstate;
+
+/* The state of the parser, used internally when we are parsing the
+ expression. */
+
+extern parser_state *pstate;
+
+/* 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.
+
+ This approach is still not ideal, because it mishandles template
+ types. See the comment in lex_one_token for an example. However,
+ this is still an improvement over the earlier approach, and will
+ suffice until we move to better parsing technology. */
+
+int c_yylex ();
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void c_yyerror (const char *msg);
+
+/* A helper function for the specific case of a qualified field name,
+ like "obj->type1::type2::field". This takes the type prefix
+ ("type1::type2" in the example) and finds the corresponding type.
+ It will either throw an exception, or push a scope_operation on the
+ operation stack. */
+
+void handle_qualified_field_name (qualified_name_token token);
+
+/* 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 *par_state, const char *buf, int len,
+ int parsed_float, c_exp_parser_YYSTYPE *putithere);
+
+/* Validate a parameter typelist. */
+
+void check_parameter_typelist (std::vector<struct type *> *params);
+
+/* Returns a stoken of the operator name given by OP (which does not
+ include the string "operator"). */
+
+struct stoken operator_stoken (const char *op);
+
+/* Returns a stoken of the type named TYPE. */
+
+qualified_name_token typename_stoken (const char *type);
+
+/* A convenient overload of copy_name. */
+static inline std::string
+copy_name (qualified_name_token token)
+{
+ if (token.prefix == nullptr)
+ return token.name;
+ return std::string (token.prefix) + "::" + token.name;
+}
+
+} /* namespace c_exp_parser */
+
+/* Parse a C 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 c_parse (struct parser_state *par_state);
+
+/* Parse a C escape sequence. The initial backslash of the sequence
+ is at (*PTR)[-1]. *PTR will be updated to point to just after the
+ last character of the sequence. If OUTPUT is not NULL, the
+ translated form of the escape sequence will be written there. If
+ OUTPUT is NULL, no output is written and the call will only affect
+ *PTR. If an escape sequence is expressed in target bytes, then the
+ entire sequence will simply be copied to OUTPUT. Return 1 if any
+ character was emitted, 0 otherwise. */
+
+int c_parse_escape (const char **ptr, struct obstack *output);
+
+#endif /* GDB_C_EXP_PARSER_H */
diff --git a/gdb/c-exp-parser.y b/gdb/c-exp-parser.y
index 9a1ecb3e6d3a..b24fee0058bf 100644
--- a/gdb/c-exp-parser.y
+++ b/gdb/c-exp-parser.y
@@ -39,110 +39,19 @@
#include "value.h"
#include "parser-defs.h"
#include "language.h"
+#include "c-exp-parser.h"
#include "c-lang.h"
-#include "c-support.h"
-#include "charset.h"
#include "block.h"
#include "cp-support.h"
-#include "macroscope.h"
#include "objc-lang.h"
#include "typeprint.h"
#include "cp-abi.h"
#include "type-stack.h"
#include "target-float.h"
#include "c-exp.h"
-#include "macroexp.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;
-
-/* Data that must be held for the duration of a parse. */
-
-struct c_parse_state
-{
- /* These are used to hold type lists and type stacks that are
- allocated during the parse. */
- std::vector<std::unique_ptr<std::vector<struct type *>>> type_lists;
- std::vector<std::unique_ptr<struct type_stack>> type_stacks;
-
- /* Storage for some strings allocated during the parse. */
- std::vector<gdb::unique_xmalloc_ptr<char>> strings;
-
- /* When we find that lexptr (the global var defined in parse.c) is
- pointing at a macro invocation, we expand the invocation, and call
- scan_macro_expansion to save the old lexptr here and point lexptr
- into the expanded text. When we reach the end of that, we call
- end_macro_expansion to pop back to the value we saved here. The
- macro expansion code promises to return only fully-expanded text,
- so we don't need to "push" more than one level.
-
- This is disgusting, of course. It would be cleaner to do all macro
- expansion beforehand, and then hand that to lexptr. But we don't
- really know where the expression ends. Remember, in a command like
-
- (gdb) break *ADDRESS if CONDITION
-
- we evaluate ADDRESS in the scope of the current frame, but we
- evaluate CONDITION in the scope of the breakpoint's location. So
- it's simply wrong to try to macro-expand the whole thing at once. */
- const char *macro_original_text = nullptr;
-
- /* We save all intermediate macro expansions on this obstack for the
- duration of a single parse. The expansion text may sometimes have
- to live past the end of the expansion, due to yacc lookahead.
- Rather than try to be clever about saving the data for a single
- token, we simply keep it all and delete it after parsing has
- completed. */
- auto_obstack expansion_obstack;
-
- /* The type stack. */
- struct type_stack type_stack;
-
- /* When set, a name token is not looked up. This can be useful when
- the search domain is known by context. TYPE_CODE_UNDEF is used
- to mean "unset" here -- typically only types with tags (enum,
- struct, class, union) use this feature, but TYPE_CODE_VOID is
- also used to avoid the lookup for field names. */
- type_code assume_classification = TYPE_CODE_UNDEF;
-};
-
-/* Used for field names, which skip name lookup. */
-struct qualified_name_token
-{
- /* The prefix, if any. This can be nullptr. */
- const char *prefix;
- /* The field name itself. */
- const char *name;
- /* True if the COMPLETE token was seen. */
- bool complete;
-};
-
-/* A convenient overload of copy_name. */
-static std::string
-copy_name (qualified_name_token token)
-{
- if (token.prefix == nullptr)
- return token.name;
- return std::string (token.prefix) + "::" + token.name;
-}
-
-/* This is set and cleared in c_parse. */
-
-static struct c_parse_state *cpstate;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
-
-static int type_aggregate_p (struct type *);
-
-static void handle_qualified_field_name (qualified_name_token token);
-
+using namespace c_exp_parser;
using namespace expr;
%}
@@ -163,7 +72,7 @@ using namespace expr;
} typed_val_float;
struct type *tval;
struct stoken sval;
- qualified_name_token qval;
+ c_exp_parser::qualified_name_token qval;
struct typed_stoken tsval;
struct ttype tsym;
struct symtoken ssym;
@@ -181,12 +90,6 @@ using namespace expr;
%{
/* YYSTYPE gets defined by %union */
-static int parse_number (struct parser_state *par_state,
- const char *, int, int, YYSTYPE *);
-static struct stoken operator_stoken (const char *);
-static qualified_name_token typename_stoken (const char *);
-static void check_parameter_typelist (std::vector<struct type *> *);
-
#if defined(YYBISON) && YYBISON < 30800
static void c_print_token (FILE *file, int type, YYSTYPE value);
#define YYPRINT(FILE, TYPE, VALUE) c_print_token (FILE, TYPE, VALUE)
@@ -1901,1666 +1804,8 @@ name_not_typename : NAME
%%
-/* Returns a stoken of the operator name given by OP (which does not
- include the string "operator"). */
-
-static struct stoken
-operator_stoken (const char *op)
-{
- struct stoken st = { NULL, 0 };
- char *buf;
-
- st.length = CP_OPERATOR_LEN + strlen (op);
- buf = (char *) malloc (st.length + 1);
- strcpy (buf, CP_OPERATOR_STR);
- strcat (buf, op);
- st.ptr = buf;
-
- /* The toplevel (c_parse) will free the memory allocated here. */
- cpstate->strings.emplace_back (buf);
- return st;
-};
-
-/* Returns a stoken of the type named TYPE. */
-
-static qualified_name_token
-typename_stoken (const char *type)
-{
- return qualified_name_token { nullptr, type, false };
-};
-
-/* 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_NAMESPACE
- || (type->code () == TYPE_CODE_ENUM
- && type->is_declared_class ()));
-}
-
-/* Validate a parameter typelist. */
-
-static void
-check_parameter_typelist (std::vector<struct type *> *params)
-{
- struct type *type;
- int ix;
-
- for (ix = 0; ix < params->size (); ++ix)
- {
- type = (*params)[ix];
- if (type != NULL && check_typedef (type)->code () == TYPE_CODE_VOID)
- {
- if (ix == 0)
- {
- if (params->size () == 1)
- {
- /* Ok. */
- break;
- }
- error (_("parameter types following 'void'"));
- }
- else
- error (_("'void' invalid as parameter 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 ***/
-
-static int
-parse_number (struct parser_state *par_state,
- const char *buf, 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;
-
- /* Imaginary number. */
- bool imaginary_p = false;
-
- /* We have found a "L" or "U" (or "i") suffix. */
- int found_suffix = 0;
-
- if (parsed_float)
- {
- if (len >= 1 && buf[len - 1] == 'i')
- {
- imaginary_p = true;
- --len;
- }
-
- /* Handle suffixes for decimal floating-point: "df", "dd" or "dl". */
- if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'f')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_decfloat;
- len -= 2;
- }
- else if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'd')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_decdouble;
- len -= 2;
- }
- else if (len >= 2 && buf[len - 2] == 'd' && buf[len - 1] == 'l')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_declong;
- len -= 2;
- }
- /* Handle suffixes: 'f' for float, 'l' for long double. */
- else if (len >= 1 && c_tolower (buf[len - 1]) == 'f')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_float;
- len -= 1;
- }
- else if (len >= 1 && c_tolower (buf[len - 1]) == 'l')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_long_double;
- len -= 1;
- }
- /* Default type for floating-point literals is double. */
- else
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_double;
- }
-
- if (!parse_float (buf, len,
- putithere->typed_val_float.type,
- putithere->typed_val_float.val))
- return ERROR;
-
- if (imaginary_p)
- putithere->typed_val_float.type
- = init_complex_type (nullptr, putithere->typed_val_float.type);
-
- return imaginary_p ? COMPLEX_FLOAT : FLOAT;
- }
-
- /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
- if (buf[0] == '0' && len > 1)
- switch (buf[1])
- {
- case 'x':
- case 'X':
- if (len >= 3)
- {
- buf += 2;
- base = 16;
- len -= 2;
- }
- break;
-
- case 'b':
- case 'B':
- if (len >= 3)
- {
- buf += 2;
- base = 2;
- len -= 2;
- }
- break;
-
- case 't':
- case 'T':
- case 'd':
- case 'D':
- if (len >= 3)
- {
- buf += 2;
- base = 10;
- len -= 2;
- }
- break;
-
- default:
- base = 8;
- break;
- }
-
- while (len-- > 0)
- {
- c = *buf++;
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- if (c != 'l' && c != 'u' && c != 'i')
- 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 if (c == 'i')
- {
- imaginary_p = true;
- 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' && c != 'i')
- {
- /* Test for overflow. */
- if (prevn == 0 && n == 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
- /* No 'u' suffix. */
- = !unsigned_p;
- bool have_unsigned
- = ((/* 'u' suffix. */
- unsigned_p)
- || (/* Not a decimal. */
- base != 10)
- || (/* Allowed as a convenience, in case decimal doesn't fit in largest
- signed type. */
- !fits_in_type (1, n, long_long_bits, true)));
- bool have_int
- /* No 'l' or 'll' suffix. */
- = long_p == 0;
- bool have_long
- /* No 'll' suffix. */
- = 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 && have_unsigned && 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 && have_unsigned && 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 (have_unsigned && 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;
-
- if (imaginary_p)
- putithere->typed_val_int.type
- = init_complex_type (nullptr, putithere->typed_val_int.type);
-
- return imaginary_p ? COMPLEX_INT : INT;
-}
-
-/* Temporary obstack used for holding strings. */
-static struct obstack tempbuf;
-static int tempbuf_init;
-
-/* Parse a C escape sequence. The initial backslash of the sequence
- is at (*PTR)[-1]. *PTR will be updated to point to just after the
- last character of the sequence. If OUTPUT is not NULL, the
- translated form of the escape sequence will be written there. If
- OUTPUT is NULL, no output is written and the call will only affect
- *PTR. If an escape sequence is expressed in target bytes, then the
- entire sequence will simply be copied to OUTPUT. Return 1 if any
- character was emitted, 0 otherwise. */
-
-int
-c_parse_escape (const char **ptr, struct obstack *output)
-{
- const char *tokptr = *ptr;
- int result = 1;
-
- /* Some escape sequences undergo character set conversion. Those we
- translate here. */
- switch (*tokptr)
- {
- /* Hex escapes do not undergo character set conversion, so keep
- the escape sequence for later. */
- case 'x':
- if (output)
- obstack_grow_str (output, "\\x");
- ++tokptr;
- if (!c_isxdigit (*tokptr))
- error (_("\\x escape without a following hex digit"));
- while (c_isxdigit (*tokptr))
- {
- if (output)
- obstack_1grow (output, *tokptr);
- ++tokptr;
- }
- break;
-
- /* Octal escapes do not undergo character set conversion, so
- keep the escape sequence for later. */
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- {
- int i;
- if (output)
- obstack_grow_str (output, "\\");
- for (i = 0;
- i < 3 && c_isdigit (*tokptr) && *tokptr != '8' && *tokptr != '9';
- ++i)
- {
- if (output)
- obstack_1grow (output, *tokptr);
- ++tokptr;
- }
- }
- break;
-
- /* We handle UCNs later. We could handle them here, but that
- would mean a spurious error in the case where the UCN could
- be converted to the target charset but not the host
- charset. */
- case 'u':
- case 'U':
- {
- char c = *tokptr;
- int i, len = c == 'U' ? 8 : 4;
- if (output)
- {
- obstack_1grow (output, '\\');
- obstack_1grow (output, *tokptr);
- }
- ++tokptr;
- if (!c_isxdigit (*tokptr))
- error (_("\\%c escape without a following hex digit"), c);
- for (i = 0; i < len && c_isxdigit (*tokptr); ++i)
- {
- if (output)
- obstack_1grow (output, *tokptr);
- ++tokptr;
- }
- }
- break;
-
- /* We must pass backslash through so that it does not
- cause quoting during the second expansion. */
- case '\\':
- if (output)
- obstack_grow_str (output, "\\\\");
- ++tokptr;
- break;
-
- /* Escapes which undergo conversion. */
- case 'a':
- if (output)
- obstack_1grow (output, '\a');
- ++tokptr;
- break;
- case 'b':
- if (output)
- obstack_1grow (output, '\b');
- ++tokptr;
- break;
- case 'f':
- if (output)
- obstack_1grow (output, '\f');
- ++tokptr;
- break;
- case 'n':
- if (output)
- obstack_1grow (output, '\n');
- ++tokptr;
- break;
- case 'r':
- if (output)
- obstack_1grow (output, '\r');
- ++tokptr;
- break;
- case 't':
- if (output)
- obstack_1grow (output, '\t');
- ++tokptr;
- break;
- case 'v':
- if (output)
- obstack_1grow (output, '\v');
- ++tokptr;
- break;
-
- /* GCC extension. */
- case 'e':
- if (output)
- obstack_1grow (output, HOST_ESCAPE_CHAR);
- ++tokptr;
- break;
-
- /* Backslash-newline expands to nothing at all. */
- case '\n':
- ++tokptr;
- result = 0;
- break;
-
- /* A few escapes just expand to the character itself. */
- case '\'':
- case '\"':
- case '?':
- /* GCC extensions. */
- case '(':
- case '{':
- case '[':
- case '%':
- /* Unrecognized escapes turn into the character itself. */
- default:
- if (output)
- obstack_1grow (output, *tokptr);
- ++tokptr;
- break;
- }
- *ptr = tokptr;
- return result;
-}
-
-/* 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;
- c_string_type type;
- int is_objc = 0;
-
- /* 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);
-
- /* Record the string type. */
- if (*tokptr == 'L')
- {
- type = C_WIDE_STRING;
- ++tokptr;
- }
- else if (*tokptr == 'u')
- {
- type = C_STRING_16;
- ++tokptr;
- }
- else if (*tokptr == 'U')
- {
- type = C_STRING_32;
- ++tokptr;
- }
- else if (*tokptr == '@')
- {
- /* An Objective C string. */
- is_objc = 1;
- type = C_STRING;
- ++tokptr;
- }
- else
- type = C_STRING;
-
- /* Skip the quote. */
- quote = *tokptr;
- if (quote == '\'')
- type |= C_CHAR;
- ++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 = type;
- value->ptr = (char *) obstack_base (&tempbuf);
- value->length = obstack_object_size (&tempbuf);
-
- *outptr = tokptr;
-
- return quote == '"' ? (is_objc ? NSSTRING : STRING) : CHAR;
-}
-
-/* This is used to associate some attributes with a token. */
-
-enum token_flag
-{
- /* If this bit is set, the token is C++-only. */
-
- FLAG_CXX = 1,
-
- /* If this bit is set, the token is C-only. */
-
- FLAG_C = 2,
-
- /* If this bit is set, the token is conditional: if there is a
- symbol of the same name, then the token is a symbol; otherwise,
- the token is a keyword. */
-
- FLAG_SHADOW = 4
-};
-DEF_ENUM_FLAGS_TYPE (enum token_flag, token_flags);
-
-struct c_token
-{
- const char *oper;
- int token;
- enum exp_opcode opcode;
- token_flags flags;
-};
-
-static const struct c_token tokentab3[] =
- {
- {">>=", ASSIGN_MODIFY, BINOP_RSH, 0},
- {"<<=", ASSIGN_MODIFY, BINOP_LSH, 0},
- {"->*", ARROW_STAR, OP_NULL, FLAG_CXX},
- {"...", DOTDOTDOT, OP_NULL, 0}
- };
-
-static const struct c_token tokentab2[] =
- {
- {"+=", ASSIGN_MODIFY, BINOP_ADD, 0},
- {"-=", ASSIGN_MODIFY, BINOP_SUB, 0},
- {"*=", ASSIGN_MODIFY, BINOP_MUL, 0},
- {"/=", ASSIGN_MODIFY, BINOP_DIV, 0},
- {"%=", ASSIGN_MODIFY, BINOP_REM, 0},
- {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR, 0},
- {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND, 0},
- {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR, 0},
- {"++", INCREMENT, OP_NULL, 0},
- {"--", DECREMENT, OP_NULL, 0},
- {"->", ARROW, OP_NULL, 0},
- {"&&", ANDAND, OP_NULL, 0},
- {"||", OROR, OP_NULL, 0},
- /* "::" is *not* only C++: gdb overrides its meaning in several
- different ways, e.g., 'filename'::func, function::variable. */
- {"::", COLONCOLON, OP_NULL, 0},
- {"<<", LSH, OP_NULL, 0},
- {">>", RSH, OP_NULL, 0},
- {"==", EQUAL, OP_NULL, 0},
- {"!=", NOTEQUAL, OP_NULL, 0},
- {"<=", LEQ, OP_NULL, 0},
- {">=", GEQ, OP_NULL, 0},
- {".*", DOT_STAR, OP_NULL, FLAG_CXX}
- };
-
-/* Identifier-like tokens. Only type-specifiers than can appear in
- multi-word type names (for example 'double' can appear in 'long
- double') need to be listed here. type-specifiers that are only ever
- single word (like 'char') are handled by the classify_name function. */
-static const struct c_token ident_tokens[] =
- {
- {"unsigned", UNSIGNED, OP_NULL, 0},
- {"template", TEMPLATE, OP_NULL, FLAG_CXX},
- {"volatile", VOLATILE_KEYWORD, OP_NULL, 0},
- {"struct", STRUCT, OP_NULL, 0},
- {"signed", SIGNED_KEYWORD, OP_NULL, 0},
- {"sizeof", SIZEOF, OP_NULL, 0},
- {"_Alignof", ALIGNOF, OP_NULL, 0},
- {"alignof", ALIGNOF, OP_NULL, FLAG_CXX},
- {"double", DOUBLE_KEYWORD, OP_NULL, 0},
- {"float", FLOAT_KEYWORD, OP_NULL, 0},
- {"false", FALSEKEYWORD, OP_NULL, FLAG_CXX},
- {"class", CLASS, OP_NULL, FLAG_CXX},
- {"union", UNION, OP_NULL, 0},
- {"short", SHORT, OP_NULL, 0},
- {"const", CONST_KEYWORD, OP_NULL, 0},
- {"restrict", RESTRICT, OP_NULL, FLAG_C | FLAG_SHADOW},
- {"__restrict__", RESTRICT, OP_NULL, 0},
- {"__restrict", RESTRICT, OP_NULL, 0},
- {"_Atomic", ATOMIC, OP_NULL, 0},
- {"enum", ENUM, OP_NULL, 0},
- {"long", LONG, OP_NULL, 0},
- {"_Complex", COMPLEX, OP_NULL, 0},
- {"__complex__", COMPLEX, OP_NULL, 0},
-
- {"true", TRUEKEYWORD, OP_NULL, FLAG_CXX},
- {"int", INT_KEYWORD, OP_NULL, 0},
- {"new", NEW, OP_NULL, FLAG_CXX},
- {"delete", DELETE, OP_NULL, FLAG_CXX},
- {"operator", OPERATOR, OP_NULL, FLAG_CXX},
-
- {"and", ANDAND, OP_NULL, FLAG_CXX},
- {"and_eq", ASSIGN_MODIFY, BINOP_BITWISE_AND, FLAG_CXX},
- {"bitand", '&', OP_NULL, FLAG_CXX},
- {"bitor", '|', OP_NULL, FLAG_CXX},
- {"compl", '~', OP_NULL, FLAG_CXX},
- {"not", '!', OP_NULL, FLAG_CXX},
- {"not_eq", NOTEQUAL, OP_NULL, FLAG_CXX},
- {"or", OROR, OP_NULL, FLAG_CXX},
- {"or_eq", ASSIGN_MODIFY, BINOP_BITWISE_IOR, FLAG_CXX},
- {"xor", '^', OP_NULL, FLAG_CXX},
- {"xor_eq", ASSIGN_MODIFY, BINOP_BITWISE_XOR, FLAG_CXX},
-
- {"const_cast", CONST_CAST, OP_NULL, FLAG_CXX },
- {"dynamic_cast", DYNAMIC_CAST, OP_NULL, FLAG_CXX },
- {"static_cast", STATIC_CAST, OP_NULL, FLAG_CXX },
- {"reinterpret_cast", REINTERPRET_CAST, OP_NULL, FLAG_CXX },
-
- {"__typeof__", TYPEOF, OP_TYPEOF, 0 },
- {"__typeof", TYPEOF, OP_TYPEOF, 0 },
- {"typeof", TYPEOF, OP_TYPEOF, FLAG_SHADOW },
- {"__decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX },
- {"decltype", DECLTYPE, OP_DECLTYPE, FLAG_CXX | FLAG_SHADOW },
-
- {"typeid", TYPEID, OP_TYPEID, FLAG_CXX}
- };
-
-
-static void
-scan_macro_expansion (const char *expansion)
-{
- /* We'd better not be trying to push the stack twice. */
- gdb_assert (! cpstate->macro_original_text);
-
- /* Copy to the obstack. */
- const char *copy = obstack_strdup (&cpstate->expansion_obstack, expansion);
-
- /* Save the old lexptr value, so we can return to it when we're done
- parsing the expanded text. */
- cpstate->macro_original_text = pstate->lexptr;
- pstate->lexptr = copy;
-}
-
-static int
-scanning_macro_expansion (void)
-{
- return cpstate->macro_original_text != 0;
-}
-
-static void
-finished_macro_expansion (void)
-{
- /* There'd better be something to pop back to. */
- gdb_assert (cpstate->macro_original_text);
-
- /* Pop back to the original text. */
- pstate->lexptr = cpstate->macro_original_text;
- cpstate->macro_original_text = 0;
-}
-
-/* Return true iff the token represents a C++ cast operator. */
-
-static int
-is_cast_operator (const char *token, int len)
-{
- return (! strncmp (token, "dynamic_cast", len)
- || ! strncmp (token, "static_cast", len)
- || ! strncmp (token, "reinterpret_cast", len)
- || ! strncmp (token, "const_cast", len));
-}
-
-/* The scope used for macro expansion. */
-static struct macro_scope *expression_macro_scope;
-
-/* 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. */
-static bool last_was_structop;
-
-/* Depth of parentheses. */
-static int paren_depth;
-
-/* Lex an Objective-C @selector. Return true if lexed. In this case,
- sets the resulting token and updates the lex pointer. Otherwise
- returns false and updates nothing. */
-
-static bool
-lex_selector (const char **lex_ptr, struct stoken *token)
-{
- const char *p = *lex_ptr;
-
- if (!startswith (p, "selector"))
- return false;
-
- p += strlen ("selector");
- p = skip_spaces (p);
- if (*p != '(')
- return false;
- ++p;
-
- /* The selector name matches [A-Za-z0-9:_-]+. We could probably be
- a bit more refined but meh. */
- const char *start = p;
- while (c_isalnum (*p) || *p == ':' || *p == '_' || *p == '-')
- ++p;
- if (p == start)
- return false;
- const char *end = p;
-
- p = skip_spaces (p);
- if (*p != ')')
- return false;
- ++p;
-
- *lex_ptr = p;
- *token = { start, (int) (end - start) };
- return true;
-}
-
-/* Read one token, getting characters through lexptr. */
-
-static int
-lex_one_token (struct parser_state *par_state, bool *is_quoted_name)
-{
- int c;
- int namelen;
- const char *tokstart;
- bool saw_structop = last_was_structop;
-
- last_was_structop = false;
- *is_quoted_name = false;
-
- retry:
-
- /* Check if this is a macro invocation that we need to expand. */
- if (! scanning_macro_expansion ())
- {
- gdb::unique_xmalloc_ptr<char> expanded
- = macro_expand_next (&pstate->lexptr, *expression_macro_scope);
-
- if (expanded != nullptr)
- scan_macro_expansion (expanded.get ());
- }
-
- 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)
- {
- if ((token.flags & FLAG_CXX) != 0
- && par_state->language ()->la_language != language_cplus)
- break;
- gdb_assert ((token.flags & FLAG_C) == 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)
- {
- if ((token.flags & FLAG_CXX) != 0
- && par_state->language ()->la_language != language_cplus)
- break;
- gdb_assert ((token.flags & FLAG_C) == 0);
-
- pstate->lexptr += 2;
- yylval.opcode = token.opcode;
- if (token.token == ARROW)
- last_was_structop = 1;
- return token.token;
- }
-
- switch (c = *tokstart)
- {
- case 0:
- /* If we were just scanning the result of a macro expansion,
- then we need to resume scanning the original text.
- 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 (scanning_macro_expansion ())
- {
- finished_macro_expansion ();
- goto retry;
- }
- else if (saw_name_at_eof)
- {
- saw_name_at_eof = 0;
- return COMPLETE;
- }
- else if (par_state->parse_completion && saw_structop)
- return COMPLETE;
- else
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- pstate->lexptr++;
- goto retry;
-
- case '[':
- case '(':
- paren_depth++;
- pstate->lexptr++;
- if (par_state->language ()->la_language == language_objc
- && c == '[')
- return OBJC_LBRAC;
- return c;
-
- case ']':
- case ')':
- if (paren_depth == 0)
- return 0;
- paren_depth--;
- pstate->lexptr++;
- return c;
-
- case ',':
- if (pstate->comma_terminates
- && paren_depth == 0
- && ! scanning_macro_expansion ())
- return 0;
- pstate->lexptr++;
- return c;
-
- case '.':
- /* Might be a floating point number. */
- if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
- {
- last_was_structop = true;
- 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, got_p = 0, toktype;
- const char *p = tokstart;
- int hex = input_radix > 10;
-
- if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
- {
- p += 2;
- hex = 1;
- }
- else if (c == '0' && (p[1]=='t' || p[1]=='T' || p[1]=='d' || p[1]=='D'))
- {
- p += 2;
- hex = 0;
- }
-
- /* If the token includes the C++14 digits separator, we make a
- copy so that we don't have to handle the separator in
- parse_number. */
- std::optional<std::string> no_tick;
- 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 && !got_p && (*p == 'e' || *p == 'E'))
- got_dot = got_e = 1;
- else if (!got_e && !got_p && (*p == 'p' || *p == 'P'))
- got_dot = got_p = 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'))
- || (got_p && (p[-1] == 'p' || p[-1] == 'P')))
- && (*p == '-' || *p == '+'))
- {
- /* This is the sign of the exponent, not the end of
- the number. */
- }
- else if (*p == '\'')
- {
- if (!no_tick.has_value ())
- no_tick.emplace (tokstart, p);
- 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;
- if (no_tick.has_value ())
- no_tick->push_back (*p);
- }
- if (no_tick.has_value ())
- toktype = parse_number (par_state, no_tick->c_str (),
- no_tick->length (),
- got_dot | got_e | got_p, &yylval);
- else
- toktype = parse_number (par_state, tokstart, p - tokstart,
- got_dot | got_e | got_p, &yylval);
- if (toktype == ERROR)
- error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
- tokstart);
- pstate->lexptr = p;
- return toktype;
- }
-
- case '@':
- {
- const char *p = &tokstart[1];
-
- if (par_state->language ()->la_language == language_objc)
- {
- struct stoken sel_token;
- if (lex_selector (&p, &sel_token))
- {
- pstate->lexptr = p;
- yylval.sval = sel_token;
- return SELECTOR;
- }
- else if (*p == '"')
- goto parse_string;
- }
-
- while (c_isspace (*p))
- p++;
- size_t len = strlen ("entry");
- if (strncmp (p, "entry", len) == 0 && !c_ident_is_alnum (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 'L':
- case 'u':
- case 'U':
- if (tokstart[1] != '"' && tokstart[1] != '\'')
- break;
- [[fallthrough]];
- case '\'':
- case '"':
-
- parse_string:
- {
- int host_len;
- int result = parse_string_or_char (tokstart, &pstate->lexptr,
- &yylval.tsval, &host_len);
- if (result == CHAR)
- {
- if (host_len == 0)
- error (_("Empty character constant."));
- else if (host_len > 2 && c == '\'')
- {
- ++tokstart;
- namelen = pstate->lexptr - tokstart - 1;
- *is_quoted_name = true;
-
- goto tryname;
- }
- else if (host_len > 1)
- error (_("Invalid character constant."));
- }
- return result;
- }
- }
-
- if (!(c == '_' || c == '$' || c_ident_is_alpha (c)))
- /* 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_ident_is_alnum (c) || c == '<');)
- {
- /* Template parameter lists are part of the name.
- FIXME: This mishandles `print $a<4&&$a>3'. */
-
- if (c == '<')
- {
- if (! is_cast_operator (tokstart, namelen))
- {
- /* Scan ahead to get rest of the template specification. Note
- that we look ahead only when the '<' adjoins non-whitespace
- characters; for comparison expressions, e.g. "a < b > c",
- there must be spaces before the '<', etc. */
- const char *p = find_template_name_end (tokstart + namelen);
-
- if (p)
- namelen = p - tokstart;
- }
- break;
- }
- 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'
- && ! scanning_macro_expansion ())
- {
- 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')
- && ! scanning_macro_expansion ())
- {
- 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)
- {
- if ((token.flags & FLAG_CXX) != 0
- && par_state->language ()->la_language != language_cplus)
- break;
- if ((token.flags & FLAG_C) != 0
- && par_state->language ()->la_language != language_c
- && par_state->language ()->la_language != language_objc)
- break;
-
- if ((token.flags & FLAG_SHADOW) != 0)
- {
- struct field_of_this_result is_a_field_of_this;
-
- if (lookup_symbol (copy.c_str (),
- pstate->expression_context_block,
- SEARCH_VFT, &is_a_field_of_this).symbol
- != NULL)
- {
- /* The keyword is shadowed. */
- break;
- }
- }
-
- /* 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 && *pstate->lexptr == '\0')
- saw_name_at_eof = 1;
-
- yylval.ssym.stoken = yylval.sval;
- yylval.ssym.sym.symbol = NULL;
- yylval.ssym.sym.block = NULL;
- yylval.ssym.is_a_field_of_this = 0;
- return NAME;
-}
-
-/* An object of this type is pushed on a FIFO by the "outer" lexer. */
-struct c_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<c_token_and_value> token_fifo;
-
-/* Non-zero if the lexer should return tokens from the FIFO. */
-static int popping;
-
-/* Temporary storage for c_lex; this holds symbol names as they are
- built up. */
-static auto_obstack name_obstack;
-
-/* 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.
- IS_QUOTED_NAME is non-zero if the name token was originally quoted
- in single quotes. IS_AFTER_STRUCTOP is true if this name follows
- a structure operator -- either '.' or ARROW */
-
-static int
-classify_name (struct parser_state *par_state, const struct block *block,
- bool is_quoted_name, bool is_after_structop)
-{
- struct block_symbol bsym;
- struct field_of_this_result is_a_field_of_this;
-
- std::string copy = copy_name (yylval.sval);
-
- bsym = lookup_symbol (copy.c_str (), block, SEARCH_VFT,
- &is_a_field_of_this);
-
- if (bsym.symbol && bsym.symbol->loc_class () == LOC_BLOCK)
- {
- yylval.ssym.sym = bsym;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- return BLOCKNAME;
- }
- else if (!bsym.symbol)
- {
- /* If we found a field of 'this', we might have erroneously
- found a constructor where we wanted a type name. Handle this
- case by noticing that we found a constructor and then look up
- the type tag instead. */
- if (is_a_field_of_this.type != NULL
- && is_a_field_of_this.fn_field != NULL
- && TYPE_FN_FIELD_CONSTRUCTOR (is_a_field_of_this.fn_field->fn_fields,
- 0))
- {
- struct field_of_this_result inner_is_a_field_of_this;
-
- bsym = lookup_symbol (copy.c_str (), block, SEARCH_STRUCT_DOMAIN,
- &inner_is_a_field_of_this);
- if (bsym.symbol != NULL)
- {
- yylval.tsym.type = bsym.symbol->type ();
- return TYPENAME;
- }
- }
-
- /* If we found a field on the "this" object, or we are looking
- up a field on a struct, then we want to prefer it over a
- filename. However, if the name was quoted, then it is better
- to check for a filename or a block, since this is the only
- way the user has of requiring the extension to be used. */
- if ((is_a_field_of_this.type == NULL && !is_after_structop)
- || is_quoted_name)
- {
- /* See if it's a file name. */
- if (auto symtab = lookup_symtab (current_program_space, copy.c_str ());
- symtab != nullptr)
- {
- yylval.bval
- = symtab->compunit ().blockvector ()->static_block ();
-
- return FILENAME;
- }
- }
- }
-
- if (bsym.symbol && bsym.symbol->loc_class () == LOC_TYPEDEF)
- {
- yylval.tsym.type = bsym.symbol->type ();
- return TYPENAME;
- }
-
- /* See if it's an ObjC classname. */
- if (par_state->language ()->la_language == language_objc && !bsym.symbol)
- {
- CORE_ADDR Class = lookup_objc_class (par_state->gdbarch (),
- copy.c_str ());
- if (Class)
- {
- struct symbol *sym;
-
- yylval.theclass.theclass = Class;
- sym = lookup_struct_noerr (copy.c_str (),
- par_state->expression_context_block);
- if (sym)
- yylval.theclass.type = sym->type ();
- return CLASSNAME;
- }
- }
-
- /* 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 (!bsym.symbol
- && ((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 = bsym;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- return NAME_OR_INT;
- }
- }
-
- /* Any other kind of symbol */
- yylval.ssym.sym = bsym;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
-
- if (bsym.symbol == NULL
- && par_state->language ()->la_language == language_cplus
- && is_a_field_of_this.type == NULL
- && lookup_minimal_symbol (current_program_space, copy.c_str ()).minsym == nullptr)
- return UNKNOWN_CPP_NAME;
-
- return NAME;
-}
-
-/* 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, false, false);
-
- type = check_typedef (context);
- if (!type_aggregate_p (type))
- return ERROR;
-
- std::string copy = copy_name (yylval.ssym.stoken);
- /* N.B. We assume the symbol can only be in VAR_DOMAIN. */
- yylval.ssym.sym = cp_lookup_nested_symbol (type, copy.c_str (), block,
- SEARCH_VFT);
-
- /* If no symbol was found, search for a matching base class named
- COPY. This will allow users to enter qualified names of class members
- relative to the `this' pointer. */
- if (yylval.ssym.sym.symbol == NULL)
- {
- struct type *base_type = cp_find_type_baseclass_by_name (type,
- copy.c_str ());
-
- if (base_type != NULL)
- {
- yylval.tsym.type = base_type;
- return TYPENAME;
- }
-
- return ERROR;
- }
-
- switch (yylval.ssym.sym.symbol->loc_class ())
- {
- case LOC_BLOCK:
- case LOC_LABEL:
- /* cp_lookup_nested_symbol might have accidentally found a constructor
- named COPY when we really wanted a base class of the same name.
- Double-check this case by looking for a base class. */
- {
- struct type *base_type
- = cp_find_type_baseclass_by_name (type, copy.c_str ());
-
- if (base_type != NULL)
- {
- yylval.tsym.type = base_type;
- return TYPENAME;
- }
- }
- return ERROR;
-
- case LOC_TYPEDEF:
- yylval.tsym.type = yylval.ssym.sym.symbol->type ();
- return TYPENAME;
-
- default:
- return NAME;
- }
- internal_error (_("not reached"));
-}
-
-/* A helper function for the specific case of a qualified field name,
- like "obj->type1::type2::field". This takes the type prefix
- ("type1::type2" in the example) and finds the corresponding type.
- It will either throw an exception, or push a scope_operation on the
- operation stack. */
-static void
-handle_qualified_field_name (qualified_name_token token)
-{
- struct type *type = nullptr;
- std::string accum;
- for (const auto name : split_name (token.prefix, split_style::CXX))
- {
- std::string current (name);
-
- if (accum.empty ())
- accum = name;
- else
- accum = accum + "::" + current;
-
- yylval.ssym.stoken.ptr = current.c_str ();
- yylval.ssym.stoken.length = current.size ();
- yylval.ssym.sym = {};
- yylval.ssym.is_a_field_of_this = 0;
-
- int kind = classify_inner_name (pstate,
- pstate->expression_context_block,
- type);
- if (kind != TYPENAME)
- error (_("could not find type '%s'"), accum.c_str ());
-
- type = yylval.tsym.type;
- }
-
- type = check_typedef (type);
- if (!type_aggregate_p (type))
- error (_("`%s' is not defined as an aggregate type."),
- type->safe_name ());
- if (token.name[0] == '~')
- destructor_name_p (token.name, type);
- pstate->push_new<scope_operation> (type, token.name);
-}
-
-/* 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.
-
- This approach is still not ideal, because it mishandles template
- types. See the comment in lex_one_token for an example. However,
- this is still an improvement over the earlier approach, and will
- suffice until we move to better parsing technology. */
-
-static int
-yylex (void)
-{
- c_token_and_value current;
- int first_was_coloncolon, last_was_coloncolon;
- struct type *context_type = NULL;
- int last_to_examine, next_to_examine, checkpoint;
- const struct block *search_block;
- bool is_quoted_name, last_lex_was_structop;
-
- if (popping && !token_fifo.empty ())
- goto do_pop;
- popping = 0;
-
- last_lex_was_structop = last_was_structop;
-
- /* Read the first token and decide what to do. Most of the
- subsequent code is C++-only; but also depends on seeing a "::" or
- name-like token. */
- current.token = lex_one_token (pstate, &is_quoted_name);
- if (cpstate->assume_classification == TYPE_CODE_UNDEF
- && current.token == NAME)
- current.token = classify_name (pstate, pstate->expression_context_block,
- is_quoted_name, last_lex_was_structop);
- if (pstate->language ()->la_language != language_cplus
- || (current.token != TYPENAME && current.token != COLONCOLON
- && current.token != FILENAME
- && (cpstate->assume_classification == TYPE_CODE_UNDEF
- || current.token != NAME))
- || cpstate->assume_classification == TYPE_CODE_VOID)
- return current.token;
-
- /* Read any sequence of alternating "::" and name-like tokens into
- the token FIFO. */
- current.value = yylval;
- token_fifo.push_back (current);
- last_was_coloncolon = current.token == COLONCOLON;
- while (1)
- {
- bool ignore;
-
- /* We ignore quoted names other than the very first one.
- Subsequent ones do not have any special meaning. */
- current.token = lex_one_token (pstate, &ignore);
- current.value = yylval;
- token_fifo.push_back (current);
-
- if ((last_was_coloncolon && current.token != NAME)
- || (!last_was_coloncolon && current.token != COLONCOLON))
- break;
- last_was_coloncolon = !last_was_coloncolon;
- }
- 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;
-
- name_obstack.clear ();
- checkpoint = 0;
- if (current.token == FILENAME)
- search_block = current.value.bval;
- else if (current.token == COLONCOLON)
- search_block = NULL;
- else
- {
- gdb_assert (current.token == TYPENAME
- || cpstate->assume_classification != TYPE_CODE_UNDEF);
- 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;
- }
-
- first_was_coloncolon = current.token == COLONCOLON;
- last_was_coloncolon = first_was_coloncolon;
-
- while (next_to_examine <= last_to_examine)
- {
- c_token_and_value next;
-
- next = token_fifo[next_to_examine];
- ++next_to_examine;
-
- if (next.token == NAME && last_was_coloncolon)
- {
- int classification;
-
- yylval = next.value;
- if (cpstate->assume_classification != TYPE_CODE_UNDEF)
- classification = NAME;
- else
- 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 != NAME)
- break;
-
- /* Accept up to this token. */
- checkpoint = next_to_examine;
-
- /* Update the partial name we are constructing. */
- if (next_to_examine > 1)
- {
- /* 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 = (const char *) obstack_base (&name_obstack);
- yylval.sval.length = obstack_object_size (&name_obstack);
- current.value = yylval;
- current.token = classification;
-
- last_was_coloncolon = 0;
-
- if (cpstate->assume_classification == TYPE_CODE_UNDEF
- && classification == NAME)
- break;
-
- context_type = yylval.tsym.type;
- }
- else if (next.token == COLONCOLON && !last_was_coloncolon)
- last_was_coloncolon = 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)
- {
- current.value.sval.ptr
- = obstack_strndup (&cpstate->expansion_obstack,
- current.value.sval.ptr,
- current.value.sval.length);
-
- 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
-c_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;
-
- c_parse_state cstate;
- scoped_restore cstate_restore = make_scoped_restore (&cpstate, &cstate);
-
- macro_scope macro_scope;
-
- if (par_state->expression_context_block)
- macro_scope
- = sal_macro_scope (find_sal_for_pc (par_state->expression_context_pc, 0));
- else
- macro_scope = default_macro_scope ();
- if (!macro_scope.is_valid ())
- macro_scope = user_macro_scope ();
-
- scoped_restore restore_macro_scope
- = make_scoped_restore (&expression_macro_scope, ¯o_scope);
-
- scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
- par_state->debug);
-
- /* Initialize some state used by the lexer. */
- last_was_structop = false;
- 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;
-}
-
#if defined(YYBISON) && YYBISON < 30800
-
/* This is called via the YYPRINT macro when parser debugging is
enabled. It prints a token's value. */
@@ -3611,9 +1856,3 @@ c_print_token (FILE *file, int type, YYSTYPE value)
}
#endif
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index f4458f3566db..f12f80db9882 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -58,12 +58,6 @@ enum c_string_type_values : unsigned
DEF_ENUM_FLAGS_TYPE (enum c_string_type_values, c_string_type);
-/* Defined in c-exp-parser.y. */
-
-extern int c_parse (struct parser_state *);
-
-extern int c_parse_escape (const char **, struct obstack *);
-
/* Defined in c-typeprint.c */
/* Print TYPE to STREAM using syntax appropriate for LANGUAGE, a
diff --git a/gdb/d-exp-parser.y b/gdb/d-exp-parser.y
index ee23a6c3254c..c35d78b83140 100644
--- a/gdb/d-exp-parser.y
+++ b/gdb/d-exp-parser.y
@@ -43,6 +43,7 @@
#include "parser-defs.h"
#include "language.h"
#include "c-lang.h"
+#include "c-exp-parser.h"
#include "d-lang.h"
#include "charset.h"
#include "block.h"
diff --git a/gdb/go-exp-parser.y b/gdb/go-exp-parser.y
index ce29a00e1228..2ae357081b36 100644
--- a/gdb/go-exp-parser.y
+++ b/gdb/go-exp-parser.y
@@ -56,6 +56,7 @@
#include "parser-defs.h"
#include "language.h"
#include "c-lang.h"
+#include "c-exp-parser.h"
#include "go-lang.h"
#include "charset.h"
#include "block.h"
diff --git a/gdb/language.c b/gdb/language.c
index 97fb1dcf3fee..6f0492b90e69 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -42,6 +42,7 @@
#include "cp-support.h"
#include "frame.h"
#include "c-lang.h"
+#include "c-exp-parser.h"
#include <algorithm>
#include "gdbarch.h"
diff --git a/gdb/macroexp.c b/gdb/macroexp.c
index 82f1378e1535..fe53a9eaad9e 100644
--- a/gdb/macroexp.c
+++ b/gdb/macroexp.c
@@ -17,14 +17,10 @@
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 "gdbsupport/gdb_obstack.h"
#include "macrotab.h"
#include "macroexp.h"
#include "macroscope.h"
-#include "c-lang.h"
-
-
-\f
+#include "c-exp-parser.h"
/* A string type that we can use to refer to substrings of other
strings. */
--
2.55.0
next prev parent reply other threads:[~2026-09-04 17:06 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 ` simon.marchi [this message]
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 ` [PATCH 15/17] gdb: move go-exp-parser.y's support code to go-exp-parser.c simon.marchi
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-12-simon.marchi@polymtl.ca \
--to=simon.marchi@polymtl.ca \
--cc=gdb-patches@sourceware.org \
--cc=simon.marchi@efficios.com \
/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