* [PATCH v2 00/19] Move C++ support code out of .y files
@ 2026-09-05 4:23 simon.marchi
2026-09-05 4:23 ` [PATCH v2 01/19] gdb/ada-exp-parser: remove name_info struct simon.marchi
` (18 more replies)
0 siblings, 19 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Hi,
This is v2 of this series:
https://inbox.sourceware.org/gdb-patches/20260904170338.1643894-1-simon.marchi@polymtl.ca
The new version addresses the comments from Kevin.
- Patch 3 is new, to remove the YY_NULL to YY_NULLPTR substitution.
- Patch 13, previously 12, update comment to refer to
ada-exp-parser.c instead of ada-exp-parser.y.
- Patch 19 is new, to wire up "set debug parser" with the Modula-2
and Pascal parsers, so that m2_yydebug and pascal_yydebug are used.
Simon Marchi (19):
gdb/ada-exp-parser: remove name_info struct
gdb: replace parse_type macros with functions
gdb: suffix flex/bison output files with -gen.c
gdb: remove YY_NULL to YY_NULLPTR substitution
gdb: move parser output post-processing to a script
gdb: let the parser and lexer generators prefix their symbols
gdb: separate cp-name-parser's symbol prefix with an underscore
gdb: make $(YACC) and $(FLEX) generate headers
gdb: add check for stale build generated files
gdb: move cp-name-parser.y's support code to cp-name-parser.c
gdb: rename LANG-exp.y to LANG-exp-parser.y
gdb: move c-exp-parser.y's support code to c-exp-parser.c
gdb: move ada-exp-parser.y's support code to ada-exp-parser.c
gdb: move d-exp-parser.y's support code to d-exp-parser.c
gdb: move f-exp-parser.y's support code to f-exp-parser.c
gdb: move go-exp-parser.y's support code to go-exp-parser.c
gdb: move m2-exp-parser.y's support code to m2-exp-parser.c
gdb: move p-exp-parser.y's support code to p-exp-parser.c
gdb: honor "set debug parser" in the Modula-2 and Pascal parsers
gdb/.gitignore | 27 +-
gdb/Makefile.in | 194 +-
gdb/ada-exp-parser.c | 1351 +++++++++++
gdb/ada-exp-parser.h | 427 ++++
gdb/ada-exp-parser.y | 845 +++++++
gdb/ada-exp.h | 2 +-
gdb/ada-exp.y | 2019 ----------------
gdb/ada-lang.c | 1 +
gdb/ada-lang.h | 2 -
gdb/ada-lex.l | 431 +---
gdb/c-exp-parser.c | 1696 ++++++++++++++
gdb/c-exp-parser.h | 182 ++
gdb/c-exp-parser.y | 1858 +++++++++++++++
gdb/c-exp.y | 3626 -----------------------------
gdb/c-lang.h | 6 -
gdb/cp-name-parser.c | 1050 +++++++++
gdb/cp-name-parser.h | 130 ++
gdb/cp-name-parser.y | 1120 +--------
gdb/cp-support.c | 4 +-
gdb/{d-exp.y => d-exp-parser.c} | 738 +-----
gdb/d-exp-parser.h | 83 +
gdb/d-exp-parser.y | 600 +++++
gdb/d-lang.c | 1 +
gdb/d-lang.h | 4 -
gdb/{f-exp.y => f-exp-parser.c} | 982 +-------
gdb/f-exp-parser.h | 102 +
gdb/f-exp-parser.y | 766 ++++++
gdb/f-lang.c | 9 +
gdb/{go-exp.y => go-exp-parser.c} | 736 +-----
gdb/go-exp-parser.h | 59 +
gdb/go-exp-parser.y | 603 +++++
gdb/go-lang.c | 9 +
gdb/language.c | 1 +
gdb/m2-exp-parser.c | 491 ++++
gdb/m2-exp-parser.h | 67 +
gdb/{m2-exp.y => m2-exp-parser.y} | 467 +---
gdb/m2-lang.c | 9 +
gdb/macroexp.c | 6 +-
gdb/{p-exp.y => p-exp-parser.c} | 930 +-------
gdb/p-exp-parser.h | 84 +
gdb/p-exp-parser.y | 752 ++++++
gdb/p-lang.c | 9 +
gdb/parser-defs.h | 8 +
gdb/post-process-parser-output.sh | 66 +
gdb/yy-remap.h | 96 -
45 files changed, 11794 insertions(+), 10855 deletions(-)
create mode 100644 gdb/ada-exp-parser.c
create mode 100644 gdb/ada-exp-parser.h
create mode 100644 gdb/ada-exp-parser.y
delete mode 100644 gdb/ada-exp.y
create mode 100644 gdb/c-exp-parser.c
create mode 100644 gdb/c-exp-parser.h
create mode 100644 gdb/c-exp-parser.y
delete mode 100644 gdb/c-exp.y
create mode 100644 gdb/cp-name-parser.c
create mode 100644 gdb/cp-name-parser.h
rename gdb/{d-exp.y => d-exp-parser.c} (58%)
create mode 100644 gdb/d-exp-parser.h
create mode 100644 gdb/d-exp-parser.y
rename gdb/{f-exp.y => f-exp-parser.c} (53%)
create mode 100644 gdb/f-exp-parser.h
create mode 100644 gdb/f-exp-parser.y
rename gdb/{go-exp.y => go-exp-parser.c} (59%)
create mode 100644 gdb/go-exp-parser.h
create mode 100644 gdb/go-exp-parser.y
create mode 100644 gdb/m2-exp-parser.c
create mode 100644 gdb/m2-exp-parser.h
rename gdb/{m2-exp.y => m2-exp-parser.y} (53%)
rename gdb/{p-exp.y => p-exp-parser.c} (50%)
create mode 100644 gdb/p-exp-parser.h
create mode 100644 gdb/p-exp-parser.y
create mode 100755 gdb/post-process-parser-output.sh
delete mode 100644 gdb/yy-remap.h
base-commit: 002e6fb956c0be93efc91a8c16077eb3d4259dfc
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 01/19] gdb/ada-exp-parser: remove name_info struct
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 02/19] gdb: replace parse_type macros with functions simon.marchi
` (17 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
It is unused.
Change-Id: I8573a1224489058c0ba404235a577adf573b4e37
---
gdb/ada-exp.y | 7 -------
1 file changed, 7 deletions(-)
diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index a79d0ca83cb5..15f64fdf01f9 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -53,13 +53,6 @@
#define GDB_YY_REMAP_PREFIX ada_
#include "yy-remap.h"
-struct name_info {
- struct symbol *sym;
- struct minimal_symbol *msym;
- const struct block *block;
- struct stoken stoken;
-};
-
/* The state of the parser, used internally when we are parsing the
expression. */
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 02/19] gdb: replace parse_type macros with functions
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
2026-09-05 4:23 ` [PATCH v2 01/19] gdb/ada-exp-parser: remove name_info struct simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 03/19] gdb: suffix flex/bison output files with -gen.c simon.marchi
` (16 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
I noticed that many parsers defined the same parse_type macro, so I thought
let's move it to parser-defs.h so it can be shared. And while at it, change
it to a static inline function, as well as the other macros (the
language-specific ones).
Change-Id: I39160263f2ce63fdaddebf9f0b61ccaf94773e1d
---
gdb/ada-exp.y | 2 --
gdb/c-exp.y | 2 --
gdb/d-exp.y | 11 ++++++++---
gdb/f-exp.y | 11 ++++++++---
gdb/go-exp.y | 2 --
gdb/m2-exp.y | 11 ++++++++---
gdb/p-exp.y | 2 --
gdb/parser-defs.h | 8 ++++++++
8 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index 15f64fdf01f9..7fd0e6d7c7db 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -46,8 +46,6 @@
#include "ada-exp.h"
#include "cli/cli-style.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX ada_
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index e6a82e2aa3cc..d58bce833551 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -54,8 +54,6 @@
#include "macroexp.h"
#include "cli/cli-style.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX c_
diff --git a/gdb/d-exp.y b/gdb/d-exp.y
index 1031eb1eccac..d3263ba4fd72 100644
--- a/gdb/d-exp.y
+++ b/gdb/d-exp.y
@@ -50,9 +50,6 @@
#include "expop.h"
#include "cli/cli-style.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-#define parse_d_type(ps) builtin_d_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX d_
@@ -74,6 +71,14 @@ static void yyerror (const char *);
static int type_aggregate_p (struct type *);
+/* Return the D type table for the architecture associated to PS. */
+
+static inline const struct builtin_d_type *
+parse_d_type (parser_state *ps)
+{
+ return builtin_d_type (ps->gdbarch ());
+}
+
using namespace expr;
%}
diff --git a/gdb/f-exp.y b/gdb/f-exp.y
index 4216112c10b7..4cdfcc973923 100644
--- a/gdb/f-exp.y
+++ b/gdb/f-exp.y
@@ -52,9 +52,6 @@
#include "type-stack.h"
#include "f-exp.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-#define parse_f_type(ps) builtin_f_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX f_
@@ -97,6 +94,14 @@ static void fortran_wrap2_kind (type *base_type);
template<typename T>
static void fortran_wrap3_kind (type *base_type);
+/* Return the Fortran type table for the architecture associated to PS. */
+
+static inline const struct builtin_f_type *
+parse_f_type (parser_state *ps)
+{
+ return builtin_f_type (ps->gdbarch ());
+}
+
using namespace expr;
%}
diff --git a/gdb/go-exp.y b/gdb/go-exp.y
index 289fd81db857..199da6dc5c37 100644
--- a/gdb/go-exp.y
+++ b/gdb/go-exp.y
@@ -61,8 +61,6 @@
#include "block.h"
#include "expop.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX go_
diff --git a/gdb/m2-exp.y b/gdb/m2-exp.y
index 97e833ddf6ef..0889054f1f37 100644
--- a/gdb/m2-exp.y
+++ b/gdb/m2-exp.y
@@ -45,9 +45,6 @@
#include "block.h"
#include "m2-exp.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-#define parse_m2_type(ps) builtin_m2_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX m2_
@@ -69,6 +66,14 @@ static int parse_number (int);
/* The sign of the number being parsed. */
static int number_sign = 1;
+/* Return the Modula-2 type table for the architecture associated to PS. */
+
+static inline const struct builtin_m2_type *
+parse_m2_type (parser_state *ps)
+{
+ return builtin_m2_type (ps->gdbarch ());
+}
+
using namespace expr;
%}
diff --git a/gdb/p-exp.y b/gdb/p-exp.y
index 1a43837ae333..70c74beae438 100644
--- a/gdb/p-exp.y
+++ b/gdb/p-exp.y
@@ -51,8 +51,6 @@
#include "block.h"
#include "expop.h"
-#define parse_type(ps) builtin_type (ps->gdbarch ())
-
/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
etc). */
#define GDB_YY_REMAP_PREFIX pascal_
diff --git a/gdb/parser-defs.h b/gdb/parser-defs.h
index cfa9d9c111da..7e4f438e6f08 100644
--- a/gdb/parser-defs.h
+++ b/gdb/parser-defs.h
@@ -329,6 +329,14 @@ struct parser_state : public expr_builder
std::vector<expr::operation_up> m_operations;
};
+/* Return the type table for the architecture associated to PS. */
+
+static inline const struct builtin_type *
+parse_type (parser_state *ps)
+{
+ return builtin_type (ps->gdbarch ());
+}
+
/* A string token, either a char-string or bit-string. Char-strings are
used, for example, for the names of symbols. */
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 03/19] gdb: suffix flex/bison output files with -gen.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
2026-09-05 4:23 ` [PATCH v2 01/19] gdb/ada-exp-parser: remove name_info struct simon.marchi
2026-09-05 4:23 ` [PATCH v2 02/19] gdb: replace parse_type macros with functions simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 04/19] gdb: remove YY_NULL to YY_NULLPTR substitution simon.marchi
` (15 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
We already have some generated files using the -gen.c suffix. I think
it's useful to quickly spot that a file is generated and should
generally not be modified by hand. Change the output bison files to use
that suffix too, as well as ada-lex.c while at it.
The other reason I want to do this change is that I want to move as much
C++ code out of the .y files, into dedicated C++ files. Logically, I
would move code out from foo-exp.y into a new file called foo-exp.c.
Naming the generated file foo-exp-gen.c instead of foo-exp.c allows me
to do this.
Until now, our own %.c: %.y and %.c: %.l pattern rules overrode make's
built-in rules of the same name. Renaming ours to %-gen.c re-enables
the built-in ones, which is a problem for my intended use case of having
a hand-written <source>/foo-exp.c, as make would generate
<build>/foo-exp.c from <source>/foo-exp.y, thus hiding
<source>/foo-exp.c. Cancel the built-in rules to avoid this.
Change-Id: I2a80e9e4c73c6a19b0e5a86b1eb2e28b9e9ee6bb
---
gdb/.gitignore | 18 ++++++++---------
gdb/Makefile.in | 53 ++++++++++++++++++++++++++++---------------------
gdb/ada-exp.y | 4 ++--
3 files changed, 41 insertions(+), 34 deletions(-)
diff --git a/gdb/.gitignore b/gdb/.gitignore
index a68ed4e33178..c4d0f0595448 100644
--- a/gdb/.gitignore
+++ b/gdb/.gitignore
@@ -5,16 +5,16 @@
/version.c
/xml-builtin.c
-/ada-exp.c
-/ada-lex.c
-/c-exp.c
-/cp-name-parser.c
-/d-exp.c
-/f-exp.c
+/ada-exp-gen.c
+/ada-lex-gen.c
+/c-exp-gen.c
+/cp-name-parser-gen.c
+/d-exp-gen.c
+/f-exp-gen.c
/gdb
/gcore
-/go-exp.c
+/go-exp-gen.c
/init.c
/jit-reader.h
-/m2-exp.c
-/p-exp.c
+/m2-exp-gen.c
+/p-exp-gen.c
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d1574ec2d2cb..aeafb9394d7b 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -128,8 +128,8 @@ COMPILE = $(ECHO_CXX) $(COMPILE.pre) $(INTERNAL_CFLAGS) $(CXXFLAGS) \
YACC = @YACC@
-# This is used to rebuild ada-lex.c from ada-lex.l. If the program is
-# not defined, but ada-lex.c is present, compilation will continue,
+# This is used to rebuild ada-lex-gen.c from ada-lex.l. If the program
+# is not defined, but ada-lex-gen.c is present, compilation will continue,
# possibly with a warning.
FLEX = flex
@@ -2058,19 +2058,19 @@ REQUIRED_SUBDIRS = doc | testsuite | data-directory
# Parser intermediate files.
YYFILES = \
- ada-exp.c \
- ada-lex.c \
- c-exp.c \
- cp-name-parser.c \
- d-exp.c \
- f-exp.c \
- go-exp.c \
- m2-exp.c \
- p-exp.c
-
-# ada-lex.c is included by another file, so it shouldn't wind up as a
-# .o itself.
-YYOBJ = $(filter-out ada-lex.o,$(patsubst %.c,%.o,$(YYFILES)))
+ ada-exp-gen.c \
+ ada-lex-gen.c \
+ c-exp-gen.c \
+ cp-name-parser-gen.c \
+ d-exp-gen.c \
+ f-exp-gen.c \
+ go-exp-gen.c \
+ m2-exp-gen.c \
+ p-exp-gen.c
+
+# ada-lex-gen.c is included by another file, so it shouldn't wind up as
+# a .o itself.
+YYOBJ = $(filter-out ada-lex-gen.o,$(patsubst %.c,%.o,$(YYFILES)))
# Things which need to be built when making a distribution.
@@ -2079,7 +2079,7 @@ DISTSTUFF = $(YYFILES)
# All generated files which can be included by another file.
generated_files = \
- ada-lex.c \
+ ada-lex-gen.c \
config.h \
jit-reader.h \
$(NAT_GENERATED_FILES) \
@@ -2339,7 +2339,7 @@ INIT_FILES_FILTER_OUT = \
INIT_FILES = \
$(patsubst %.o,%.c, \
- $(patsubst %-exp.o,%-exp.y, \
+ $(patsubst %-exp-gen.o,%-exp.y, \
$(filter-out $(INIT_FILES_FILTER_OUT), $(COMMON_OBS))))
init.c: stamp-init; @true
@@ -2678,15 +2678,22 @@ po/$(PACKAGE).pot: force
#
# YACC/LEX dependencies
#
-# LANG-exp.c is generated in objdir from LANG-exp.y if it doesn't
-# exist in srcdir, then compiled in objdir to LANG-exp.o. If we
-# said LANG-exp.c rather than ./c-exp.c some makes would
-# sometimes re-write it into $(srcdir)/c-exp.c. Remove bogus
+# LANG-exp-gen.c is generated in objdir from LANG-exp.y if it doesn't
+# exist in srcdir, then compiled in objdir to LANG-exp-gen.o. If we
+# said LANG-exp-gen.c rather than ./c-exp-gen.c some makes would
+# sometimes re-write it into $(srcdir)/c-exp-gen.c. Remove bogus
# decls for malloc/realloc/free which conflict with everything else.
-# Strictly speaking c-exp.c should therefore depend on
+# Strictly speaking c-exp-gen.c should therefore depend on
# Makefile.in, but that was a pretty big annoyance.
+# Cancel make's built-in rules for producing a .c file from a .y or .l
+# file, to avoid them producing a <build>/foo-exp.c file from
+# <source>/foo-exp.y, when we also have a hand-written <source>/foo-exp.c
+# file.
%.c: %.y
+%.c: %.l
+
+%-gen.c: %.y
$(ECHO_YACC) $(SHELL) $(YLWRAP) $< y.tab.c $@.tmp -- \
$(YACC) $(YFLAGS) || (rm -f $@.tmp; false)
@sed -e '/extern.*malloc/d' \
@@ -2705,7 +2712,7 @@ po/$(PACKAGE).pot: force
< $@.tmp > $@.new && \
rm -f $@.tmp && \
mv $@.new $@
-%.c: %.l
+%-gen.c: %.l
$(ECHO_LEX) $(FLEX) -t $< > $@.tmp || (rm -f $@.tmp; false)
@sed -e '/extern.*malloc/d' \
-e '/extern.*realloc/d' \
diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index 7fd0e6d7c7db..bffe94a8de9b 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -1234,7 +1234,7 @@ primary : '*' primary %prec '.'
%%
-/* yylex defined in ada-lex.c: Reads one token, getting characters */
+/* yylex defined in ada-lex-gen.c: Reads one token, getting characters */
/* through lexptr. */
/* Remap normal flex interface names (yylex) as well as gratuitously */
@@ -1254,7 +1254,7 @@ primary : '*' primary %prec '.'
/* The following kludge was found necessary to prevent conflicts between */
/* defs.h and non-standard stdlib.h files. */
#define qsort __qsort__dummy
-#include "ada-lex.c"
+#include "ada-lex-gen.c"
int
ada_parse (struct parser_state *par_state)
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 04/19] gdb: remove YY_NULL to YY_NULLPTR substitution
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (2 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 03/19] gdb: suffix flex/bison output files with -gen.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 05/19] gdb: move parser output post-processing to a script simon.marchi
` (14 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
The rule generating parsers from .y files substitutes YY_NULL with
YY_NULLPTR. It was introduced by commit 722bcb33bf03 ("Replace YY_NULL with
YY_NULLPTR in LANG-exp.c", 2016-11-03), to fix a build failure with bison
2.6.4 (anything older than 3.0.2). This version emitted the macro:
#define YY_NULL nullptr
while flex emitted:
#define YY_NULL 0
Bison 3.0.2 started to use the name YY_NULLPTR instead, and the
substitution was us doing the same thing by hand, to circumvent the problem
when using older versions.
I think it is safe to assume that no GDB developer uses a > 13 year old
Bison, so we can remove this substitution.
Change-Id: I518dbb1794c9bdd8b016046639697e31168a9282
---
gdb/Makefile.in | 1 -
1 file changed, 1 deletion(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index aeafb9394d7b..dec887ff6095 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2705,7 +2705,6 @@ po/$(PACKAGE).pot: force
-e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
-e 's/\([ \t;,(]\)free$$/\1xfree/g' \
-e '/^#line.*y.tab.c/d' \
- -e 's/YY_NULL/YY_NULLPTR/g' \
-e "s/YYSTYPE/$(subst -,_,$*)_YYSTYPE/g" \
-e "s/yyalloc/$(subst -,_,$*)_yyalloc/g" \
-e "s/yysymbol_kind_t/$(subst -,_,$*)_yysymbol_kind_t/g" \
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 05/19] gdb: move parser output post-processing to a script
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (3 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 04/19] gdb: remove YY_NULL to YY_NULLPTR substitution simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 06/19] gdb: let the parser and lexer generators prefix their symbols simon.marchi
` (13 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
The output of bison and flex needs some post-processing before being
compiled. It is currently done by a sed invocation inlined in each of the
two pattern rules, which is kind of hard to read and modify. Additionally,
a following patch adds the generation of header files for parsers, and
those will need the same post processing. Moving the code to a dedicated
script makes re-use easier.
Move the code to post-process-parser-output.sh, which takes the name of the
parser generator (flex or bison), just to know which substitutions to do,
as well as the name of the parser file.
Make the Makefile rules depend on the script, so that the parsers are
regenerated when it changes.
Change-Id: I15f43184034aff33d242052db79586980ec2dac0
---
gdb/Makefile.in | 38 ++++++------------
gdb/post-process-parser-output.sh | 66 +++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+), 27 deletions(-)
create mode 100755 gdb/post-process-parser-output.sh
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index dec887ff6095..80b2f8573294 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2693,38 +2693,22 @@ po/$(PACKAGE).pot: force
%.c: %.y
%.c: %.l
-%-gen.c: %.y
+# Post-process a file produced by a parser generator, see the script
+# for details.
+POST_PROCESS_PARSER_OUTPUT_SH = $(srcdir)/post-process-parser-output.sh
+POST_PROCESS_PARSER_OUTPUT = $(SHELL) $(POST_PROCESS_PARSER_OUTPUT_SH)
+
+%-gen.c: %.y $(POST_PROCESS_PARSER_OUTPUT_SH)
$(ECHO_YACC) $(SHELL) $(YLWRAP) $< y.tab.c $@.tmp -- \
$(YACC) $(YFLAGS) || (rm -f $@.tmp; false)
- @sed -e '/extern.*malloc/d' \
- -e '/extern.*realloc/d' \
- -e '/extern.*free/d' \
- -e '/include.*malloc.h/d' \
- -e 's/\([^x]\)malloc/\1xmalloc/g' \
- -e 's/\([^x]\)realloc/\1xrealloc/g' \
- -e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
- -e 's/\([ \t;,(]\)free$$/\1xfree/g' \
- -e '/^#line.*y.tab.c/d' \
- -e "s/YYSTYPE/$(subst -,_,$*)_YYSTYPE/g" \
- -e "s/yyalloc/$(subst -,_,$*)_yyalloc/g" \
- -e "s/yysymbol_kind_t/$(subst -,_,$*)_yysymbol_kind_t/g" \
- < $@.tmp > $@.new && \
+ @$(POST_PROCESS_PARSER_OUTPUT) bison $* < $@.tmp > $@.new && \
rm -f $@.tmp && \
mv $@.new $@
-%-gen.c: %.l
+%-gen.c: %.l $(POST_PROCESS_PARSER_OUTPUT_SH)
$(ECHO_LEX) $(FLEX) -t $< > $@.tmp || (rm -f $@.tmp; false)
- @sed -e '/extern.*malloc/d' \
- -e '/extern.*realloc/d' \
- -e '/extern.*free/d' \
- -e '/include.*malloc.h/d' \
- -e 's/\([^x]\)malloc/\1xmalloc/g' \
- -e 's/\([^x]\)realloc/\1xrealloc/g' \
- -e 's/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g' \
- -e 's/\([ \t;,(]\)free$$/\1xfree/g' \
- -e 's/yy_flex_xrealloc/yyxrealloc/g' \
- < $@.tmp > $@.new && \
- rm -f $@.tmp && \
- mv $@.new $@
+ @$(POST_PROCESS_PARSER_OUTPUT) flex $* < $@.tmp > $@.new && \
+ rm -f $@.tmp && \
+ mv $@.new $@
# XML rules
diff --git a/gdb/post-process-parser-output.sh b/gdb/post-process-parser-output.sh
new file mode 100755
index 000000000000..a0cf6b464c4b
--- /dev/null
+++ b/gdb/post-process-parser-output.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# Copyright (C) 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/>.
+
+# Post-process a file produced by flex or bison/byacc, reading it from
+# standard input and writing the result to standard output.
+#
+# Usage:
+#
+# ./post-process-parser-output.sh GENERATOR NAME < INPUT > OUTPUT
+#
+# Where GENERATOR is "bison" (regardless of whether we're using the actual
+# bison or byacc) or "flex", and NAME is the name of the parser (e.g.
+# "cp-name-parser"), used to make the global symbols names bison produces
+# unique.
+
+set -e
+
+generator="$1"
+
+# Convert e.g. "cp-name-parser" to "cp_name_parser".
+name=$(echo "$2" | sed -e 's/-/_/g')
+
+common='
+/extern.*malloc/d
+/extern.*realloc/d
+/extern.*free/d
+/include.*malloc.h/d
+s/\([^x]\)malloc/\1xmalloc/g
+s/\([^x]\)realloc/\1xrealloc/g
+s/\([ \t;,(]\)free\([ \t]*[&(),]\)/\1xfree\2/g
+s/\([ \t;,(]\)free$/\1xfree/g
+'
+
+case "$generator" in
+ bison)
+ sed -e "$common" \
+ -e '/^#line.*y.tab.c/d' \
+ -e "s/YYSTYPE/${name}_YYSTYPE/g" \
+ -e "s/yyalloc/${name}_yyalloc/g" \
+ -e "s/yysymbol_kind_t/${name}_yysymbol_kind_t/g"
+ ;;
+ flex)
+ sed -e "$common" \
+ -e 's/yy_flex_xrealloc/yyxrealloc/g'
+ ;;
+ *)
+ echo "$0: unknown parser generator \"$generator\"" >&2
+ exit 1
+ ;;
+esac
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 06/19] gdb: let the parser and lexer generators prefix their symbols
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (4 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 05/19] gdb: move parser output post-processing to a script simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 07/19] gdb: separate cp-name-parser's symbol prefix with an underscore simon.marchi
` (12 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
In order for parsers to be able to co-exist and avoid symbol clashes, they
all include yy-remap.h to rename their symbols to something unique.
Bison and byacc both support doing this natively though, with the `-p`
flag. flex also has a similar option `-P`. I think we can get rid of
`yy-remap.h` in favor of using these options.
It's not totally clear to me if "byacc" is a single program or if there are
many variants of "byacc" maintained by various projects, but here are those
I found, which support `-p`:
- https://invisible-island.net/byacc/manpage/yacc.html
- https://man.openbsd.org/yacc
- https://man.freebsd.org/cgi/man.cgi?query=byacc&apropos=0&sektion=1&manpath=FreeBSD+15.1-RELEASE&format=html
- https://man.netbsd.org/yacc.1
AIX yacc supports it:
https://www.ibm.com/docs/en/aix/7.3.0?topic=y-yacc-command
Solaris yacc supports it:
https://docs.oracle.com/cd/E88353_01/html/E37839/yacc-1.html
And in any case, it's always possible to just use GNU bison.
For flex, it appears that the various BSDs just use plain flex, so they
support `-P`. However, it looks like the AIX and Solaris lex don't
support it:
- https://www.ibm.com/docs/en/aix/7.3.0?topic=l-lex-command
- https://docs.oracle.com/cd/E88353_01/html/E37839/lex-1.html
It's not clear to me if we already strictly required flex or not. We
don't use AC_PROG_LEX, and gdb/Makefile.in hardcodes:
FLEX = flex
It's possible to override it with
$ make FLEX=lex
but I don't know if that is really a supported use case. I would like
feedback from the AIX and Solaris maintainers to know whether using flex
to build GDB is ok or not (you might already do it). Remember that it's
only necessary when building from git, as generated parser/lexer files
are distributed in tarballs.
So, this patch removes yy-remap.h, and then updates gdb/Makefile.in to pass
a unique `-p` value to all processed .y files. This is done by defining
variables based on the files base names:
...
YY_PREFIX_c-exp = c_yy
YY_PREFIX_cp-name-parser = cpnameyy
YY_PREFIX_d-exp = d_yy
...
and then accessing them like this:
-p $(YY_PREFIX_$*)
And likewise for flex.
yy-remap.h did one more thing besides the renaming: it defaulted YYDEBUG to
1, to enable the parsers' tracing support. Pass `-t` to $(YACC), which
should have the same effect. Note that the `-t` passed to lex means
something else (output to stdout).
I had Claude check the symbols in the generated objects before and after
the patch, for both bison and byacc (the Arch Linux package), and this is
the result:
Verified by comparing the symbols exported by each generated parser's .o
file before and after this patch, with nm --defined-only --extern-only,
using both bison (3.8.2) and byacc (20260126):
- For the seven pure parsers (c, cp-name, d, f, go, m2, p), the
exported symbols are identical before and after, with both
generators. Replacing the yy-remap.h #defines with the generator's
-p flag changes nothing in the emitted interface.
- ada-exp is the only one that differs, and only in the lexer symbols:
25 flex symbols that used to be exported as bare globals (yyin,
yyout, yyleng, yylineno, yy_flex_debug, yyalloc, yyfree, yyxrealloc,
yy_scan_*, yyget_*/yyset_*, yy_flush_buffer, yy{push,pop}_buffer_state,
yylex_destroy) are now prefixed with ada_. The old hand-written block
only remapped seven flex symbols; -P covers them all. Same result
with both generators.
No exported symbol with a bare "yy" prefix remains in any parser. byacc
additionally exports yyval and yyerrflag, but prefixed (e.g. c_yyval),
so they still cannot collide.
Change-Id: Ib46ff232722c686072ece4fdbf4f70737e10d8c1
---
gdb/Makefile.in | 18 +++++++--
gdb/ada-exp.y | 19 ---------
gdb/c-exp.y | 5 ---
gdb/cp-name-parser.y | 3 --
gdb/d-exp.y | 5 ---
gdb/f-exp.y | 5 ---
gdb/go-exp.y | 5 ---
gdb/m2-exp.y | 5 ---
gdb/p-exp.y | 5 ---
gdb/yy-remap.h | 96 --------------------------------------------
10 files changed, 15 insertions(+), 151 deletions(-)
delete mode 100644 gdb/yy-remap.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 80b2f8573294..fdfefbff1f6c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1741,7 +1741,6 @@ HFILES_NO_SRCDIR = \
xml-syscall.h \
xml-tdesc.h \
xtensa-tdep.h \
- yy-remap.h \
z80-tdep.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -2698,14 +2697,27 @@ po/$(PACKAGE).pot: force
POST_PROCESS_PARSER_OUTPUT_SH = $(srcdir)/post-process-parser-output.sh
POST_PROCESS_PARSER_OUTPUT = $(SHELL) $(POST_PROCESS_PARSER_OUTPUT_SH)
+# The prefix to give to the symbols each parser or lexer generator
+# produces, so that they can coexist in the same program. There is no
+# rule tying it to the file name, so spell it out for each one.
+YY_PREFIX_ada-exp = ada_yy
+YY_PREFIX_ada-lex = ada_yy
+YY_PREFIX_c-exp = c_yy
+YY_PREFIX_cp-name-parser = cpnameyy
+YY_PREFIX_d-exp = d_yy
+YY_PREFIX_f-exp = f_yy
+YY_PREFIX_go-exp = go_yy
+YY_PREFIX_m2-exp = m2_yy
+YY_PREFIX_p-exp = pascal_yy
+
%-gen.c: %.y $(POST_PROCESS_PARSER_OUTPUT_SH)
$(ECHO_YACC) $(SHELL) $(YLWRAP) $< y.tab.c $@.tmp -- \
- $(YACC) $(YFLAGS) || (rm -f $@.tmp; false)
+ $(YACC) $(YFLAGS) -p $(YY_PREFIX_$*) -t || (rm -f $@.tmp; false)
@$(POST_PROCESS_PARSER_OUTPUT) bison $* < $@.tmp > $@.new && \
rm -f $@.tmp && \
mv $@.new $@
%-gen.c: %.l $(POST_PROCESS_PARSER_OUTPUT_SH)
- $(ECHO_LEX) $(FLEX) -t $< > $@.tmp || (rm -f $@.tmp; false)
+ $(ECHO_LEX) $(FLEX) -t -P $(YY_PREFIX_$*) $< > $@.tmp || (rm -f $@.tmp; false)
@$(POST_PROCESS_PARSER_OUTPUT) flex $* < $@.tmp > $@.new && \
rm -f $@.tmp && \
mv $@.new $@
diff --git a/gdb/ada-exp.y b/gdb/ada-exp.y
index bffe94a8de9b..433293d22ad9 100644
--- a/gdb/ada-exp.y
+++ b/gdb/ada-exp.y
@@ -46,11 +46,6 @@
#include "ada-exp.h"
#include "cli/cli-style.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX ada_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
@@ -1237,20 +1232,6 @@ primary : '*' primary %prec '.'
/* yylex defined in ada-lex-gen.c: Reads one token, getting characters */
/* through lexptr. */
-/* Remap normal flex interface names (yylex) as well as gratuitously */
-/* global symbol names, so we can have multiple flex-generated parsers */
-/* in gdb. */
-
-/* (See note above on previous definitions for YACC.) */
-
-#define yy_create_buffer ada_yy_create_buffer
-#define yy_delete_buffer ada_yy_delete_buffer
-#define yy_init_buffer ada_yy_init_buffer
-#define yy_load_buffer_state ada_yy_load_buffer_state
-#define yy_switch_to_buffer ada_yy_switch_to_buffer
-#define yyrestart ada_yyrestart
-#define yytext ada_yytext
-
/* The following kludge was found necessary to prevent conflicts between */
/* defs.h and non-standard stdlib.h files. */
#define qsort __qsort__dummy
diff --git a/gdb/c-exp.y b/gdb/c-exp.y
index d58bce833551..9a1ecb3e6d3a 100644
--- a/gdb/c-exp.y
+++ b/gdb/c-exp.y
@@ -54,11 +54,6 @@
#include "macroexp.h"
#include "cli/cli-style.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX c_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
index 1fd4a17db06a..44ef1a41eb39 100644
--- a/gdb/cp-name-parser.y
+++ b/gdb/cp-name-parser.y
@@ -45,9 +45,6 @@
#include "parser-defs.h"
#include "gdbsupport/selftest.h"
-#define GDB_YY_REMAP_PREFIX cpname
-#include "yy-remap.h"
-
%}
%union
diff --git a/gdb/d-exp.y b/gdb/d-exp.y
index d3263ba4fd72..3b3a212cd741 100644
--- a/gdb/d-exp.y
+++ b/gdb/d-exp.y
@@ -50,11 +50,6 @@
#include "expop.h"
#include "cli/cli-style.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX d_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
diff --git a/gdb/f-exp.y b/gdb/f-exp.y
index 4cdfcc973923..ad1a9253d7f9 100644
--- a/gdb/f-exp.y
+++ b/gdb/f-exp.y
@@ -52,11 +52,6 @@
#include "type-stack.h"
#include "f-exp.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX f_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
diff --git a/gdb/go-exp.y b/gdb/go-exp.y
index 199da6dc5c37..1384c3ebeeec 100644
--- a/gdb/go-exp.y
+++ b/gdb/go-exp.y
@@ -61,11 +61,6 @@
#include "block.h"
#include "expop.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX go_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
diff --git a/gdb/m2-exp.y b/gdb/m2-exp.y
index 0889054f1f37..fae0f86ffdae 100644
--- a/gdb/m2-exp.y
+++ b/gdb/m2-exp.y
@@ -45,11 +45,6 @@
#include "block.h"
#include "m2-exp.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX m2_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
diff --git a/gdb/p-exp.y b/gdb/p-exp.y
index 70c74beae438..dd4ae2cb209b 100644
--- a/gdb/p-exp.y
+++ b/gdb/p-exp.y
@@ -51,11 +51,6 @@
#include "block.h"
#include "expop.h"
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc). */
-#define GDB_YY_REMAP_PREFIX pascal_
-#include "yy-remap.h"
-
/* The state of the parser, used internally when we are parsing the
expression. */
diff --git a/gdb/yy-remap.h b/gdb/yy-remap.h
deleted file mode 100644
index 0cadf6082fd5..000000000000
--- a/gdb/yy-remap.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/* 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_YY_REMAP_H
-#define GDB_YY_REMAP_H
-
-/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
- etc), as well as gratuitously global symbol names, so we can have
- multiple yacc generated parsers in gdb. Note that these are only
- the variables produced by yacc. If other parser generators (bison,
- byacc, etc) produce additional global names that conflict at link
- time, then those parser generators need to be fixed instead of
- adding those names to this list. */
-
-/* NOTE: This is clumsy since BISON and FLEX provide --prefix options.
- We are maintaining it to accommodate systems without BISON. */
-
-/* Define GDB_YY_REMAP_PREFIX to the desired remapping prefix before
- including this file. */
-#ifndef GDB_YY_REMAP_PREFIX
-# error "GDB_YY_REMAP_PREFIX not defined"
-#endif
-
-#define GDB_YY_REMAP_2(PREFIX, YYSYM) PREFIX ## YYSYM
-#define GDB_YY_REMAP_1(PREFIX, YYSYM) GDB_YY_REMAP_2 (PREFIX, YYSYM)
-#define GDB_YY_REMAP(YYSYM) GDB_YY_REMAP_1 (GDB_YY_REMAP_PREFIX, YYSYM)
-
-#define yymaxdepth GDB_YY_REMAP (yymaxdepth)
-#define yyparse GDB_YY_REMAP (yyparse)
-#define yylex GDB_YY_REMAP (yylex)
-#define yyerror GDB_YY_REMAP (yyerror)
-#define yylval GDB_YY_REMAP (yylval)
-#define yychar GDB_YY_REMAP (yychar)
-#define yydebug GDB_YY_REMAP (yydebug)
-#define yypact GDB_YY_REMAP (yypact)
-#define yyr1 GDB_YY_REMAP (yyr1)
-#define yyr2 GDB_YY_REMAP (yyr2)
-#define yydef GDB_YY_REMAP (yydef)
-#define yychk GDB_YY_REMAP (yychk)
-#define yypgo GDB_YY_REMAP (yypgo)
-#define yyact GDB_YY_REMAP (yyact)
-#define yyexca GDB_YY_REMAP (yyexca)
-#define yyerrflag GDB_YY_REMAP (yyerrflag)
-#define yynerrs GDB_YY_REMAP (yynerrs)
-#define yyps GDB_YY_REMAP (yyps)
-#define yypv GDB_YY_REMAP (yypv)
-#define yys GDB_YY_REMAP (yys)
-#define yy_yys GDB_YY_REMAP (yy_yys)
-#define yystate GDB_YY_REMAP (yystate)
-#define yytmp GDB_YY_REMAP (yytmp)
-#define yyv GDB_YY_REMAP (yyv)
-#define yy_yyv GDB_YY_REMAP (yy_yyv)
-#define yyval GDB_YY_REMAP (yyval)
-#define yylloc GDB_YY_REMAP (yylloc)
-#define yyreds GDB_YY_REMAP (yyreds) /* With YYDEBUG defined */
-#define yytoks GDB_YY_REMAP (yytoks) /* With YYDEBUG defined */
-#define yyname GDB_YY_REMAP (yyname) /* With YYDEBUG defined */
-#define yyrule GDB_YY_REMAP (yyrule) /* With YYDEBUG defined */
-#define yylhs GDB_YY_REMAP (yylhs)
-#define yylen GDB_YY_REMAP (yylen)
-#define yydefred GDB_YY_REMAP (yydefred)
-#define yydgoto GDB_YY_REMAP (yydgoto)
-#define yysindex GDB_YY_REMAP (yysindex)
-#define yyrindex GDB_YY_REMAP (yyrindex)
-#define yygindex GDB_YY_REMAP (yygindex)
-#define yytable GDB_YY_REMAP (yytable)
-#define yycheck GDB_YY_REMAP (yycheck)
-#define yyss GDB_YY_REMAP (yyss)
-#define yysslim GDB_YY_REMAP (yysslim)
-#define yyssp GDB_YY_REMAP (yyssp)
-#define yystacksize GDB_YY_REMAP (yystacksize)
-#define yyvs GDB_YY_REMAP (yyvs)
-#define yyvsp GDB_YY_REMAP (yyvsp)
-#define YYSTACKDATA GDB_YY_REMAP (YYSTACKDATA)
-
-/* The following are common to all parsers. */
-
-#ifndef YYDEBUG
-# define YYDEBUG 1 /* Default to yydebug support */
-#endif
-
-#endif /* GDB_YY_REMAP_H */
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 07/19] gdb: separate cp-name-parser's symbol prefix with an underscore
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (5 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 06/19] gdb: let the parser and lexer generators prefix their symbols simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 08/19] gdb: make $(YACC) and $(FLEX) generate headers simon.marchi
` (11 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
cp-name-parser is the only parser whose prefix does not have an
underscore before the `yy`, so its generated symbols come out as
cpnameyyparse, while the others get c_yyparse, ada_yyparse, and so on.
Line them up, giving cpname_yyparse. A following patch moves
cp-name-parser.y's support code to a separate file, where these names
have to be written out by hand, so it is worth having them readable.
Change-Id: I94925c0d6275d8e3dc70314987ed48dcc3903afa
---
gdb/Makefile.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index fdfefbff1f6c..daf5cf2baee1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2703,7 +2703,7 @@ POST_PROCESS_PARSER_OUTPUT = $(SHELL) $(POST_PROCESS_PARSER_OUTPUT_SH)
YY_PREFIX_ada-exp = ada_yy
YY_PREFIX_ada-lex = ada_yy
YY_PREFIX_c-exp = c_yy
-YY_PREFIX_cp-name-parser = cpnameyy
+YY_PREFIX_cp-name-parser = cpname_yy
YY_PREFIX_d-exp = d_yy
YY_PREFIX_f-exp = f_yy
YY_PREFIX_go-exp = go_yy
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 08/19] gdb: make $(YACC) and $(FLEX) generate headers
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (6 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 07/19] gdb: separate cp-name-parser's symbol prefix with an underscore simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 09/19] gdb: add check for stale build generated files simon.marchi
` (10 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
I'd like to move the hand-written support code of parsers out of the .y
files, because I find it very impractical to read and edit. C++ tooling
also typically doesn't work for the C++ code inside .y files.
To help with this, pass -d to $(YACC), so that it also produces header
files exposing the generated parsers, allowing to call them from other
files. I checked that the following yacc implementations support it:
- bison
- AIX (https://www.ibm.com/docs/en/aix/7.2.0?topic=y-yacc-command)
- Solaris (https://docs.oracle.com/cd/E88353_01/html/E37839/yacc-1.html)
- NetBSD (https://man.netbsd.org/yacc.1)
- FreeBSD (https://man.freebsd.org/cgi/man.cgi?query=yacc&apropos=0&sektion=1&manpath=FreeBSD+15.1-RELEASE&arch=default&format=html)
- OpenBSD (https://man.openbsd.org/yacc)
Also pass `--header-file` to $(FLEX) to have it generate a header
file. This is a flex-specific option, but a previous patch in this
series (to use -P) already effectively required the use of flex.
The post-processing must be applied to the header files exactly as it is
applied to the source files (it contains declarations of things that get
renamed by that post-processing), so call the post processing script for
the headers too.
Add the generated headers to generated_files, so they are created before
anything that could include them is compiled.
Change-Id: I7153f85c69e989b5a1e4318288b85ba4b456a319
---
gdb/.gitignore | 9 +++++++++
gdb/Makefile.in | 38 +++++++++++++++++++++++++-------------
2 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/gdb/.gitignore b/gdb/.gitignore
index c4d0f0595448..6b5600dd695e 100644
--- a/gdb/.gitignore
+++ b/gdb/.gitignore
@@ -6,15 +6,24 @@
/xml-builtin.c
/ada-exp-gen.c
+/ada-exp-gen.h
/ada-lex-gen.c
+/ada-lex-gen.h
/c-exp-gen.c
+/c-exp-gen.h
/cp-name-parser-gen.c
+/cp-name-parser-gen.h
/d-exp-gen.c
+/d-exp-gen.h
/f-exp-gen.c
+/f-exp-gen.h
/gdb
/gcore
/go-exp-gen.c
+/go-exp-gen.h
/init.c
/jit-reader.h
/m2-exp-gen.c
+/m2-exp-gen.h
/p-exp-gen.c
+/p-exp-gen.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index daf5cf2baee1..e6c1587f4767 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2071,9 +2071,12 @@ YYFILES = \
# a .o itself.
YYOBJ = $(filter-out ada-lex-gen.o,$(patsubst %.c,%.o,$(YYFILES)))
+# The headers bison/byacc and flex generate alongside the files above.
+YYHFILES = $(patsubst %-gen.c,%-gen.h,$(YYFILES))
+
# Things which need to be built when making a distribution.
-DISTSTUFF = $(YYFILES)
+DISTSTUFF = $(YYFILES) $(YYHFILES)
# All generated files which can be included by another file.
@@ -2081,6 +2084,7 @@ generated_files = \
ada-lex-gen.c \
config.h \
jit-reader.h \
+ $(YYHFILES) \
$(NAT_GENERATED_FILES) \
$(NM_H)
@@ -2437,7 +2441,7 @@ local-maintainer-clean:
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
rm -f TAGS
- rm -f $(YYFILES)
+ rm -f $(YYFILES) $(YYHFILES)
rm -f nm.h config.status
do-maintainer-clean:
@@ -2710,17 +2714,25 @@ YY_PREFIX_go-exp = go_yy
YY_PREFIX_m2-exp = m2_yy
YY_PREFIX_p-exp = pascal_yy
-%-gen.c: %.y $(POST_PROCESS_PARSER_OUTPUT_SH)
- $(ECHO_YACC) $(SHELL) $(YLWRAP) $< y.tab.c $@.tmp -- \
- $(YACC) $(YFLAGS) -p $(YY_PREFIX_$*) -t || (rm -f $@.tmp; false)
- @$(POST_PROCESS_PARSER_OUTPUT) bison $* < $@.tmp > $@.new && \
- rm -f $@.tmp && \
- mv $@.new $@
-%-gen.c: %.l $(POST_PROCESS_PARSER_OUTPUT_SH)
- $(ECHO_LEX) $(FLEX) -t -P $(YY_PREFIX_$*) $< > $@.tmp || (rm -f $@.tmp; false)
- @$(POST_PROCESS_PARSER_OUTPUT) flex $* < $@.tmp > $@.new && \
- rm -f $@.tmp && \
- mv $@.new $@
+%-gen.c %-gen.h: %.y $(POST_PROCESS_PARSER_OUTPUT_SH)
+ $(ECHO_YACC) $(SHELL) $(YLWRAP) $< \
+ y.tab.c $*-gen.c.tmp \
+ y.tab.h $*-gen.h.tmp -- \
+ $(YACC) $(YFLAGS) -p $(YY_PREFIX_$*) -t -d \
+ || (rm -f $*-gen.c.tmp $*-gen.h.tmp; false)
+ @$(POST_PROCESS_PARSER_OUTPUT) bison $* < $*-gen.c.tmp > $*-gen.c.new \
+ && rm -f $*-gen.c.tmp && mv $*-gen.c.new $*-gen.c
+ @$(POST_PROCESS_PARSER_OUTPUT) bison $* < $*-gen.h.tmp > $*-gen.h.new \
+ && rm -f $*-gen.h.tmp && mv $*-gen.h.new $*-gen.h
+
+%-gen.c %-gen.h: %.l $(POST_PROCESS_PARSER_OUTPUT_SH)
+ $(ECHO_LEX) $(FLEX) -t -P $(YY_PREFIX_$*) \
+ --header-file=$*-gen.h.tmp $< > $*-gen.c.tmp \
+ || (rm -f $*-gen.c.tmp $*-gen.h.tmp; false)
+ @$(POST_PROCESS_PARSER_OUTPUT) flex $* < $*-gen.c.tmp > $*-gen.c.new \
+ && rm -f $*-gen.c.tmp && mv $*-gen.c.new $*-gen.c
+ @$(POST_PROCESS_PARSER_OUTPUT) flex $* < $*-gen.h.tmp > $*-gen.h.new \
+ && rm -f $*-gen.h.tmp && mv $*-gen.h.new $*-gen.h
# XML rules
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 09/19] gdb: add check for stale build generated files
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (7 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 08/19] gdb: make $(YACC) and $(FLEX) generate headers simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 10/19] gdb: move cp-name-parser.y's support code to cp-name-parser.c simon.marchi
` (9 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
Before this series, building gdb generates a file called cp-name-parser.c,
from cp-name-parser.y. After this series, cp-name-parser.c is a source
file written by hand checked in git. When doing out of tree builds, a
leftover generated cp-name-parser.c in the build directory would interfere
with the build. To make "cp-name-parser.o", it will prefer the stale
cp-name-parser.c from the build directory rather than the good
cp-name-parser.c from the source directory. Debugging the build failure
that arises from this can be difficult and time consuming, especially if
you have never hit the problem before.
I propose to add a check in the Makefile to point out if there exists a
file in the build directory that is known to be stale and that could
interfere with the build.
The alternative is obviously to avoid naming the new file
"cp-name-parser.c", but I don't want to avoid using a good and logical
file name just because of that.
The check looks like this when it catches something:
Error: stale generated files found in the build directory:
cp-name-parser.c
They are left over from an older build of GDB and shadow the
source files with the same name. Run "make clean" (or delete
them manually) then build again.
I made "make clean" remove those files, because why not, it seems
convenient.
The check and removal in "make clean" only happen when the source directory
is different from the build directory. For in-tree builds, a stale
generated cp-name-parser.c file would presumably prevent checking out a git
commit that has a tracked cp-name-parser.c file.
This patch only adds the machinery, the following patch adds
cp-name-parser.c to the STALE_GENERATED_FILES list. I have done it in this
order to avoid having some commits that could present the conflict but
don't have the check.
Change-Id: I109ba215b3e30debe82b711efb8826668fea0969
---
gdb/Makefile.in | 37 ++++++++++++++++++++++++++++++++++++-
1 file changed, 36 insertions(+), 1 deletion(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index e6c1587f4767..6cb5482bbc66 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2074,6 +2074,15 @@ YYOBJ = $(filter-out ada-lex-gen.o,$(patsubst %.c,%.o,$(YYFILES)))
# The headers bison/byacc and flex generate alongside the files above.
YYHFILES = $(patsubst %-gen.c,%-gen.h,$(YYFILES))
+# Files that used to be generated in the build directory (for builds from git
+# at least), but are now committed source files. A leftover copy in an
+# existing build directory would shadow the real source file with the same name
+# and cause a hard to debug build failure.
+#
+# Files in there can be removed after a while, once build directories with them
+# are unlikely to be around.
+STALE_GENERATED_FILES =
+
# Things which need to be built when making a distribution.
DISTSTUFF = $(YYFILES) $(YYHFILES)
@@ -2091,9 +2100,32 @@ generated_files = \
# Flags needed to compile Python code
PYTHON_CFLAGS = @PYTHON_CFLAGS@
-all: gdb$(EXEEXT) $(CONFIG_ALL) gcore gstack gdb-add-index
+all: check-stale-generated-files gdb$(EXEEXT) $(CONFIG_ALL) gcore \
+ gstack gdb-add-index
@$(MAKE) $(FLAGS_TO_PASS) DO=all "DODIRS=$(SUBDIRS)" subdir_do
+# Error out if the build directory contains known stale generated files, which
+# would shadow the source files with the same name and cause a hard to debug
+# build failure. See STALE_GENERATED_FILES above.
+.PHONY: check-stale-generated-files
+check-stale-generated-files:
+ @if test "$(srcdir)" != "."; then \
+ stale=""; \
+ for f in $(STALE_GENERATED_FILES); do \
+ if test -f "$$f"; then \
+ stale="$$stale $$f"; \
+ fi; \
+ done; \
+ if test -n "$$stale"; then \
+ echo "Error: stale generated files found in the build directory:" >&2; \
+ echo " $$stale" >&2; \
+ echo "They are left over from an older build of GDB and shadow the" >&2; \
+ echo "source files with the same name. Run \"make clean\" (or delete" >&2; \
+ echo "them manually) then build again." >&2; \
+ exit 1; \
+ fi; \
+ fi
+
# Rule for compiling .c files.
%.o: %.c
$(COMPILE) $<
@@ -2417,6 +2449,9 @@ clean mostlyclean: $(CONFIG_CLEAN)
for d in $(ALL_DEPDIRS); do \
if test -d "$$d"; then rmdir "$$d" || exit 1; fi; \
done
+ if test "$(srcdir)" != "."; then \
+ rm -f $(STALE_GENERATED_FILES); \
+ fi
# This used to depend on c-exp.c m2-exp.c TAGS
# I believe this is wrong; the makefile standards for distclean just
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 10/19] gdb: move cp-name-parser.y's support code to cp-name-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (8 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 09/19] gdb: add check for stale build generated files simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 11/19] gdb: rename LANG-exp.y to LANG-exp-parser.y simon.marchi
` (8 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
The goal of this patch (and the following ones for the other parsers) is
to move the C++ support code (everything but the parser rules
themselves) from cp-name-parser.y to cp-name-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.
Move all the epilogue out to cp-name-parser.c, leaving cp-name-parser.y
with just the prologue and the grammar rules. The code is mostly moved
as-is.
- struct cpname_state, along with the flag constants for d_qualify and
d_int_type and the d_left/d_right macros, moves to the
cp-name-parser.h header, so that both cp-name-parser-gen.c and
cp-name-parser.c can see it.
- The old yylex and yyerror functions are renamed explicitly to
cpname_yylex and cpname_yyerror. They used to be effectively named
that, thanks to the parser generator's -p flag, but now that they
live in cp-name-parser.c, they just have that name. They are
declared in cp-name-parser.h, so that cp-name-parser-gen.c can see
them. cp-name-parser-gen.c calls yylex and yyerror, which -p turns
into cpname_yylex and cpname_yyerror.
- cp_demangled_name_to_comp is the function that calls into the
generated parser's entry point, yyparse, or rather cpname_yyparse,
thanks to the -p flag. Bison declares yyparse in the generated
header, but byacc does not, so cp-name-parser.c has to declare it by
hand unfortunately.
- cp-support.c had its own copies of the d_left and d_right macros,
drop them and use the ones from cp-name-parser.h.
I wrapped most of cp-name-parser.{c,h} in namespace `cp_name_parser`.
There is no real risk of symbol collisions here, but this is for
consistency with the subsequent patches, so that all parsers use the
same patterns.
Add cp-name-parser.c to STALE_GENERATED_FILES, such that a stale
cp-name-parser.c file in the build directory will make the build stop with
a clear error.
Change-Id: I2a47664d8e68f3e9ab41cf42378d2c61dcaa27f1
---
gdb/Makefile.in | 4 +-
gdb/cp-name-parser.c | 1050 +++++++++++++++++++++++++++++++++++++++
gdb/cp-name-parser.h | 130 +++++
gdb/cp-name-parser.y | 1115 +-----------------------------------------
gdb/cp-support.c | 4 +-
5 files changed, 1187 insertions(+), 1116 deletions(-)
create mode 100644 gdb/cp-name-parser.c
create mode 100644 gdb/cp-name-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 6cb5482bbc66..a7d79e08655e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1077,6 +1077,7 @@ COMMON_SFILES = \
corefile.c \
corelow.c \
cp-abi.c \
+ cp-name-parser.c \
cp-namespace.c \
cp-support.c \
cp-valprint.c \
@@ -1374,6 +1375,7 @@ HFILES_NO_SRCDIR = \
config/nm-linux.h \
config/sparc/nm-sol2.h \
cp-abi.h \
+ cp-name-parser.h \
cp-support.h \
cris-tdep.h \
csky-tdep.h \
@@ -2081,7 +2083,7 @@ YYHFILES = $(patsubst %-gen.c,%-gen.h,$(YYFILES))
#
# Files in there can be removed after a while, once build directories with them
# are unlikely to be around.
-STALE_GENERATED_FILES =
+STALE_GENERATED_FILES = cp-name-parser.c
# Things which need to be built when making a distribution.
diff --git a/gdb/cp-name-parser.c b/gdb/cp-name-parser.c
new file mode 100644
index 000000000000..d04ed3594878
--- /dev/null
+++ b/gdb/cp-name-parser.c
@@ -0,0 +1,1050 @@
+/* Helper functions for the C++ name parser, for GDB.
+
+ Copyright (C) 2003-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 "cp-name-parser.h"
+#include "cp-name-parser-gen.h"
+#include "c-support.h"
+#include "cp-support.h"
+#include "gdbsupport/gdb_obstack.h"
+#include "gdbsupport/selftest.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ cp-name-parser-gen.c. Bison produces a declaration for yyparse in
+ cp-name-parser-gen.h, but byacc does not, hence this declaration. */
+
+int cpname_yyparse (cp_name_parser::cpname_state *state);
+
+namespace cp_name_parser {
+
+struct demangle_component *
+cpname_state::d_grab ()
+{
+ return obstack_new<demangle_component> (&demangle_info->obstack);
+}
+
+/* Helper functions. These wrap the demangler tree interface, handle
+ allocation from our global store, and return the allocated component. */
+
+struct demangle_component *
+cpname_state::fill_comp (enum demangle_component_type d_type,
+ struct demangle_component *lhs,
+ struct demangle_component *rhs)
+{
+ struct demangle_component *ret = d_grab ();
+ int i;
+
+ i = cplus_demangle_fill_component (ret, d_type, lhs, rhs);
+ gdb_assert (i);
+
+ return ret;
+}
+
+struct demangle_component *
+cpname_state::make_operator (const char *name, int args)
+{
+ struct demangle_component *ret = d_grab ();
+ int i;
+
+ i = cplus_demangle_fill_operator (ret, name, args);
+ gdb_assert (i);
+
+ return ret;
+}
+
+struct demangle_component *
+cpname_state::make_dtor (enum gnu_v3_dtor_kinds kind,
+ struct demangle_component *name)
+{
+ struct demangle_component *ret = d_grab ();
+ int i;
+
+ i = cplus_demangle_fill_dtor (ret, kind, name);
+ gdb_assert (i);
+
+ return ret;
+}
+
+struct demangle_component *
+cpname_state::make_builtin_type (const char *name)
+{
+ struct demangle_component *ret = d_grab ();
+ int i;
+
+ i = cplus_demangle_fill_builtin_type (ret, name);
+ gdb_assert (i);
+
+ return ret;
+}
+
+struct demangle_component *
+cpname_state::make_name (const char *name, int len)
+{
+ struct demangle_component *ret = d_grab ();
+ int i;
+
+ i = cplus_demangle_fill_name (ret, name, len);
+ gdb_assert (i);
+
+ return ret;
+}
+
+/* Apply QUALIFIERS to LHS and return a qualified component. IS_METHOD
+ is set if LHS is a method, in which case the qualifiers are logically
+ applied to "this". We apply qualifiers in a consistent order; LHS
+ may already be qualified; duplicate qualifiers are not created. */
+
+struct demangle_component *
+cpname_state::d_qualify (struct demangle_component *lhs, int qualifiers,
+ int is_method)
+{
+ struct demangle_component **inner_p;
+ enum demangle_component_type type;
+
+ /* For now the order is CONST (innermost), VOLATILE, RESTRICT. */
+
+#define HANDLE_QUAL(TYPE, MTYPE, QUAL) \
+ if ((qualifiers & QUAL) && (type != TYPE) && (type != MTYPE)) \
+ { \
+ *inner_p = fill_comp (is_method ? MTYPE : TYPE, \
+ *inner_p, NULL); \
+ inner_p = &d_left (*inner_p); \
+ type = (*inner_p)->type; \
+ } \
+ else if (type == TYPE || type == MTYPE) \
+ { \
+ inner_p = &d_left (*inner_p); \
+ type = (*inner_p)->type; \
+ }
+
+ inner_p = &lhs;
+
+ type = (*inner_p)->type;
+
+ HANDLE_QUAL (DEMANGLE_COMPONENT_RESTRICT, DEMANGLE_COMPONENT_RESTRICT_THIS, QUAL_RESTRICT);
+ HANDLE_QUAL (DEMANGLE_COMPONENT_VOLATILE, DEMANGLE_COMPONENT_VOLATILE_THIS, QUAL_VOLATILE);
+ HANDLE_QUAL (DEMANGLE_COMPONENT_CONST, DEMANGLE_COMPONENT_CONST_THIS, QUAL_CONST);
+#undef HANDLE_QUAL
+
+ return lhs;
+}
+
+/* Return a builtin type corresponding to FLAGS. */
+
+struct demangle_component *
+cpname_state::d_int_type (int flags)
+{
+ const char *name;
+
+ switch (flags)
+ {
+ case INT_SIGNED | INT_CHAR:
+ name = "signed char";
+ break;
+ case INT_CHAR:
+ name = "char";
+ break;
+ case INT_UNSIGNED | INT_CHAR:
+ name = "unsigned char";
+ break;
+ case 0:
+ case INT_SIGNED:
+ name = "int";
+ break;
+ case INT_UNSIGNED:
+ name = "unsigned int";
+ break;
+ case INT_LONG:
+ case INT_SIGNED | INT_LONG:
+ name = "long";
+ break;
+ case INT_UNSIGNED | INT_LONG:
+ name = "unsigned long";
+ break;
+ case INT_SHORT:
+ case INT_SIGNED | INT_SHORT:
+ name = "short";
+ break;
+ case INT_UNSIGNED | INT_SHORT:
+ name = "unsigned short";
+ break;
+ case INT_LLONG | INT_LONG:
+ case INT_SIGNED | INT_LLONG | INT_LONG:
+ name = "long long";
+ break;
+ case INT_UNSIGNED | INT_LLONG | INT_LONG:
+ name = "unsigned long long";
+ break;
+ default:
+ return NULL;
+ }
+
+ return make_builtin_type (name);
+}
+
+/* Wrapper to create a unary operation. */
+
+struct demangle_component *
+cpname_state::d_unary (const char *name, struct demangle_component *lhs)
+{
+ return fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator (name, 1), lhs);
+}
+
+/* Wrapper to create a binary operation. */
+
+struct demangle_component *
+cpname_state::d_binary (const char *name, struct demangle_component *lhs,
+ struct demangle_component *rhs)
+{
+ return fill_comp (DEMANGLE_COMPONENT_BINARY, make_operator (name, 2),
+ fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs));
+}
+
+/* Take care of parsing a number (anything that starts with a digit).
+ The number starts at P and contains LEN characters. Store the result in
+ YYLVAL. */
+
+int
+cpname_state::parse_number (const char *p, int len, int parsed_float,
+ cp_name_parser_YYSTYPE *lvalp)
+{
+ int unsigned_p = 0;
+
+ /* Number of "L" suffixes encountered. */
+ int long_p = 0;
+
+ struct demangle_component *type, *name;
+ enum demangle_component_type literal_type;
+
+ if (p[0] == '-')
+ {
+ literal_type = DEMANGLE_COMPONENT_LITERAL_NEG;
+ p++;
+ len--;
+ }
+ else
+ literal_type = DEMANGLE_COMPONENT_LITERAL;
+
+ if (parsed_float)
+ {
+ /* It's a float since it contains a point or an exponent. */
+ char c;
+
+ /* The GDB lexer checks the result of scanf at this point. Not doing
+ this leaves our error checking slightly weaker but only for invalid
+ data. */
+
+ /* See if it has `f' or `l' suffix (float or long double). */
+
+ c = c_tolower (p[len - 1]);
+
+ if (c == 'f')
+ {
+ len--;
+ type = make_builtin_type ("float");
+ }
+ else if (c == 'l')
+ {
+ len--;
+ type = make_builtin_type ("long double");
+ }
+ else if (c_isdigit (c) || c == '.')
+ type = make_builtin_type ("double");
+ else
+ return ERROR;
+
+ name = make_name (p, len);
+ lvalp->comp = fill_comp (literal_type, type, name);
+
+ return FLOAT;
+ }
+
+ /* Note that we do not automatically generate unsigned types. This
+ can't be done because we don't have access to the gdbarch
+ here. */
+
+ int base = 10;
+ if (len > 1 && p[0] == '0')
+ {
+ if (p[1] == 'x' || p[1] == 'X')
+ {
+ base = 16;
+ p += 2;
+ len -= 2;
+ }
+ else if (p[1] == 'b' || p[1] == 'B')
+ {
+ base = 2;
+ p += 2;
+ len -= 2;
+ }
+ else if (p[1] == 'd' || p[1] == 'D' || p[1] == 't' || p[1] == 'T')
+ {
+ /* Apparently gdb extensions. */
+ base = 10;
+ p += 2;
+ len -= 2;
+ }
+ else
+ base = 8;
+ }
+
+ long_p = 0;
+ unsigned_p = 0;
+ while (len > 0)
+ {
+ if (p[len - 1] == 'l' || p[len - 1] == 'L')
+ {
+ len--;
+ long_p++;
+ continue;
+ }
+ if (p[len - 1] == 'u' || p[len - 1] == 'U')
+ {
+ len--;
+ unsigned_p++;
+ continue;
+ }
+ break;
+ }
+
+ /* Use gdb_mpz here in case a 128-bit value appears. */
+ gdb_mpz value (0);
+ for (int off = 0; off < len; ++off)
+ {
+ int dig;
+
+ if (c_isdigit (p[off]))
+ dig = p[off] - '0';
+ else
+ dig = c_tolower (p[off]) - 'a' + 10;
+
+ if (dig >= base)
+ return ERROR;
+
+ value *= base;
+ value += dig;
+ }
+
+ std::string printed = value.str ();
+ const char *copy = obstack_strdup (&demangle_info->obstack, printed);
+
+ if (long_p == 0)
+ {
+ if (unsigned_p)
+ type = make_builtin_type ("unsigned int");
+ else
+ type = make_builtin_type ("int");
+ }
+ else if (long_p == 1)
+ {
+ if (unsigned_p)
+ type = make_builtin_type ("unsigned long");
+ else
+ type = make_builtin_type ("long");
+ }
+ else
+ {
+ if (unsigned_p)
+ type = make_builtin_type ("unsigned long long");
+ else
+ type = make_builtin_type ("long long");
+ }
+
+ name = make_name (copy, strlen (copy));
+ lvalp->comp = fill_comp (literal_type, type, name);
+
+ return INT;
+}
+
+} /* namespace cp_name_parser */
+
+/* See cp-support.h. */
+
+gdb::unique_xmalloc_ptr<char>
+cp_comp_to_string (struct demangle_component *result, int estimated_len)
+{
+ size_t err;
+
+ char *res = gdb_cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI,
+ result, estimated_len, &err);
+ return gdb::unique_xmalloc_ptr<char> (res);
+}
+
+/* Merge the two parse trees given by DEST and SRC. The parse tree
+ in SRC is attached to DEST at the node represented by TARGET.
+
+ NOTE 1: Since there is no API to merge obstacks, this function does
+ even attempt to try it. Fortunately, we do not (yet?) need this ability.
+ The code will assert if SRC->obstack is not empty.
+
+ NOTE 2: The string from which SRC was parsed must not be freed, since
+ this function will place pointers to that string into DEST. */
+
+void
+cp_merge_demangle_parse_infos (struct demangle_parse_info *dest,
+ struct demangle_component *target,
+ demangle_parse_info_up src)
+{
+ /* Copy the SRC's parse data into DEST. */
+ *target = *src->tree;
+
+ /* Make sure SRC is owned by DEST. */
+ dest->infos.push_back (std::move (src));
+}
+
+/* Convert a demangled name to a demangle_component tree. On success,
+ a structure containing the root of the new tree is returned. On
+ error, NULL is returned, and an error message will be set in
+ *ERRMSG. */
+
+demangle_parse_info_up
+cp_demangled_name_to_comp (const char *demangled_name,
+ std::string *errmsg)
+{
+ auto result = std::make_unique<demangle_parse_info> ();
+ cp_name_parser::cpname_state state (demangled_name, result.get ());
+
+ /* Note that we can't set yydebug here, as is done in the other
+ parsers. Bison implements yydebug as a global, even with a pure
+ parser, and this parser is run from worker threads. So, changing
+ yydebug causes TSan reports. If you need to debug this parser,
+ debug gdb and set the global from the outer gdb. */
+ if (cpname_yyparse (&state))
+ {
+ if (state.global_errmsg && errmsg)
+ *errmsg = state.global_errmsg;
+ return NULL;
+ }
+
+ result->tree = state.global_result;
+
+ return result;
+}
+
+/* Find the end of a symbol name starting at LEXPTR. */
+
+static const char *
+symbol_end (const char *lexptr)
+{
+ const char *p = lexptr;
+
+ while (*p && (c_ident_is_alnum (*p) || *p == '_' || *p == '$' || *p == '.'))
+ p++;
+
+ return p;
+}
+
+static const char backslashable[] = "abefnrtv";
+static const char represented[] = "\a\b\e\f\n\r\t\v";
+
+/* Translate the backslash the way we would in the host character set. */
+static int
+c_parse_backslash (int host_char, int *target_char)
+{
+ const char *ix;
+ ix = strchr (backslashable, host_char);
+ if (! ix)
+ return 0;
+ else
+ *target_char = represented[ix - backslashable];
+ return 1;
+}
+
+/* Parse a C escape sequence. STRING_PTR points to a variable
+ containing a pointer to the string to parse. That pointer
+ should point to the character after the \. That pointer
+ is updated past the characters we use. The value of the
+ escape sequence is returned.
+
+ A negative value means the sequence \ newline was seen,
+ which is supposed to be equivalent to nothing at all.
+
+ If \ is followed by a null character, we return a negative
+ value and leave the string pointer pointing at the null character.
+
+ If \ is followed by 000, we return 0 and leave the string pointer
+ after the zeros. A value of 0 does not mean end of string. */
+
+static int
+cp_parse_escape (const char **string_ptr)
+{
+ int target_char;
+ int c = *(*string_ptr)++;
+ if (c_parse_backslash (c, &target_char))
+ return target_char;
+ else
+ switch (c)
+ {
+ case '\n':
+ return -2;
+ case 0:
+ (*string_ptr)--;
+ return 0;
+ case '^':
+ {
+ c = *(*string_ptr)++;
+
+ if (c == '?')
+ return 0177;
+ else if (c == '\\')
+ target_char = cp_parse_escape (string_ptr);
+ else
+ target_char = c;
+
+ /* Now target_char is something like `c', and we want to find
+ its control-character equivalent. */
+ target_char = target_char & 037;
+
+ return target_char;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int i = c - '0';
+ int count = 0;
+ while (++count < 3)
+ {
+ c = (**string_ptr);
+ if (c >= '0' && c <= '7')
+ {
+ (*string_ptr)++;
+ i *= 8;
+ i += c - '0';
+ }
+ else
+ {
+ break;
+ }
+ }
+ return i;
+ }
+ default:
+ return c;
+ }
+}
+
+#define HANDLE_SPECIAL(string, comp) \
+ if (startswith (tokstart, string)) \
+ { \
+ state->lexptr = tokstart + sizeof (string) - 1; \
+ lvalp->lval = comp; \
+ return DEMANGLER_SPECIAL; \
+ }
+
+#define HANDLE_TOKEN2(string, token) \
+ if (state->lexptr[1] == string[1]) \
+ { \
+ state->lexptr += 2; \
+ lvalp->opname = string; \
+ return token; \
+ }
+
+#define HANDLE_TOKEN3(string, token) \
+ if (state->lexptr[1] == string[1] && state->lexptr[2] == string[2]) \
+ { \
+ state->lexptr += 3; \
+ lvalp->opname = string; \
+ return token; \
+ }
+
+void
+cpname_yyerror (cp_name_parser::cpname_state *state, const char *msg)
+{
+ if (state->global_errmsg)
+ return;
+
+ state->error_lexptr = state->prev_lexptr;
+ state->global_errmsg = msg ? msg : "parse error";
+}
+
+/* Read one token, getting characters through LEXPTR. */
+
+int
+cpname_yylex (cp_name_parser_YYSTYPE *lvalp,
+ cp_name_parser::cpname_state *state)
+{
+ int c;
+ int namelen;
+ const char *tokstart;
+ char *copy;
+
+ retry:
+ state->prev_lexptr = state->lexptr;
+ tokstart = state->lexptr;
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ state->lexptr++;
+ goto retry;
+
+ case '\'':
+ /* We either have a character constant ('0' or '\177' for example)
+ or we have a quoted symbol reference ('foo(int,int)' in C++
+ for example). */
+ state->lexptr++;
+ c = *state->lexptr++;
+ if (c == '\\')
+ c = cp_parse_escape (&state->lexptr);
+ else if (c == '\'')
+ {
+ cpname_yyerror (state, _("empty character constant"));
+ return ERROR;
+ }
+
+ /* We over-allocate here, but it doesn't really matter . */
+ copy = (char *) obstack_alloc (&state->demangle_info->obstack, 30);
+ xsnprintf (copy, 30, "%d", c);
+
+ c = *state->lexptr++;
+ if (c != '\'')
+ {
+ cpname_yyerror (state, _("invalid character constant"));
+ return ERROR;
+ }
+
+ lvalp->comp
+ = state->fill_comp (DEMANGLE_COMPONENT_LITERAL,
+ state->make_builtin_type ("char"),
+ state->make_name (copy, strlen (copy)));
+
+ return INT;
+
+ case '(':
+ if (startswith (tokstart, "(anonymous namespace)"))
+ {
+ state->lexptr += 21;
+ lvalp->comp = state->make_name ("(anonymous namespace)",
+ sizeof "(anonymous namespace)" - 1);
+ return NAME;
+ }
+ [[fallthrough]];
+
+ case ')':
+ case ',':
+ state->lexptr++;
+ return c;
+
+ case '.':
+ if (state->lexptr[1] == '.' && state->lexptr[2] == '.')
+ {
+ state->lexptr += 3;
+ return ELLIPSIS;
+ }
+
+ /* Might be a floating point number. */
+ if (state->lexptr[1] < '0' || state->lexptr[1] > '9')
+ goto symbol; /* Nope, must be a symbol. */
+
+ goto try_number;
+
+ case '-':
+ HANDLE_TOKEN2 ("-=", ASSIGN_MODIFY);
+ HANDLE_TOKEN2 ("--", DECREMENT);
+ HANDLE_TOKEN2 ("->", ARROW);
+
+ /* For construction vtables. This is kind of hokey. */
+ if (startswith (tokstart, "-in-"))
+ {
+ state->lexptr += 4;
+ return CONSTRUCTION_IN;
+ }
+
+ if (state->lexptr[1] < '0' || state->lexptr[1] > '9')
+ {
+ state->lexptr++;
+ return '-';
+ }
+
+ try_number:
+ [[fallthrough]];
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0, toktype;
+ const char *p = tokstart;
+ int hex = 0;
+
+ if (c == '-')
+ p++;
+
+ 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 && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ /* This test does not include !hex, because a '.' always indicates
+ a decimal floating point number regardless of the radix.
+
+ NOTE drow/2005-03-09: This comment is not accurate in C99;
+ however, it's not clear that all the floating point support
+ in this file is doing any good here. */
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ {
+ /* This is the sign of the exponent, not the end of
+ the number. */
+ }
+ /* C++14 allows a separator. */
+ 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 (! c_isalnum (*p))
+ break;
+ if (no_tick.has_value ())
+ no_tick->push_back (*p);
+ }
+ if (no_tick.has_value ())
+ toktype = state->parse_number (no_tick->c_str (), no_tick->length (),
+ got_dot | got_e, lvalp);
+ else
+ toktype = state->parse_number (tokstart, p - tokstart,
+ got_dot | got_e, lvalp);
+ if (toktype == ERROR)
+ {
+ cpname_yyerror (state, _("invalid number"));
+ return ERROR;
+ }
+ state->lexptr = p;
+ return toktype;
+ }
+
+ case '+':
+ HANDLE_TOKEN2 ("+=", ASSIGN_MODIFY);
+ HANDLE_TOKEN2 ("++", INCREMENT);
+ state->lexptr++;
+ return c;
+ case '*':
+ HANDLE_TOKEN2 ("*=", ASSIGN_MODIFY);
+ state->lexptr++;
+ return c;
+ case '/':
+ HANDLE_TOKEN2 ("/=", ASSIGN_MODIFY);
+ state->lexptr++;
+ return c;
+ case '%':
+ HANDLE_TOKEN2 ("%=", ASSIGN_MODIFY);
+ state->lexptr++;
+ return c;
+ case '|':
+ HANDLE_TOKEN2 ("|=", ASSIGN_MODIFY);
+ HANDLE_TOKEN2 ("||", OROR);
+ state->lexptr++;
+ return c;
+ case '&':
+ HANDLE_TOKEN2 ("&=", ASSIGN_MODIFY);
+ HANDLE_TOKEN2 ("&&", ANDAND);
+ state->lexptr++;
+ return c;
+ case '^':
+ HANDLE_TOKEN2 ("^=", ASSIGN_MODIFY);
+ state->lexptr++;
+ return c;
+ case '!':
+ HANDLE_TOKEN2 ("!=", NOTEQUAL);
+ state->lexptr++;
+ return c;
+ case '<':
+ HANDLE_TOKEN3 ("<<=", ASSIGN_MODIFY);
+ HANDLE_TOKEN3 ("<=>", SPACESHIP);
+ HANDLE_TOKEN2 ("<=", LEQ);
+ HANDLE_TOKEN2 ("<<", LSH);
+ state->lexptr++;
+ return c;
+ case '>':
+ HANDLE_TOKEN3 (">>=", ASSIGN_MODIFY);
+ HANDLE_TOKEN2 (">=", GEQ);
+ HANDLE_TOKEN2 (">>", RSH);
+ state->lexptr++;
+ return c;
+ case '=':
+ HANDLE_TOKEN2 ("==", EQUAL);
+ state->lexptr++;
+ return c;
+ case ':':
+ HANDLE_TOKEN2 ("::", COLONCOLON);
+ state->lexptr++;
+ return c;
+
+ case '[':
+ case ']':
+ case '?':
+ case '@':
+ case '~':
+ case '{':
+ case '}':
+ symbol:
+ state->lexptr++;
+ return c;
+
+ case '"':
+ /* These can't occur in C++ names. */
+ cpname_yyerror (state, _("unexpected string literal"));
+ return ERROR;
+ }
+
+ if (!(c == '_' || c == '$' || c_ident_is_alpha (c)))
+ {
+ /* We must have come across a bad character (e.g. ';'). */
+ cpname_yyerror (state, _("invalid character"));
+ return ERROR;
+ }
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ do
+ c = tokstart[++namelen];
+ while (c_ident_is_alnum (c) || c == '_' || c == '$');
+
+ state->lexptr += namelen;
+
+ /* Catch specific keywords. Notice that some of the keywords contain
+ spaces, and are sorted by the length of the first word. They must
+ all include a trailing space in the string comparison. */
+ switch (namelen)
+ {
+ case 16:
+ if (startswith (tokstart, "reinterpret_cast"))
+ return REINTERPRET_CAST;
+ break;
+ case 12:
+ if (startswith (tokstart, "construction vtable for "))
+ {
+ state->lexptr = tokstart + 24;
+ return CONSTRUCTION_VTABLE;
+ }
+ if (startswith (tokstart, "dynamic_cast"))
+ return DYNAMIC_CAST;
+ break;
+ case 11:
+ if (startswith (tokstart, "static_cast"))
+ return STATIC_CAST;
+ break;
+ case 9:
+ HANDLE_SPECIAL ("covariant return thunk to ", DEMANGLE_COMPONENT_COVARIANT_THUNK);
+ HANDLE_SPECIAL ("reference temporary for ", DEMANGLE_COMPONENT_REFTEMP);
+ break;
+ case 8:
+ HANDLE_SPECIAL ("typeinfo for ", DEMANGLE_COMPONENT_TYPEINFO);
+ HANDLE_SPECIAL ("typeinfo fn for ", DEMANGLE_COMPONENT_TYPEINFO_FN);
+ HANDLE_SPECIAL ("typeinfo name for ", DEMANGLE_COMPONENT_TYPEINFO_NAME);
+ if (startswith (tokstart, "operator"))
+ return OPERATOR;
+ if (startswith (tokstart, "restrict"))
+ return RESTRICT;
+ if (startswith (tokstart, "unsigned"))
+ return UNSIGNED;
+ if (startswith (tokstart, "template"))
+ return TEMPLATE;
+ if (startswith (tokstart, "volatile"))
+ return VOLATILE_KEYWORD;
+ break;
+ case 7:
+ HANDLE_SPECIAL ("virtual thunk to ", DEMANGLE_COMPONENT_VIRTUAL_THUNK);
+ if (startswith (tokstart, "wchar_t"))
+ return WCHAR_T;
+ break;
+ case 6:
+ if (startswith (tokstart, "global constructors keyed to "))
+ {
+ const char *p;
+ state->lexptr = tokstart + 29;
+ lvalp->lval = DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS;
+ /* Find the end of the symbol. */
+ p = symbol_end (state->lexptr);
+ lvalp->comp = state->make_name (state->lexptr, p - state->lexptr);
+ state->lexptr = p;
+ return DEMANGLER_SPECIAL;
+ }
+ if (startswith (tokstart, "global destructors keyed to "))
+ {
+ const char *p;
+ state->lexptr = tokstart + 28;
+ lvalp->lval = DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS;
+ /* Find the end of the symbol. */
+ p = symbol_end (state->lexptr);
+ lvalp->comp = state->make_name (state->lexptr, p - state->lexptr);
+ state->lexptr = p;
+ return DEMANGLER_SPECIAL;
+ }
+
+ HANDLE_SPECIAL ("vtable for ", DEMANGLE_COMPONENT_VTABLE);
+ if (startswith (tokstart, "delete"))
+ return DELETE;
+ if (startswith (tokstart, "struct"))
+ return STRUCT;
+ if (startswith (tokstart, "signed"))
+ return SIGNED_KEYWORD;
+ if (startswith (tokstart, "sizeof"))
+ return SIZEOF;
+ if (startswith (tokstart, "double"))
+ return DOUBLE_KEYWORD;
+ break;
+ case 5:
+ HANDLE_SPECIAL ("guard variable for ", DEMANGLE_COMPONENT_GUARD);
+ if (startswith (tokstart, "false"))
+ return FALSEKEYWORD;
+ if (startswith (tokstart, "class"))
+ return CLASS;
+ if (startswith (tokstart, "union"))
+ return UNION;
+ if (startswith (tokstart, "float"))
+ return FLOAT_KEYWORD;
+ if (startswith (tokstart, "short"))
+ return SHORT;
+ if (startswith (tokstart, "const"))
+ return CONST_KEYWORD;
+ break;
+ case 4:
+ if (startswith (tokstart, "void"))
+ return VOID;
+ if (startswith (tokstart, "bool"))
+ return BOOL;
+ if (startswith (tokstart, "char"))
+ return CHAR;
+ if (startswith (tokstart, "enum"))
+ return ENUM;
+ if (startswith (tokstart, "long"))
+ return LONG;
+ if (startswith (tokstart, "true"))
+ return TRUEKEYWORD;
+ break;
+ case 3:
+ HANDLE_SPECIAL ("VTT for ", DEMANGLE_COMPONENT_VTT);
+ HANDLE_SPECIAL ("non-virtual thunk to ", DEMANGLE_COMPONENT_THUNK);
+ if (startswith (tokstart, "new"))
+ return NEW;
+ if (startswith (tokstart, "int"))
+ return INT_KEYWORD;
+ break;
+ default:
+ break;
+ }
+
+ lvalp->comp = state->make_name (tokstart, namelen);
+ return NAME;
+}
+
+#if GDB_SELF_TEST
+
+static void
+should_be_the_same (const char *one, const char *two)
+{
+ gdb::unique_xmalloc_ptr<char> cpone = cp_canonicalize_string (one);
+ gdb::unique_xmalloc_ptr<char> cptwo = cp_canonicalize_string (two);
+
+ if (cpone != nullptr)
+ one = cpone.get ();
+ if (cptwo != nullptr)
+ two = cptwo.get ();
+
+ SELF_CHECK (streq (one, two));
+}
+
+static void
+should_parse (const char *name)
+{
+ auto parsed = cp_demangled_name_to_comp (name);
+ SELF_CHECK (parsed != nullptr);
+}
+
+static void
+canonicalize_tests ()
+{
+ should_be_the_same ("short int", "short");
+ should_be_the_same ("int short", "short");
+
+ should_be_the_same ("C<(char) 1>::m()", "C<(char) '\\001'>::m()");
+ should_be_the_same ("x::y::z<1>", "x::y::z<0x01>");
+ should_be_the_same ("x::y::z<1>", "x::y::z<01>");
+ should_be_the_same ("x::y::z<(unsigned long long) 1>", "x::y::z<01ull>");
+ should_be_the_same ("x::y::z<0b111>", "x::y::z<7>");
+ should_be_the_same ("x::y::z<0b111>", "x::y::z<0t7>");
+ should_be_the_same ("x::y::z<0b111>", "x::y::z<0D7>");
+
+ should_be_the_same ("x::y::z<0xff'ff>", "x::y::z<65535>");
+
+ should_be_the_same ("something<void ()>", "something< void() >");
+ should_be_the_same ("something<void ()>", "something<void (void)>");
+
+ should_parse ("void whatever::operator<=><int, int>");
+
+ should_be_the_same ("Foozle<int>::fogey<Empty<int> > (Empty<int>)",
+ "Foozle<int>::fogey<Empty<int>> (Empty<int>)");
+
+ should_be_the_same ("something :: operator new [ ]",
+ "something::operator new[]");
+ should_be_the_same ("something :: operator new",
+ "something::operator new");
+ should_be_the_same ("operator()", "operator ()");
+}
+
+#endif
+
+INIT_GDB_FILE (cp_name_parser)
+{
+#if GDB_SELF_TEST
+ selftests::register_test ("canonicalize", canonicalize_tests);
+#endif
+}
diff --git a/gdb/cp-name-parser.h b/gdb/cp-name-parser.h
new file mode 100644
index 000000000000..31b2227227fc
--- /dev/null
+++ b/gdb/cp-name-parser.h
@@ -0,0 +1,130 @@
+/* State of the C++ name parser, for GDB.
+
+ Copyright (C) 2003-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_CP_NAME_PARSER_H
+#define GDB_CP_NAME_PARSER_H
+
+#include "demangle.h"
+
+union cp_name_parser_YYSTYPE;
+struct demangle_parse_info;
+
+/* Flags passed to cpname_state::d_qualify. */
+
+#define QUAL_CONST 1
+#define QUAL_RESTRICT 2
+#define QUAL_VOLATILE 4
+
+/* Flags passed to cpname_state::d_int_type. */
+
+#define INT_CHAR (1 << 0)
+#define INT_SHORT (1 << 1)
+#define INT_LONG (1 << 2)
+#define INT_LLONG (1 << 3)
+
+#define INT_SIGNED (1 << 4)
+#define INT_UNSIGNED (1 << 5)
+
+#define d_left(dc) (dc)->u.s_binary.left
+#define d_right(dc) (dc)->u.s_binary.right
+
+namespace cp_name_parser {
+
+/* State of an ongoing parse. */
+
+struct cpname_state
+{
+ cpname_state (const char *input, demangle_parse_info *info)
+ : lexptr (input),
+ prev_lexptr (input),
+ demangle_info (info)
+ { }
+
+ /* Un-push a character into the lexer. This can only un-push the
+ previous character in the input string. */
+ void unpush (char c)
+ {
+ gdb_assert (lexptr[-1] == c);
+ --lexptr;
+ }
+
+ /* LEXPTR is the current pointer into our lex buffer. PREV_LEXPTR
+ is the start of the last token lexed, only used for diagnostics.
+ ERROR_LEXPTR is the first place an error occurred. GLOBAL_ERRMSG
+ is the first error message encountered. */
+
+ const char *lexptr, *prev_lexptr;
+ const char *error_lexptr = nullptr;
+ const char *global_errmsg = nullptr;
+
+ demangle_parse_info *demangle_info;
+
+ /* The parse tree created by the parser is stored here after a
+ successful parse. */
+
+ struct demangle_component *global_result = nullptr;
+
+ struct demangle_component *d_grab ();
+
+ /* Helper functions. These wrap the demangler tree interface,
+ handle allocation from our global store, and return the allocated
+ component. */
+
+ struct demangle_component *fill_comp (enum demangle_component_type d_type,
+ struct demangle_component *lhs,
+ struct demangle_component *rhs);
+
+ struct demangle_component *make_operator (const char *name, int args);
+
+ struct demangle_component *make_dtor (enum gnu_v3_dtor_kinds kind,
+ struct demangle_component *name);
+
+ struct demangle_component *make_builtin_type (const char *name);
+
+ struct demangle_component *make_name (const char *name, int len);
+
+ struct demangle_component *d_qualify (struct demangle_component *lhs,
+ int qualifiers, int is_method);
+
+ struct demangle_component *d_int_type (int flags);
+
+ struct demangle_component *d_unary (const char *name,
+ struct demangle_component *lhs);
+
+ struct demangle_component *d_binary (const char *name,
+ struct demangle_component *lhs,
+ struct demangle_component *rhs);
+
+ int parse_number (const char *p, int len, int parsed_float,
+ cp_name_parser_YYSTYPE *lvalp);
+};
+
+} /* namespace cp_name_parser */
+
+/* The lexer used by the generated parser. */
+
+int cpname_yylex (cp_name_parser_YYSTYPE *lvalp,
+ cp_name_parser::cpname_state *state);
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void cpname_yyerror (cp_name_parser::cpname_state *state, const char *msg);
+
+#endif /* GDB_CP_NAME_PARSER_H */
diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
index 44ef1a41eb39..687380c4ca68 100644
--- a/gdb/cp-name-parser.y
+++ b/gdb/cp-name-parser.y
@@ -32,18 +32,14 @@
this uses %lex-param and %parse-param rather than the simpler
%param -- Byacc does not support the latter. */
%pure-parser
-%lex-param {struct cpname_state *state}
-%parse-param {struct cpname_state *state}
+%lex-param {cp_name_parser::cpname_state *state}
+%parse-param {cp_name_parser::cpname_state *state}
%{
-
-#include <unistd.h>
#include "demangle.h"
+#include "cp-name-parser.h"
#include "cp-support.h"
-#include "c-support.h"
-#include "parser-defs.h"
-#include "gdbsupport/selftest.h"
%}
@@ -67,169 +63,6 @@
const char *opname;
}
-%{
-
-struct cpname_state
-{
- cpname_state (const char *input, demangle_parse_info *info)
- : lexptr (input),
- prev_lexptr (input),
- demangle_info (info)
- { }
-
- /* Un-push a character into the lexer. This can only un-push the
- previous character in the input string. */
- void unpush (char c)
- {
- gdb_assert (lexptr[-1] == c);
- --lexptr;
- }
-
- /* LEXPTR is the current pointer into our lex buffer. PREV_LEXPTR
- is the start of the last token lexed, only used for diagnostics.
- ERROR_LEXPTR is the first place an error occurred. GLOBAL_ERRMSG
- is the first error message encountered. */
-
- const char *lexptr, *prev_lexptr;
- const char *error_lexptr = nullptr;
- const char *global_errmsg = nullptr;
-
- demangle_parse_info *demangle_info;
-
- /* The parse tree created by the parser is stored here after a
- successful parse. */
-
- struct demangle_component *global_result = nullptr;
-
- struct demangle_component *d_grab ();
-
- /* Helper functions. These wrap the demangler tree interface,
- handle allocation from our global store, and return the allocated
- component. */
-
- struct demangle_component *fill_comp (enum demangle_component_type d_type,
- struct demangle_component *lhs,
- struct demangle_component *rhs);
-
- struct demangle_component *make_operator (const char *name, int args);
-
- struct demangle_component *make_dtor (enum gnu_v3_dtor_kinds kind,
- struct demangle_component *name);
-
- struct demangle_component *make_builtin_type (const char *name);
-
- struct demangle_component *make_name (const char *name, int len);
-
- struct demangle_component *d_qualify (struct demangle_component *lhs,
- int qualifiers, int is_method);
-
- struct demangle_component *d_int_type (int flags);
-
- struct demangle_component *d_unary (const char *name,
- struct demangle_component *lhs);
-
- struct demangle_component *d_binary (const char *name,
- struct demangle_component *lhs,
- struct demangle_component *rhs);
-
- int parse_number (const char *p, int len, int parsed_float, YYSTYPE *lvalp);
-};
-
-struct demangle_component *
-cpname_state::d_grab ()
-{
- return obstack_new<demangle_component> (&demangle_info->obstack);
-}
-
-/* Flags passed to d_qualify. */
-
-#define QUAL_CONST 1
-#define QUAL_RESTRICT 2
-#define QUAL_VOLATILE 4
-
-/* Flags passed to d_int_type. */
-
-#define INT_CHAR (1 << 0)
-#define INT_SHORT (1 << 1)
-#define INT_LONG (1 << 2)
-#define INT_LLONG (1 << 3)
-
-#define INT_SIGNED (1 << 4)
-#define INT_UNSIGNED (1 << 5)
-
-/* Helper functions. These wrap the demangler tree interface, handle
- allocation from our global store, and return the allocated component. */
-
-struct demangle_component *
-cpname_state::fill_comp (enum demangle_component_type d_type,
- struct demangle_component *lhs,
- struct demangle_component *rhs)
-{
- struct demangle_component *ret = d_grab ();
- int i;
-
- i = cplus_demangle_fill_component (ret, d_type, lhs, rhs);
- gdb_assert (i);
-
- return ret;
-}
-
-struct demangle_component *
-cpname_state::make_operator (const char *name, int args)
-{
- struct demangle_component *ret = d_grab ();
- int i;
-
- i = cplus_demangle_fill_operator (ret, name, args);
- gdb_assert (i);
-
- return ret;
-}
-
-struct demangle_component *
-cpname_state::make_dtor (enum gnu_v3_dtor_kinds kind,
- struct demangle_component *name)
-{
- struct demangle_component *ret = d_grab ();
- int i;
-
- i = cplus_demangle_fill_dtor (ret, kind, name);
- gdb_assert (i);
-
- return ret;
-}
-
-struct demangle_component *
-cpname_state::make_builtin_type (const char *name)
-{
- struct demangle_component *ret = d_grab ();
- int i;
-
- i = cplus_demangle_fill_builtin_type (ret, name);
- gdb_assert (i);
-
- return ret;
-}
-
-struct demangle_component *
-cpname_state::make_name (const char *name, int len)
-{
- struct demangle_component *ret = d_grab ();
- int i;
-
- i = cplus_demangle_fill_name (ret, name, len);
- gdb_assert (i);
-
- return ret;
-}
-
-#define d_left(dc) (dc)->u.s_binary.left
-#define d_right(dc) (dc)->u.s_binary.right
-
-static int yylex (YYSTYPE *, cpname_state *);
-static void yyerror (cpname_state *, const char *);
-%}
-
%type <comp> exp exp1 type start start_opt oper colon_name
%type <comp> unqualified_name colon_ext_name
%type <comp> templ template_arg
@@ -1196,945 +1029,3 @@ exp : FALSEKEYWORD
;
/* end of C++. */
-
-%%
-
-/* Apply QUALIFIERS to LHS and return a qualified component. IS_METHOD
- is set if LHS is a method, in which case the qualifiers are logically
- applied to "this". We apply qualifiers in a consistent order; LHS
- may already be qualified; duplicate qualifiers are not created. */
-
-struct demangle_component *
-cpname_state::d_qualify (struct demangle_component *lhs, int qualifiers,
- int is_method)
-{
- struct demangle_component **inner_p;
- enum demangle_component_type type;
-
- /* For now the order is CONST (innermost), VOLATILE, RESTRICT. */
-
-#define HANDLE_QUAL(TYPE, MTYPE, QUAL) \
- if ((qualifiers & QUAL) && (type != TYPE) && (type != MTYPE)) \
- { \
- *inner_p = fill_comp (is_method ? MTYPE : TYPE, \
- *inner_p, NULL); \
- inner_p = &d_left (*inner_p); \
- type = (*inner_p)->type; \
- } \
- else if (type == TYPE || type == MTYPE) \
- { \
- inner_p = &d_left (*inner_p); \
- type = (*inner_p)->type; \
- }
-
- inner_p = &lhs;
-
- type = (*inner_p)->type;
-
- HANDLE_QUAL (DEMANGLE_COMPONENT_RESTRICT, DEMANGLE_COMPONENT_RESTRICT_THIS, QUAL_RESTRICT);
- HANDLE_QUAL (DEMANGLE_COMPONENT_VOLATILE, DEMANGLE_COMPONENT_VOLATILE_THIS, QUAL_VOLATILE);
- HANDLE_QUAL (DEMANGLE_COMPONENT_CONST, DEMANGLE_COMPONENT_CONST_THIS, QUAL_CONST);
-
- return lhs;
-}
-
-/* Return a builtin type corresponding to FLAGS. */
-
-struct demangle_component *
-cpname_state::d_int_type (int flags)
-{
- const char *name;
-
- switch (flags)
- {
- case INT_SIGNED | INT_CHAR:
- name = "signed char";
- break;
- case INT_CHAR:
- name = "char";
- break;
- case INT_UNSIGNED | INT_CHAR:
- name = "unsigned char";
- break;
- case 0:
- case INT_SIGNED:
- name = "int";
- break;
- case INT_UNSIGNED:
- name = "unsigned int";
- break;
- case INT_LONG:
- case INT_SIGNED | INT_LONG:
- name = "long";
- break;
- case INT_UNSIGNED | INT_LONG:
- name = "unsigned long";
- break;
- case INT_SHORT:
- case INT_SIGNED | INT_SHORT:
- name = "short";
- break;
- case INT_UNSIGNED | INT_SHORT:
- name = "unsigned short";
- break;
- case INT_LLONG | INT_LONG:
- case INT_SIGNED | INT_LLONG | INT_LONG:
- name = "long long";
- break;
- case INT_UNSIGNED | INT_LLONG | INT_LONG:
- name = "unsigned long long";
- break;
- default:
- return NULL;
- }
-
- return make_builtin_type (name);
-}
-
-/* Wrapper to create a unary operation. */
-
-struct demangle_component *
-cpname_state::d_unary (const char *name, struct demangle_component *lhs)
-{
- return fill_comp (DEMANGLE_COMPONENT_UNARY, make_operator (name, 1), lhs);
-}
-
-/* Wrapper to create a binary operation. */
-
-struct demangle_component *
-cpname_state::d_binary (const char *name, struct demangle_component *lhs,
- struct demangle_component *rhs)
-{
- return fill_comp (DEMANGLE_COMPONENT_BINARY, make_operator (name, 2),
- fill_comp (DEMANGLE_COMPONENT_BINARY_ARGS, lhs, rhs));
-}
-
-/* Find the end of a symbol name starting at LEXPTR. */
-
-static const char *
-symbol_end (const char *lexptr)
-{
- const char *p = lexptr;
-
- while (*p && (c_ident_is_alnum (*p) || *p == '_' || *p == '$' || *p == '.'))
- p++;
-
- return p;
-}
-
-/* Take care of parsing a number (anything that starts with a digit).
- The number starts at P and contains LEN characters. Store the result in
- YYLVAL. */
-
-int
-cpname_state::parse_number (const char *p, int len, int parsed_float,
- YYSTYPE *lvalp)
-{
- int unsigned_p = 0;
-
- /* Number of "L" suffixes encountered. */
- int long_p = 0;
-
- struct demangle_component *type, *name;
- enum demangle_component_type literal_type;
-
- if (p[0] == '-')
- {
- literal_type = DEMANGLE_COMPONENT_LITERAL_NEG;
- p++;
- len--;
- }
- else
- literal_type = DEMANGLE_COMPONENT_LITERAL;
-
- if (parsed_float)
- {
- /* It's a float since it contains a point or an exponent. */
- char c;
-
- /* The GDB lexer checks the result of scanf at this point. Not doing
- this leaves our error checking slightly weaker but only for invalid
- data. */
-
- /* See if it has `f' or `l' suffix (float or long double). */
-
- c = c_tolower (p[len - 1]);
-
- if (c == 'f')
- {
- len--;
- type = make_builtin_type ("float");
- }
- else if (c == 'l')
- {
- len--;
- type = make_builtin_type ("long double");
- }
- else if (c_isdigit (c) || c == '.')
- type = make_builtin_type ("double");
- else
- return ERROR;
-
- name = make_name (p, len);
- lvalp->comp = fill_comp (literal_type, type, name);
-
- return FLOAT;
- }
-
- /* Note that we do not automatically generate unsigned types. This
- can't be done because we don't have access to the gdbarch
- here. */
-
- int base = 10;
- if (len > 1 && p[0] == '0')
- {
- if (p[1] == 'x' || p[1] == 'X')
- {
- base = 16;
- p += 2;
- len -= 2;
- }
- else if (p[1] == 'b' || p[1] == 'B')
- {
- base = 2;
- p += 2;
- len -= 2;
- }
- else if (p[1] == 'd' || p[1] == 'D' || p[1] == 't' || p[1] == 'T')
- {
- /* Apparently gdb extensions. */
- base = 10;
- p += 2;
- len -= 2;
- }
- else
- base = 8;
- }
-
- long_p = 0;
- unsigned_p = 0;
- while (len > 0)
- {
- if (p[len - 1] == 'l' || p[len - 1] == 'L')
- {
- len--;
- long_p++;
- continue;
- }
- if (p[len - 1] == 'u' || p[len - 1] == 'U')
- {
- len--;
- unsigned_p++;
- continue;
- }
- break;
- }
-
- /* Use gdb_mpz here in case a 128-bit value appears. */
- gdb_mpz value (0);
- for (int off = 0; off < len; ++off)
- {
- int dig;
- if (c_isdigit (p[off]))
- dig = p[off] - '0';
- else
- dig = c_tolower (p[off]) - 'a' + 10;
- if (dig >= base)
- return ERROR;
- value *= base;
- value += dig;
- }
-
- std::string printed = value.str ();
- const char *copy = obstack_strdup (&demangle_info->obstack, printed);
-
- if (long_p == 0)
- {
- if (unsigned_p)
- type = make_builtin_type ("unsigned int");
- else
- type = make_builtin_type ("int");
- }
- else if (long_p == 1)
- {
- if (unsigned_p)
- type = make_builtin_type ("unsigned long");
- else
- type = make_builtin_type ("long");
- }
- else
- {
- if (unsigned_p)
- type = make_builtin_type ("unsigned long long");
- else
- type = make_builtin_type ("long long");
- }
-
- name = make_name (copy, strlen (copy));
- lvalp->comp = fill_comp (literal_type, type, name);
-
- return INT;
-}
-
-static const char backslashable[] = "abefnrtv";
-static const char represented[] = "\a\b\e\f\n\r\t\v";
-
-/* Translate the backslash the way we would in the host character set. */
-static int
-c_parse_backslash (int host_char, int *target_char)
-{
- const char *ix;
- ix = strchr (backslashable, host_char);
- if (! ix)
- return 0;
- else
- *target_char = represented[ix - backslashable];
- return 1;
-}
-
-/* Parse a C escape sequence. STRING_PTR points to a variable
- containing a pointer to the string to parse. That pointer
- should point to the character after the \. That pointer
- is updated past the characters we use. The value of the
- escape sequence is returned.
-
- A negative value means the sequence \ newline was seen,
- which is supposed to be equivalent to nothing at all.
-
- If \ is followed by a null character, we return a negative
- value and leave the string pointer pointing at the null character.
-
- If \ is followed by 000, we return 0 and leave the string pointer
- after the zeros. A value of 0 does not mean end of string. */
-
-static int
-cp_parse_escape (const char **string_ptr)
-{
- int target_char;
- int c = *(*string_ptr)++;
- if (c_parse_backslash (c, &target_char))
- return target_char;
- else
- switch (c)
- {
- case '\n':
- return -2;
- case 0:
- (*string_ptr)--;
- return 0;
- case '^':
- {
- c = *(*string_ptr)++;
-
- if (c == '?')
- return 0177;
- else if (c == '\\')
- target_char = cp_parse_escape (string_ptr);
- else
- target_char = c;
-
- /* Now target_char is something like `c', and we want to find
- its control-character equivalent. */
- target_char = target_char & 037;
-
- return target_char;
- }
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- {
- int i = c - '0';
- int count = 0;
- while (++count < 3)
- {
- c = (**string_ptr);
- if (c >= '0' && c <= '7')
- {
- (*string_ptr)++;
- i *= 8;
- i += c - '0';
- }
- else
- {
- break;
- }
- }
- return i;
- }
- default:
- return c;
- }
-}
-
-#define HANDLE_SPECIAL(string, comp) \
- if (startswith (tokstart, string)) \
- { \
- state->lexptr = tokstart + sizeof (string) - 1; \
- lvalp->lval = comp; \
- return DEMANGLER_SPECIAL; \
- }
-
-#define HANDLE_TOKEN2(string, token) \
- if (state->lexptr[1] == string[1]) \
- { \
- state->lexptr += 2; \
- lvalp->opname = string; \
- return token; \
- }
-
-#define HANDLE_TOKEN3(string, token) \
- if (state->lexptr[1] == string[1] && state->lexptr[2] == string[2]) \
- { \
- state->lexptr += 3; \
- lvalp->opname = string; \
- return token; \
- }
-
-/* Read one token, getting characters through LEXPTR. */
-
-static int
-yylex (YYSTYPE *lvalp, cpname_state *state)
-{
- int c;
- int namelen;
- const char *tokstart;
- char *copy;
-
- retry:
- state->prev_lexptr = state->lexptr;
- tokstart = state->lexptr;
-
- switch (c = *tokstart)
- {
- case 0:
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- state->lexptr++;
- goto retry;
-
- case '\'':
- /* We either have a character constant ('0' or '\177' for example)
- or we have a quoted symbol reference ('foo(int,int)' in C++
- for example). */
- state->lexptr++;
- c = *state->lexptr++;
- if (c == '\\')
- c = cp_parse_escape (&state->lexptr);
- else if (c == '\'')
- {
- yyerror (state, _("empty character constant"));
- return ERROR;
- }
-
- /* We over-allocate here, but it doesn't really matter . */
- copy = (char *) obstack_alloc (&state->demangle_info->obstack, 30);
- xsnprintf (copy, 30, "%d", c);
-
- c = *state->lexptr++;
- if (c != '\'')
- {
- yyerror (state, _("invalid character constant"));
- return ERROR;
- }
-
- lvalp->comp
- = state->fill_comp (DEMANGLE_COMPONENT_LITERAL,
- state->make_builtin_type ("char"),
- state->make_name (copy, strlen (copy)));
-
- return INT;
-
- case '(':
- if (startswith (tokstart, "(anonymous namespace)"))
- {
- state->lexptr += 21;
- lvalp->comp = state->make_name ("(anonymous namespace)",
- sizeof "(anonymous namespace)" - 1);
- return NAME;
- }
- [[fallthrough]];
-
- case ')':
- case ',':
- state->lexptr++;
- return c;
-
- case '.':
- if (state->lexptr[1] == '.' && state->lexptr[2] == '.')
- {
- state->lexptr += 3;
- return ELLIPSIS;
- }
-
- /* Might be a floating point number. */
- if (state->lexptr[1] < '0' || state->lexptr[1] > '9')
- goto symbol; /* Nope, must be a symbol. */
-
- goto try_number;
-
- case '-':
- HANDLE_TOKEN2 ("-=", ASSIGN_MODIFY);
- HANDLE_TOKEN2 ("--", DECREMENT);
- HANDLE_TOKEN2 ("->", ARROW);
-
- /* For construction vtables. This is kind of hokey. */
- if (startswith (tokstart, "-in-"))
- {
- state->lexptr += 4;
- return CONSTRUCTION_IN;
- }
-
- if (state->lexptr[1] < '0' || state->lexptr[1] > '9')
- {
- state->lexptr++;
- return '-';
- }
-
- try_number:
- [[fallthrough]];
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- /* It's a number. */
- int got_dot = 0, got_e = 0, toktype;
- const char *p = tokstart;
- int hex = 0;
-
- if (c == '-')
- p++;
-
- 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 && (*p == 'e' || *p == 'E'))
- got_dot = got_e = 1;
- /* This test does not include !hex, because a '.' always indicates
- a decimal floating point number regardless of the radix.
-
- NOTE drow/2005-03-09: This comment is not accurate in C99;
- however, it's not clear that all the floating point support
- in this file is doing any good here. */
- else if (!got_dot && *p == '.')
- got_dot = 1;
- else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
- && (*p == '-' || *p == '+'))
- {
- /* This is the sign of the exponent, not the end of
- the number. */
- }
- /* C++14 allows a separator. */
- 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 (! c_isalnum (*p))
- break;
- if (no_tick.has_value ())
- no_tick->push_back (*p);
- }
- if (no_tick.has_value ())
- toktype = state->parse_number (no_tick->c_str (),
- no_tick->length (),
- got_dot|got_e, lvalp);
- else
- toktype = state->parse_number (tokstart, p - tokstart,
- got_dot|got_e, lvalp);
- if (toktype == ERROR)
- {
- yyerror (state, _("invalid number"));
- return ERROR;
- }
- state->lexptr = p;
- return toktype;
- }
-
- case '+':
- HANDLE_TOKEN2 ("+=", ASSIGN_MODIFY);
- HANDLE_TOKEN2 ("++", INCREMENT);
- state->lexptr++;
- return c;
- case '*':
- HANDLE_TOKEN2 ("*=", ASSIGN_MODIFY);
- state->lexptr++;
- return c;
- case '/':
- HANDLE_TOKEN2 ("/=", ASSIGN_MODIFY);
- state->lexptr++;
- return c;
- case '%':
- HANDLE_TOKEN2 ("%=", ASSIGN_MODIFY);
- state->lexptr++;
- return c;
- case '|':
- HANDLE_TOKEN2 ("|=", ASSIGN_MODIFY);
- HANDLE_TOKEN2 ("||", OROR);
- state->lexptr++;
- return c;
- case '&':
- HANDLE_TOKEN2 ("&=", ASSIGN_MODIFY);
- HANDLE_TOKEN2 ("&&", ANDAND);
- state->lexptr++;
- return c;
- case '^':
- HANDLE_TOKEN2 ("^=", ASSIGN_MODIFY);
- state->lexptr++;
- return c;
- case '!':
- HANDLE_TOKEN2 ("!=", NOTEQUAL);
- state->lexptr++;
- return c;
- case '<':
- HANDLE_TOKEN3 ("<<=", ASSIGN_MODIFY);
- HANDLE_TOKEN3 ("<=>", SPACESHIP);
- HANDLE_TOKEN2 ("<=", LEQ);
- HANDLE_TOKEN2 ("<<", LSH);
- state->lexptr++;
- return c;
- case '>':
- HANDLE_TOKEN3 (">>=", ASSIGN_MODIFY);
- HANDLE_TOKEN2 (">=", GEQ);
- HANDLE_TOKEN2 (">>", RSH);
- state->lexptr++;
- return c;
- case '=':
- HANDLE_TOKEN2 ("==", EQUAL);
- state->lexptr++;
- return c;
- case ':':
- HANDLE_TOKEN2 ("::", COLONCOLON);
- state->lexptr++;
- return c;
-
- case '[':
- case ']':
- case '?':
- case '@':
- case '~':
- case '{':
- case '}':
- symbol:
- state->lexptr++;
- return c;
-
- case '"':
- /* These can't occur in C++ names. */
- yyerror (state, _("unexpected string literal"));
- return ERROR;
- }
-
- if (!(c == '_' || c == '$' || c_ident_is_alpha (c)))
- {
- /* We must have come across a bad character (e.g. ';'). */
- yyerror (state, _("invalid character"));
- return ERROR;
- }
-
- /* It's a name. See how long it is. */
- namelen = 0;
- do
- c = tokstart[++namelen];
- while (c_ident_is_alnum (c) || c == '_' || c == '$');
-
- state->lexptr += namelen;
-
- /* Catch specific keywords. Notice that some of the keywords contain
- spaces, and are sorted by the length of the first word. They must
- all include a trailing space in the string comparison. */
- switch (namelen)
- {
- case 16:
- if (startswith (tokstart, "reinterpret_cast"))
- return REINTERPRET_CAST;
- break;
- case 12:
- if (startswith (tokstart, "construction vtable for "))
- {
- state->lexptr = tokstart + 24;
- return CONSTRUCTION_VTABLE;
- }
- if (startswith (tokstart, "dynamic_cast"))
- return DYNAMIC_CAST;
- break;
- case 11:
- if (startswith (tokstart, "static_cast"))
- return STATIC_CAST;
- break;
- case 9:
- HANDLE_SPECIAL ("covariant return thunk to ", DEMANGLE_COMPONENT_COVARIANT_THUNK);
- HANDLE_SPECIAL ("reference temporary for ", DEMANGLE_COMPONENT_REFTEMP);
- break;
- case 8:
- HANDLE_SPECIAL ("typeinfo for ", DEMANGLE_COMPONENT_TYPEINFO);
- HANDLE_SPECIAL ("typeinfo fn for ", DEMANGLE_COMPONENT_TYPEINFO_FN);
- HANDLE_SPECIAL ("typeinfo name for ", DEMANGLE_COMPONENT_TYPEINFO_NAME);
- if (startswith (tokstart, "operator"))
- return OPERATOR;
- if (startswith (tokstart, "restrict"))
- return RESTRICT;
- if (startswith (tokstart, "unsigned"))
- return UNSIGNED;
- if (startswith (tokstart, "template"))
- return TEMPLATE;
- if (startswith (tokstart, "volatile"))
- return VOLATILE_KEYWORD;
- break;
- case 7:
- HANDLE_SPECIAL ("virtual thunk to ", DEMANGLE_COMPONENT_VIRTUAL_THUNK);
- if (startswith (tokstart, "wchar_t"))
- return WCHAR_T;
- break;
- case 6:
- if (startswith (tokstart, "global constructors keyed to "))
- {
- const char *p;
- state->lexptr = tokstart + 29;
- lvalp->lval = DEMANGLE_COMPONENT_GLOBAL_CONSTRUCTORS;
- /* Find the end of the symbol. */
- p = symbol_end (state->lexptr);
- lvalp->comp = state->make_name (state->lexptr, p - state->lexptr);
- state->lexptr = p;
- return DEMANGLER_SPECIAL;
- }
- if (startswith (tokstart, "global destructors keyed to "))
- {
- const char *p;
- state->lexptr = tokstart + 28;
- lvalp->lval = DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS;
- /* Find the end of the symbol. */
- p = symbol_end (state->lexptr);
- lvalp->comp = state->make_name (state->lexptr, p - state->lexptr);
- state->lexptr = p;
- return DEMANGLER_SPECIAL;
- }
-
- HANDLE_SPECIAL ("vtable for ", DEMANGLE_COMPONENT_VTABLE);
- if (startswith (tokstart, "delete"))
- return DELETE;
- if (startswith (tokstart, "struct"))
- return STRUCT;
- if (startswith (tokstart, "signed"))
- return SIGNED_KEYWORD;
- if (startswith (tokstart, "sizeof"))
- return SIZEOF;
- if (startswith (tokstart, "double"))
- return DOUBLE_KEYWORD;
- break;
- case 5:
- HANDLE_SPECIAL ("guard variable for ", DEMANGLE_COMPONENT_GUARD);
- if (startswith (tokstart, "false"))
- return FALSEKEYWORD;
- if (startswith (tokstart, "class"))
- return CLASS;
- if (startswith (tokstart, "union"))
- return UNION;
- if (startswith (tokstart, "float"))
- return FLOAT_KEYWORD;
- if (startswith (tokstart, "short"))
- return SHORT;
- if (startswith (tokstart, "const"))
- return CONST_KEYWORD;
- break;
- case 4:
- if (startswith (tokstart, "void"))
- return VOID;
- if (startswith (tokstart, "bool"))
- return BOOL;
- if (startswith (tokstart, "char"))
- return CHAR;
- if (startswith (tokstart, "enum"))
- return ENUM;
- if (startswith (tokstart, "long"))
- return LONG;
- if (startswith (tokstart, "true"))
- return TRUEKEYWORD;
- break;
- case 3:
- HANDLE_SPECIAL ("VTT for ", DEMANGLE_COMPONENT_VTT);
- HANDLE_SPECIAL ("non-virtual thunk to ", DEMANGLE_COMPONENT_THUNK);
- if (startswith (tokstart, "new"))
- return NEW;
- if (startswith (tokstart, "int"))
- return INT_KEYWORD;
- break;
- default:
- break;
- }
-
- lvalp->comp = state->make_name (tokstart, namelen);
- return NAME;
-}
-
-static void
-yyerror (cpname_state *state, const char *msg)
-{
- if (state->global_errmsg)
- return;
-
- state->error_lexptr = state->prev_lexptr;
- state->global_errmsg = msg ? msg : "parse error";
-}
-
-/* See cp-support.h. */
-
-gdb::unique_xmalloc_ptr<char>
-cp_comp_to_string (struct demangle_component *result, int estimated_len)
-{
- size_t err;
-
- char *res = gdb_cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI,
- result, estimated_len, &err);
- return gdb::unique_xmalloc_ptr<char> (res);
-}
-
-/* Merge the two parse trees given by DEST and SRC. The parse tree
- in SRC is attached to DEST at the node represented by TARGET.
-
- NOTE 1: Since there is no API to merge obstacks, this function does
- even attempt to try it. Fortunately, we do not (yet?) need this ability.
- The code will assert if SRC->obstack is not empty.
-
- NOTE 2: The string from which SRC was parsed must not be freed, since
- this function will place pointers to that string into DEST. */
-
-void
-cp_merge_demangle_parse_infos (struct demangle_parse_info *dest,
- struct demangle_component *target,
- demangle_parse_info_up src)
-
-{
- /* Copy the SRC's parse data into DEST. */
- *target = *src->tree;
-
- /* Make sure SRC is owned by DEST. */
- dest->infos.push_back (std::move (src));
-}
-
-/* Convert a demangled name to a demangle_component tree. On success,
- a structure containing the root of the new tree is returned. On
- error, NULL is returned, and an error message will be set in
- *ERRMSG. */
-
-demangle_parse_info_up
-cp_demangled_name_to_comp (const char *demangled_name,
- std::string *errmsg)
-{
- auto result = std::make_unique<demangle_parse_info> ();
- cpname_state state (demangled_name, result.get ());
-
- /* Note that we can't set yydebug here, as is done in the other
- parsers. Bison implements yydebug as a global, even with a pure
- parser, and this parser is run from worker threads. So, changing
- yydebug causes TSan reports. If you need to debug this parser,
- debug gdb and set the global from the outer gdb. */
- if (yyparse (&state))
- {
- if (state.global_errmsg && errmsg)
- *errmsg = state.global_errmsg;
- return NULL;
- }
-
- result->tree = state.global_result;
-
- return result;
-}
-
-#if GDB_SELF_TEST
-
-static void
-should_be_the_same (const char *one, const char *two)
-{
- gdb::unique_xmalloc_ptr<char> cpone = cp_canonicalize_string (one);
- gdb::unique_xmalloc_ptr<char> cptwo = cp_canonicalize_string (two);
-
- if (cpone != nullptr)
- one = cpone.get ();
- if (cptwo != nullptr)
- two = cptwo.get ();
-
- SELF_CHECK (streq (one, two));
-}
-
-static void
-should_parse (const char *name)
-{
- auto parsed = cp_demangled_name_to_comp (name);
- SELF_CHECK (parsed != nullptr);
-}
-
-static void
-canonicalize_tests ()
-{
- should_be_the_same ("short int", "short");
- should_be_the_same ("int short", "short");
-
- should_be_the_same ("C<(char) 1>::m()", "C<(char) '\\001'>::m()");
- should_be_the_same ("x::y::z<1>", "x::y::z<0x01>");
- should_be_the_same ("x::y::z<1>", "x::y::z<01>");
- should_be_the_same ("x::y::z<(unsigned long long) 1>", "x::y::z<01ull>");
- should_be_the_same ("x::y::z<0b111>", "x::y::z<7>");
- should_be_the_same ("x::y::z<0b111>", "x::y::z<0t7>");
- should_be_the_same ("x::y::z<0b111>", "x::y::z<0D7>");
-
- should_be_the_same ("x::y::z<0xff'ff>", "x::y::z<65535>");
-
- should_be_the_same ("something<void ()>", "something< void() >");
- should_be_the_same ("something<void ()>", "something<void (void)>");
-
- should_parse ("void whatever::operator<=><int, int>");
-
- should_be_the_same ("Foozle<int>::fogey<Empty<int> > (Empty<int>)",
- "Foozle<int>::fogey<Empty<int>> (Empty<int>)");
-
- should_be_the_same ("something :: operator new [ ]",
- "something::operator new[]");
- should_be_the_same ("something :: operator new",
- "something::operator new");
- should_be_the_same ("operator()", "operator ()");
-}
-
-#endif
-
-INIT_GDB_FILE (cp_name_parser)
-{
-#if GDB_SELF_TEST
- selftests::register_test ("canonicalize", canonicalize_tests);
-#endif
-}
diff --git a/gdb/cp-support.c b/gdb/cp-support.c
index b17f1e77d683..2eb4539d7b4a 100644
--- a/gdb/cp-support.c
+++ b/gdb/cp-support.c
@@ -19,6 +19,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "cp-support.h"
+#include "cp-name-parser.h"
#include "language.h"
#include "demangle.h"
#include "cli/cli-cmds.h"
@@ -43,9 +44,6 @@
#include "typeprint.h"
#include "inferior.h"
-#define d_left(dc) (dc)->u.s_binary.left
-#define d_right(dc) (dc)->u.s_binary.right
-
/* Functions related to demangled name parsing. */
static unsigned int cp_find_first_component_aux (const char *name,
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 11/19] gdb: rename LANG-exp.y to LANG-exp-parser.y
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (9 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 10/19] gdb: move cp-name-parser.y's support code to cp-name-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 12/19] gdb: move c-exp-parser.y's support code to c-exp-parser.c simon.marchi
` (7 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@efficios.com>
I would like to move the hand-written support code out of the expression
parsers, the way it was just done for cp-name-parser.y. For
consistency, I would like to follow this naming scheme:
- LANG-exp-parser.y: bison source file, generates LANG-exp-parser-gen.c
and then LANG-exp-parser-gen.o. These files will ultimately only
contain the parser rules and the C++ code associated to them, ideally
not anything too complex.
- LANG-exp-parser.{c,h}: Hand-written support code for the parsers, as
well as entry point to the parsers (wrappers around LANG_yyparse). Much
of this code currently lives at the bottom of .y files, even though it's
just regular C++ code.
- The existing files LANG-exp.{c,h} would be reserved for the expression
evaluation side of things, which is separate from the parsing.
Therefore, rename all the .y files to end with "-parser.y". I think this
makes things clear and also consistent with "cp-name-parser.y".
Change-Id: I10e8a4de7fa86c2b69b4d62581a9255929237294
---
gdb/.gitignore | 28 +++++++--------
gdb/Makefile.in | 55 +++++++++++++++--------------
gdb/{ada-exp.y => ada-exp-parser.y} | 0
gdb/ada-exp.h | 2 +-
gdb/ada-lang.h | 2 +-
gdb/ada-lex.l | 2 +-
gdb/{c-exp.y => c-exp-parser.y} | 0
gdb/c-lang.h | 2 +-
gdb/cp-name-parser.y | 2 +-
gdb/{d-exp.y => d-exp-parser.y} | 2 +-
gdb/d-lang.h | 2 +-
gdb/{f-exp.y => f-exp-parser.y} | 0
gdb/{go-exp.y => go-exp-parser.y} | 8 ++---
gdb/{m2-exp.y => m2-exp-parser.y} | 2 +-
gdb/{p-exp.y => p-exp-parser.y} | 2 +-
15 files changed, 55 insertions(+), 54 deletions(-)
rename gdb/{ada-exp.y => ada-exp-parser.y} (100%)
rename gdb/{c-exp.y => c-exp-parser.y} (100%)
rename gdb/{d-exp.y => d-exp-parser.y} (99%)
rename gdb/{f-exp.y => f-exp-parser.y} (100%)
rename gdb/{go-exp.y => go-exp-parser.y} (99%)
rename gdb/{m2-exp.y => m2-exp-parser.y} (99%)
rename gdb/{p-exp.y => p-exp-parser.y} (99%)
diff --git a/gdb/.gitignore b/gdb/.gitignore
index 6b5600dd695e..255cccbb3b24 100644
--- a/gdb/.gitignore
+++ b/gdb/.gitignore
@@ -5,25 +5,25 @@
/version.c
/xml-builtin.c
-/ada-exp-gen.c
-/ada-exp-gen.h
+/ada-exp-parser-gen.c
+/ada-exp-parser-gen.h
/ada-lex-gen.c
/ada-lex-gen.h
-/c-exp-gen.c
-/c-exp-gen.h
+/c-exp-parser-gen.c
+/c-exp-parser-gen.h
/cp-name-parser-gen.c
/cp-name-parser-gen.h
-/d-exp-gen.c
-/d-exp-gen.h
-/f-exp-gen.c
-/f-exp-gen.h
+/d-exp-parser-gen.c
+/d-exp-parser-gen.h
+/f-exp-parser-gen.c
+/f-exp-parser-gen.h
/gdb
/gcore
-/go-exp-gen.c
-/go-exp-gen.h
+/go-exp-parser-gen.c
+/go-exp-parser-gen.h
/init.c
/jit-reader.h
-/m2-exp-gen.c
-/m2-exp-gen.h
-/p-exp-gen.c
-/p-exp-gen.h
+/m2-exp-parser-gen.c
+/m2-exp-parser-gen.h
+/p-exp-parser-gen.c
+/p-exp-parser-gen.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index a7d79e08655e..1871ef255694 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1232,19 +1232,19 @@ COMMON_SFILES = \
# Links made at configuration time should not be specified here, since
# SFILES is used in building the distribution archive.
SFILES = \
- ada-exp.y \
+ ada-exp-parser.y \
arch/i386.c \
- c-exp.y \
+ c-exp-parser.y \
cp-name-parser.y \
- d-exp.y \
+ d-exp-parser.y \
dtrace-probe.c \
elf-none-tdep.c \
- f-exp.y \
+ f-exp-parser.y \
gcore-elf.c \
gdb.c \
- go-exp.y \
- m2-exp.y \
- p-exp.y \
+ go-exp-parser.y \
+ m2-exp-parser.y \
+ p-exp-parser.y \
proc-service.list \
ser-base.c \
ser-unix.c \
@@ -2059,15 +2059,15 @@ REQUIRED_SUBDIRS = doc | testsuite | data-directory
# Parser intermediate files.
YYFILES = \
- ada-exp-gen.c \
+ ada-exp-parser-gen.c \
ada-lex-gen.c \
- c-exp-gen.c \
+ c-exp-parser-gen.c \
cp-name-parser-gen.c \
- d-exp-gen.c \
- f-exp-gen.c \
- go-exp-gen.c \
- m2-exp-gen.c \
- p-exp-gen.c
+ d-exp-parser-gen.c \
+ f-exp-parser-gen.c \
+ go-exp-parser-gen.c \
+ m2-exp-parser-gen.c \
+ p-exp-parser-gen.c
# ada-lex-gen.c is included by another file, so it shouldn't wind up as
# a .o itself.
@@ -2376,7 +2376,7 @@ INIT_FILES_FILTER_OUT = \
INIT_FILES = \
$(patsubst %.o,%.c, \
- $(patsubst %-exp-gen.o,%-exp.y, \
+ $(patsubst %-exp-parser-gen.o,%-exp-parser.y, \
$(filter-out $(INIT_FILES_FILTER_OUT), $(COMMON_OBS))))
init.c: stamp-init; @true
@@ -2718,12 +2718,13 @@ po/$(PACKAGE).pot: force
#
# YACC/LEX dependencies
#
-# LANG-exp-gen.c is generated in objdir from LANG-exp.y if it doesn't
-# exist in srcdir, then compiled in objdir to LANG-exp-gen.o. If we
-# said LANG-exp-gen.c rather than ./c-exp-gen.c some makes would
-# sometimes re-write it into $(srcdir)/c-exp-gen.c. Remove bogus
+# LANG-exp-parser-gen.c is generated in objdir from LANG-exp-parser.y if
+# it doesn't exist in srcdir, then compiled in objdir to
+# LANG-exp-parser-gen.o. If we said LANG-exp-parser-gen.c rather than
+# ./c-exp-parser-gen.c some makes would sometimes re-write it into
+# $(srcdir)/c-exp-parser-gen.c. Remove bogus
# decls for malloc/realloc/free which conflict with everything else.
-# Strictly speaking c-exp-gen.c should therefore depend on
+# Strictly speaking c-exp-parser-gen.c should therefore depend on
# Makefile.in, but that was a pretty big annoyance.
# Cancel make's built-in rules for producing a .c file from a .y or .l
@@ -2741,15 +2742,15 @@ POST_PROCESS_PARSER_OUTPUT = $(SHELL) $(POST_PROCESS_PARSER_OUTPUT_SH)
# The prefix to give to the symbols each parser or lexer generator
# produces, so that they can coexist in the same program. There is no
# rule tying it to the file name, so spell it out for each one.
-YY_PREFIX_ada-exp = ada_yy
+YY_PREFIX_ada-exp-parser = ada_yy
YY_PREFIX_ada-lex = ada_yy
-YY_PREFIX_c-exp = c_yy
+YY_PREFIX_c-exp-parser = c_yy
YY_PREFIX_cp-name-parser = cpname_yy
-YY_PREFIX_d-exp = d_yy
-YY_PREFIX_f-exp = f_yy
-YY_PREFIX_go-exp = go_yy
-YY_PREFIX_m2-exp = m2_yy
-YY_PREFIX_p-exp = pascal_yy
+YY_PREFIX_d-exp-parser = d_yy
+YY_PREFIX_f-exp-parser = f_yy
+YY_PREFIX_go-exp-parser = go_yy
+YY_PREFIX_m2-exp-parser = m2_yy
+YY_PREFIX_p-exp-parser = pascal_yy
%-gen.c %-gen.h: %.y $(POST_PROCESS_PARSER_OUTPUT_SH)
$(ECHO_YACC) $(SHELL) $(YLWRAP) $< \
diff --git a/gdb/ada-exp.y b/gdb/ada-exp-parser.y
similarity index 100%
rename from gdb/ada-exp.y
rename to gdb/ada-exp-parser.y
diff --git a/gdb/ada-exp.h b/gdb/ada-exp.h
index be9b441d0c1f..459b7d37b038 100644
--- a/gdb/ada-exp.h
+++ b/gdb/ada-exp.h
@@ -453,7 +453,7 @@ class ada_structop_operation
private:
/* We may need to provide a prefix to field name completion. See
- ada-exp.y:find_completion_bounds for details. */
+ ada-exp-parser.y:find_completion_bounds for details. */
std::string m_prefix;
};
diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h
index bc821bdaa07f..e224f99dcc19 100644
--- a/gdb/ada-lang.h
+++ b/gdb/ada-lang.h
@@ -153,7 +153,7 @@ extern int ada_get_field_index (const struct type *type,
const char *field_name,
int maybe_missing);
-extern int ada_parse (struct parser_state *); /* Defined in ada-exp.y */
+extern int ada_parse (struct parser_state *); /* Defined in ada-exp-parser.y */
/* Defined in ada-typeprint.c */
extern void ada_print_type (struct type *, const char *, struct ui_file *, int,
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index 65989ebd0a56..dab1ad0d0067 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -18,7 +18,7 @@
/*----------------------------------------------------------------------*/
-/* The converted version of this file is to be included in ada-exp.y, */
+/* The converted version of this file is to be included in ada-exp-parser.y, */
/* the Ada parser for gdb. The function yylex obtains characters from */
/* the global pointer lexptr. It returns a syntactic category for */
/* each successive token and places a semantic value into yylval */
diff --git a/gdb/c-exp.y b/gdb/c-exp-parser.y
similarity index 100%
rename from gdb/c-exp.y
rename to gdb/c-exp-parser.y
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index dd1fbaefe8eb..f4458f3566db 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -58,7 +58,7 @@ enum c_string_type_values : unsigned
DEF_ENUM_FLAGS_TYPE (enum c_string_type_values, c_string_type);
-/* Defined in c-exp.y. */
+/* Defined in c-exp-parser.y. */
extern int c_parse (struct parser_state *);
diff --git a/gdb/cp-name-parser.y b/gdb/cp-name-parser.y
index 687380c4ca68..57247f459fab 100644
--- a/gdb/cp-name-parser.y
+++ b/gdb/cp-name-parser.y
@@ -2,7 +2,7 @@
Copyright (C) 2003-2026 Free Software Foundation, Inc.
- Parts of the lexer are based on c-exp.y from GDB.
+ Parts of the lexer are based on c-exp-parser.y from GDB.
This file is part of GDB.
diff --git a/gdb/d-exp.y b/gdb/d-exp-parser.y
similarity index 99%
rename from gdb/d-exp.y
rename to gdb/d-exp-parser.y
index 3b3a212cd741..ee23a6c3254c 100644
--- a/gdb/d-exp.y
+++ b/gdb/d-exp-parser.y
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-/* This file is derived from c-exp.y, jv-exp.y. */
+/* This file is derived from c-exp-parser.y, jv-exp.y. */
/* Parse a D expression from text in a string,
and return the result as a struct expression pointer.
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index 7921d3ac804b..e8752ac95306 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -54,7 +54,7 @@ struct builtin_d_type
struct type *builtin_dchar = nullptr;
};
-/* Defined in d-exp.y. */
+/* Defined in d-exp-parser.y. */
extern int d_parse (struct parser_state *);
diff --git a/gdb/f-exp.y b/gdb/f-exp-parser.y
similarity index 100%
rename from gdb/f-exp.y
rename to gdb/f-exp-parser.y
diff --git a/gdb/go-exp.y b/gdb/go-exp-parser.y
similarity index 99%
rename from gdb/go-exp.y
rename to gdb/go-exp-parser.y
index 1384c3ebeeec..ce29a00e1228 100644
--- a/gdb/go-exp.y
+++ b/gdb/go-exp-parser.y
@@ -17,7 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-/* This file is derived from c-exp.y, p-exp.y. */
+/* This file is derived from c-exp-parser.y, p-exp-parser.y. */
/* Parse a Go expression from text in a string,
and return the result as a struct expression pointer.
@@ -626,9 +626,9 @@ name_not_typename
LEN is the number of characters in it. */
/* FIXME: Needs some error checking for the float case. */
-/* FIXME(dje): IWBN to use c-exp.y's parse_number if we could.
+/* FIXME(dje): IWBN to use c-exp-parser.y's parse_number if we could.
That will require moving the guts into a function that we both call
- as our YYSTYPE is different than c-exp.y's */
+ as our YYSTYPE is different than c-exp-parser.y's */
static int
parse_number (struct parser_state *par_state,
@@ -1422,7 +1422,7 @@ classify_name (struct parser_state *par_state, const struct block *block)
return NAME;
}
-/* This is taken from c-exp.y mostly to get something working.
+/* This is taken from c-exp-parser.y mostly to get something working.
The basic structure has been kept because we may yet need some of it. */
static int
diff --git a/gdb/m2-exp.y b/gdb/m2-exp-parser.y
similarity index 99%
rename from gdb/m2-exp.y
rename to gdb/m2-exp-parser.y
index fae0f86ffdae..0f1c62c7ae52 100644
--- a/gdb/m2-exp.y
+++ b/gdb/m2-exp-parser.y
@@ -1,6 +1,6 @@
/* YACC grammar for Modula-2 expressions, for GDB.
Copyright (C) 1986-2026 Free Software Foundation, Inc.
- Generated from expread.y (now c-exp.y) and contributed by the Department
+ Generated from expread.y (now c-exp-parser.y) and contributed by the Department
of Computer Science at the State University of New York at Buffalo, 1991.
This file is part of GDB.
diff --git a/gdb/p-exp.y b/gdb/p-exp-parser.y
similarity index 99%
rename from gdb/p-exp.y
rename to gdb/p-exp-parser.y
index dd4ae2cb209b..0bb2fca36965 100644
--- a/gdb/p-exp.y
+++ b/gdb/p-exp-parser.y
@@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
-/* This file is derived from c-exp.y */
+/* This file is derived from c-exp-parser.y */
/* Parse a Pascal expression from text in a string,
and return the result as a struct expression pointer.
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 12/19] gdb: move c-exp-parser.y's support code to c-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (10 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 11/19] gdb: rename LANG-exp.y to LANG-exp-parser.y simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c simon.marchi
` (6 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
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
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (11 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 12/19] gdb: move c-exp-parser.y's support code to c-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-08 18:28 ` Kevin Buettner
2026-09-05 4:23 ` [PATCH v2 14/19] gdb: move d-exp-parser.y's support code to d-exp-parser.c simon.marchi
` (5 subsequent siblings)
18 siblings, 1 reply; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Similar to the previous commit, but for the Ada expression parser.
This one is slightly different because the Ada parser also uses a
flex-generated lexer (the only one in the GDB tree to do so). So a few
functions are moved from ada-exp.l to ada-exp-parser.{c,h}. But the idea
remains the same.
The lexer_init function is still defined in ada-exp.l, because it needs to
see some special macros defined in ada-lex-gen.c.
Put the parser support code inside the ada_exp_parser namespace.
Change-Id: I38bee40f9b6c91c947ba8ab25fe9caa6fb9f9f15
---
gdb/Makefile.in | 2 +
gdb/ada-exp-parser.c | 1351 ++++++++++++++++++++++++++++++++++++++++++
gdb/ada-exp-parser.h | 427 +++++++++++++
gdb/ada-exp-parser.y | 1152 +----------------------------------
gdb/ada-exp.h | 2 +-
gdb/ada-lang.c | 1 +
gdb/ada-lang.h | 2 -
gdb/ada-lex.l | 429 +-------------
8 files changed, 1791 insertions(+), 1575 deletions(-)
create mode 100644 gdb/ada-exp-parser.c
create mode 100644 gdb/ada-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4cd503e2175f..d2cfecb0def9 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1032,6 +1032,7 @@ TARGET_FLAGS_TO_PASS = \
# Files that should wind up in SFILES and whose corresponding .o
# should be in COMMON_OBS.
COMMON_SFILES = \
+ ada-exp-parser.c \
ada-lang.c \
ada-tasks.c \
ada-typeprint.c \
@@ -1275,6 +1276,7 @@ HFILES_NO_SRCDIR = \
aarch64-ravenscar-thread.h \
aarch64-tdep.h \
ada-casefold.h \
+ ada-exp-parser.h \
ada-exp.h \
ada-lang.h \
addrmap.h \
diff --git a/gdb/ada-exp-parser.c b/gdb/ada-exp-parser.c
new file mode 100644
index 000000000000..4cae9e4d1fe8
--- /dev/null
+++ b/gdb/ada-exp-parser.c
@@ -0,0 +1,1351 @@
+/* Support code for the Ada 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 "ada-exp-parser.h"
+#include "ada-exp-parser-gen.h"
+#include "ada-lang.h"
+#include "ada-lex-gen.h"
+#include "block.h"
+#include "cli/cli-style.h"
+#include "gdbsupport/scoped_restore.h"
+#include "parser-defs.h"
+
+using namespace expr;
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ ada-exp-parser-gen.c. Bison produces a declaration for ada_yyparse in
+ ada-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int ada_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for ada_yydebug. */
+
+extern int ada_yydebug;
+
+namespace ada_exp_parser
+{
+
+/* See ada-exp-parser.h. */
+
+struct parser_state *pstate;
+
+/* See ada-exp-parser.h. */
+
+struct ada_parse_state *ada_parser;
+
+/* See ada-exp-parser.h. */
+
+void
+canonicalizeNumeral (char *s1, const char *s2)
+{
+ for (; *s2 != '\000'; s2 += 1)
+ {
+ if (*s2 != '_')
+ {
+ *s1 = c_tolower(*s2);
+ s1 += 1;
+ }
+ }
+ s1[0] = '\000';
+}
+
+/* See ada-exp-parser.h. */
+
+int
+processInt (struct parser_state *par_state, const char *base0,
+ const char *num0, const char *exp0)
+{
+ long exp;
+ int base;
+ /* For the based literal with an "f" prefix, we'll return a
+ floating-point number. This counts the number of "l"s seen,
+ to decide the width of the floating-point number to return. -1
+ means no "f". */
+ int floating_point_l_count = -1;
+
+ if (base0 == NULL)
+ base = 10;
+ else
+ {
+ char *end_of_base;
+ base = strtol (base0, &end_of_base, 10);
+ if (base < 2 || base > 16)
+ error (_("Invalid base: %d."), base);
+ while (*end_of_base == 'l')
+ {
+ ++floating_point_l_count;
+ ++end_of_base;
+ }
+ /* This assertion is ensured by the pattern. */
+ gdb_assert (floating_point_l_count == -1 || *end_of_base == 'f');
+ if (*end_of_base == 'f')
+ {
+ ++end_of_base;
+ ++floating_point_l_count;
+ }
+ /* This assertion is ensured by the pattern. */
+ gdb_assert (*end_of_base == '#');
+ }
+
+ if (exp0 == NULL)
+ exp = 0;
+ else
+ exp = strtol(exp0, (char **) NULL, 10);
+
+ gdb_mpz result;
+ while (c_isxdigit (*num0))
+ {
+ int dig = fromhex (*num0);
+ if (dig >= base)
+ error (_("Invalid digit `%c' in based literal"), *num0);
+ result *= base;
+ result += dig;
+ ++num0;
+ }
+
+ while (exp > 0)
+ {
+ result *= base;
+ exp -= 1;
+ }
+
+ if (floating_point_l_count > -1)
+ {
+ struct type *fp_type;
+ if (floating_point_l_count == 0)
+ fp_type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "float");
+ else if (floating_point_l_count == 1)
+ fp_type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "long_float");
+ else
+ {
+ /* This assertion is ensured by the pattern. */
+ gdb_assert (floating_point_l_count == 2);
+ fp_type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "long_long_float");
+ }
+
+ ada_yylval.typed_val_float.type = fp_type;
+ result.write (gdb::make_array_view (ada_yylval.typed_val_float.val,
+ fp_type->length ()),
+ type_byte_order (fp_type),
+ true);
+
+ return FLOAT;
+ }
+
+ const gdb_mpz *value = ada_parser->push_integer (std::move (result));
+
+ 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 ());
+
+ if (fits_in_type (1, *value, int_bits, true))
+ ada_yylval.typed_val.type = parse_type (par_state)->builtin_int;
+ else if (fits_in_type (1, *value, long_bits, true))
+ ada_yylval.typed_val.type = parse_type (par_state)->builtin_long;
+ else if (fits_in_type (1, *value, long_bits, false))
+ ada_yylval.typed_val.type
+ = builtin_type (par_state->gdbarch ())->builtin_unsigned_long;
+ else if (fits_in_type (1, *value, long_long_bits, true))
+ ada_yylval.typed_val.type = parse_type (par_state)->builtin_long_long;
+ else if (fits_in_type (1, *value, long_long_bits, false))
+ ada_yylval.typed_val.type
+ = builtin_type (par_state->gdbarch ())->builtin_unsigned_long_long;
+ else if (fits_in_type (1, *value, 128, true))
+ ada_yylval.typed_val.type
+ = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "long_long_long_integer");
+ else if (fits_in_type (1, *value, 128, false))
+ ada_yylval.typed_val.type
+ = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "unsigned_long_long_long_integer");
+ else
+ error (_("Integer literal out of range"));
+
+ ada_yylval.typed_val.val = value;
+ return INT;
+}
+
+/* See ada-exp-parser.h. */
+
+int
+processReal (struct parser_state *par_state, const char *num0)
+{
+ ada_yylval.typed_val_float.type = parse_type (par_state)->builtin_long_double;
+
+ bool parsed = parse_float (num0, strlen (num0),
+ ada_yylval.typed_val_float.type,
+ ada_yylval.typed_val_float.val);
+ gdb_assert (parsed);
+ return FLOAT;
+}
+
+
+/* See ada-exp-parser.h. */
+
+struct stoken
+processId (const char *name0, int len)
+{
+ char *name = (char *) obstack_alloc (&ada_parser->temp_space, len + 11);
+ int i0, i;
+ struct stoken result;
+
+ result.ptr = name;
+ while (len > 0 && c_isspace (name0[len-1]))
+ len -= 1;
+
+ if (name0[0] == '<' || strstr (name0, "___") != NULL)
+ {
+ strncpy (name, name0, len);
+ name[len] = '\000';
+ result.length = len;
+ return result;
+ }
+
+ bool in_quotes = false;
+ i = i0 = 0;
+ while (i0 < len)
+ {
+ if (name0[i0] == COMPLETE_CHAR)
+ {
+ /* Just ignore. */
+ ++i0;
+ }
+ else if (in_quotes)
+ name[i++] = name0[i0++];
+ else if (c_isalnum (name0[i0]))
+ {
+ name[i] = c_tolower (name0[i0]);
+ i += 1; i0 += 1;
+ }
+ else if (c_isspace (name0[i0]))
+ i0 += 1;
+ else if (name0[i0] == '\'')
+ {
+ /* Copy the starting quote, but not the ending quote. */
+ if (!in_quotes)
+ name[i++] = name0[i0++];
+ in_quotes = !in_quotes;
+ }
+ else
+ name[i++] = name0[i0++];
+ }
+ name[i] = '\000';
+
+ result.length = i;
+ return result;
+}
+
+/* See ada-exp-parser.h. */
+
+struct stoken
+processString (const char *text, int len)
+{
+ const char *p;
+ char *q;
+ const char *lim = text + len;
+ struct stoken result;
+
+ q = (char *) obstack_alloc (&ada_parser->temp_space, len);
+ result.ptr = q;
+ p = text;
+ while (p < lim)
+ {
+ if (p[0] == '[' && p[1] == '"' && p+2 < lim)
+ {
+ if (p[2] == '"') /* "...["""]... */
+ {
+ *q = '"';
+ p += 4;
+ }
+ else
+ {
+ const char *end;
+ ULONGEST chr = strtoulst (p + 2, &end, 16);
+ if (chr > 0xff)
+ error (_("wide strings are not yet supported"));
+ *q = (char) chr;
+ p = end + 1;
+ }
+ }
+ else
+ *q = *p;
+ q += 1;
+ p += 1;
+ }
+ result.length = q - result.ptr;
+ return result;
+}
+
+/* See ada-exp-parser.h. */
+
+int
+find_dot_all (const char *str)
+{
+ int i;
+
+ for (i = 0; str[i] != '\000'; i++)
+ if (str[i] == '.')
+ {
+ int i0 = i;
+
+ do
+ i += 1;
+ while (c_isspace (str[i]));
+
+ if (strncasecmp (str + i, "all", 3) == 0
+ && !c_isalnum (str[i + 3]) && str[i + 3] != '_')
+ return i0;
+ }
+ return -1;
+}
+
+/* Returns non-zero iff string SUBSEQ matches a subsequence of STR, ignoring
+ case. */
+
+static int
+subseqMatch (const char *subseq, const char *str)
+{
+ if (subseq[0] == '\0')
+ return 1;
+ else if (str[0] == '\0')
+ return 0;
+ else if (c_tolower (subseq[0]) == c_tolower (str[0]))
+ return subseqMatch (subseq+1, str+1) || subseqMatch (subseq, str+1);
+ else
+ return subseqMatch (subseq, str+1);
+}
+
+
+static const struct { const char *name; int code; }
+attributes[] = {
+ { "address", TICK_ADDRESS },
+ { "unchecked_access", TICK_ACCESS },
+ { "unrestricted_access", TICK_ACCESS },
+ { "access", TICK_ACCESS },
+ { "first", TICK_FIRST },
+ { "last", TICK_LAST },
+ { "length", TICK_LENGTH },
+ { "max", TICK_MAX },
+ { "min", TICK_MIN },
+ { "modulus", TICK_MODULUS },
+ { "object_size", TICK_OBJECT_SIZE },
+ { "pos", TICK_POS },
+ { "range", TICK_RANGE },
+ { "size", TICK_SIZE },
+ { "tag", TICK_TAG },
+ { "val", TICK_VAL },
+ { "enum_rep", TICK_ENUM_REP },
+ { "enum_val", TICK_ENUM_VAL },
+};
+
+/* See ada-exp-parser.h. */
+
+int
+processAttribute (const char *str)
+{
+ gdb_assert (*str == '\'');
+ ++str;
+ while (c_isspace (*str))
+ ++str;
+
+ int len = strlen (str);
+ if (len > 0 && str[len - 1] == COMPLETE_CHAR)
+ {
+ /* This is enforced by YY_INPUT. */
+ gdb_assert (pstate->parse_completion);
+ ada_yylval.sval.ptr = obstack_strndup (&ada_parser->temp_space,
+ str, len - 1);
+ ada_yylval.sval.length = len - 1;
+ return TICK_COMPLETE;
+ }
+
+ for (const auto &item : attributes)
+ if (strcasecmp (str, item.name) == 0)
+ return item.code;
+
+ std::optional<int> found;
+ for (const auto &item : attributes)
+ if (subseqMatch (str, item.name))
+ {
+ if (!found.has_value ())
+ found = item.code;
+ else
+ error (_("ambiguous attribute name: `%s'"), str);
+ }
+ if (!found.has_value ())
+ error (_("unrecognized attribute: `%s'"), str);
+
+ return *found;
+}
+
+bool
+ada_tick_completer::complete (struct expression *exp,
+ completion_tracker &tracker)
+{
+ completion_list output;
+ for (const auto &item : attributes)
+ {
+ if (strncasecmp (item.name, m_name.c_str (), m_name.length ()) == 0)
+ output.emplace_back (xstrdup (item.name));
+ }
+ tracker.add_completions (std::move (output));
+ return true;
+}
+
+/* See ada-exp-parser.h. */
+
+void
+rewind_to_char (int ch)
+{
+ pstate->lexptr -= ada_yyleng;
+ while (c_toupper (*pstate->lexptr) != c_toupper (ch))
+ pstate->lexptr -= 1;
+ ada_yyrestart (NULL);
+}
+
+/* See ada-exp-parser.h. */
+
+operation_up
+resolve (operation_up &&op, bool deprocedure_p, struct type *context_type)
+{
+ operation_up result = std::move (op);
+ ada_resolvable *res = dynamic_cast<ada_resolvable *> (result.get ());
+ if (res != nullptr)
+ return res->replace (std::move (result),
+ pstate->expout.get (),
+ deprocedure_p,
+ pstate->parse_completion,
+ pstate->block_tracker,
+ context_type);
+ return result;
+}
+
+/* See ada-exp-parser.h. */
+
+operation_up
+ada_pop (bool deprocedure_p, struct type *context_type)
+{
+ /* Of course it's ok to call parser_state::pop here... */
+ return resolve (pstate->pop (), deprocedure_p, context_type);
+}
+
+/* See ada-exp-parser.h. */
+
+void
+ada_addrof (struct type *type)
+{
+ operation_up arg = ada_pop (false);
+ operation_up addr = make_operation<unop_addr_operation> (std::move (arg));
+ operation_up wrapped
+ = make_operation<ada_wrapped_operation> (std::move (addr));
+ if (type != nullptr)
+ wrapped = make_operation<unop_cast_operation> (std::move (wrapped), type);
+ pstate->push (std::move (wrapped));
+}
+
+/* See ada-exp-parser.h. */
+
+operation_up
+maybe_overload (enum exp_opcode op, operation_up &lhs, operation_up &rhs)
+{
+ struct value *args[2];
+
+ int nargs = 1;
+ args[0] = lhs->evaluate (nullptr, pstate->expout.get (),
+ EVAL_AVOID_SIDE_EFFECTS);
+ if (rhs == nullptr)
+ args[1] = nullptr;
+ else
+ {
+ args[1] = rhs->evaluate (nullptr, pstate->expout.get (),
+ EVAL_AVOID_SIDE_EFFECTS);
+ ++nargs;
+ }
+
+ block_symbol fn = ada_find_operator_symbol (op, pstate->parse_completion,
+ nargs, args);
+ if (fn.symbol == nullptr)
+ return {};
+
+ if (symbol_read_needs_frame (fn.symbol))
+ pstate->block_tracker->update (fn.block, INNERMOST_BLOCK_FOR_SYMBOLS);
+ operation_up callee = make_operation<ada_var_value_operation> (fn);
+
+ std::vector<operation_up> argvec;
+ argvec.push_back (std::move (lhs));
+ if (rhs != nullptr)
+ argvec.push_back (std::move (rhs));
+ return make_operation<ada_funcall_operation> (std::move (callee),
+ std::move (argvec));
+}
+
+/* See ada-exp-parser.h. */
+
+void
+ada_funcall (int nargs)
+{
+ /* We use the ordinary pop here, because we're going to do
+ resolution in a separate step, in order to handle array
+ indices. */
+ std::vector<operation_up> args = pstate->pop_vector (nargs);
+ /* Call parser_state::pop here, because we don't want to
+ function-convert the callee slot of a call we're already
+ constructing. */
+ operation_up callee = pstate->pop ();
+
+ ada_var_value_operation *vvo
+ = dynamic_cast<ada_var_value_operation *> (callee.get ());
+ int array_arity = 0;
+ struct type *callee_t = nullptr;
+ if (vvo == nullptr
+ || vvo->get_symbol ()->domain () != UNDEF_DOMAIN)
+ {
+ struct value *callee_v = callee->evaluate (nullptr,
+ pstate->expout.get (),
+ EVAL_AVOID_SIDE_EFFECTS);
+ callee_t = ada_check_typedef (callee_v->type ());
+ array_arity = ada_array_arity (callee_t);
+ }
+
+ for (int i = 0; i < nargs; ++i)
+ {
+ struct type *subtype = nullptr;
+ if (i < array_arity)
+ subtype = ada_index_type (callee_t, i + 1, "array type");
+ args[i] = resolve (std::move (args[i]), true, subtype);
+ }
+
+ std::unique_ptr<ada_funcall_operation> funcall
+ (new ada_funcall_operation (std::move (callee), std::move (args)));
+ funcall->resolve (pstate->expout.get (), true, pstate->parse_completion,
+ pstate->block_tracker, nullptr);
+ pstate->push (std::move (funcall));
+}
+
+/* See ada-exp-parser.h. */
+
+ada_choices_component *
+choice_component ()
+{
+ ada_component *last = ada_parser->components.back ().get ();
+ return gdb::checked_static_cast<ada_choices_component *> (last);
+}
+
+/* See ada-exp-parser.h. */
+
+ada_component_up
+pop_component ()
+{
+ ada_component_up result = std::move (ada_parser->components.back ());
+ ada_parser->components.pop_back ();
+ return result;
+}
+
+/* See ada-exp-parser.h. */
+
+std::vector<ada_component_up>
+pop_components (int n)
+{
+ std::vector<ada_component_up> result (n);
+ for (int i = 1; i <= n; ++i)
+ result[n - i] = pop_component ();
+ return result;
+}
+
+/* Pop the most recent association from the global stack, and return
+ it. */
+static ada_association_up
+pop_association ()
+{
+ ada_association_up result = std::move (ada_parser->associations.back ());
+ ada_parser->associations.pop_back ();
+ return result;
+}
+
+/* See ada-exp-parser.h. */
+
+std::vector<ada_association_up>
+pop_associations (int n)
+{
+ std::vector<ada_association_up> result (n);
+ for (int i = 1; i <= n; ++i)
+ result[n - i] = pop_association ();
+ return result;
+}
+
+/* See ada-exp-parser.h. */
+
+std::unique_ptr<expr_completion_base>
+make_tick_completer (struct stoken tok)
+{
+ return (std::unique_ptr<expr_completion_base>
+ (new ada_tick_completer (std::string (tok.ptr, tok.length))));
+}
+
+/* Emit expression to access an instance of SYM, in block BLOCK (if
+ non-NULL). */
+
+static void
+write_var_from_sym (struct parser_state *par_state, block_symbol sym)
+{
+ if (symbol_read_needs_frame (sym.symbol))
+ par_state->block_tracker->update (sym.block, INNERMOST_BLOCK_FOR_SYMBOLS);
+
+ par_state->push_new<ada_var_value_operation> (sym);
+}
+
+/* See ada-exp-parser.h. */
+
+void
+write_int (struct parser_state *par_state, LONGEST arg, struct type *type)
+{
+ pstate->push_new<long_const_operation> (type, arg);
+ ada_wrap<ada_wrapped_operation> ();
+}
+
+/* Emit expression corresponding to the renamed object named
+ designated by RENAMED_ENTITY[0 .. RENAMED_ENTITY_LEN-1] in the
+ context of ORIG_LEFT_CONTEXT, to which is applied the operations
+ encoded by RENAMING_EXPR. MAX_DEPTH is the maximum number of
+ cascaded renamings to allow. If ORIG_LEFT_CONTEXT is null, it
+ defaults to the currently selected block. ORIG_SYMBOL is the
+ symbol that originally encoded the renaming. It is needed only
+ because its prefix also qualifies any index variables used to index
+ or slice an array. It should not be necessary once we go to the
+ new encoding entirely (FIXME pnh 7/20/2007). */
+
+static void
+write_object_renaming (struct parser_state *par_state,
+ const struct block *orig_left_context,
+ const char *renamed_entity, int renamed_entity_len,
+ const char *renaming_expr, int max_depth)
+{
+ char *name;
+ enum { SIMPLE_INDEX, LOWER_BOUND, UPPER_BOUND } slice_state;
+
+ if (max_depth <= 0)
+ error (_("Could not find renamed symbol"));
+
+ if (orig_left_context == NULL)
+ orig_left_context = get_selected_block ();
+
+ name = obstack_strndup (&ada_parser->temp_space, renamed_entity,
+ renamed_entity_len);
+ block_symbol sym_info = ada_lookup_encoded_symbol (name, orig_left_context,
+ SEARCH_VFT);
+ if (sym_info.symbol == NULL)
+ error (_("Could not find renamed variable: %ps"),
+ styled_string (variable_name_style.style (),
+ ada_decode (name).c_str ()));
+ else if (sym_info.symbol->loc_class () == LOC_TYPEDEF)
+ /* We have a renaming of an old-style renaming symbol. Don't
+ trust the block information. */
+ sym_info.block = orig_left_context;
+
+ {
+ const char *inner_renamed_entity;
+ int inner_renamed_entity_len;
+ const char *inner_renaming_expr;
+
+ switch (ada_parse_renaming (sym_info.symbol, &inner_renamed_entity,
+ &inner_renamed_entity_len,
+ &inner_renaming_expr))
+ {
+ case ADA_NOT_RENAMING:
+ write_var_from_sym (par_state, sym_info);
+ break;
+ case ADA_OBJECT_RENAMING:
+ write_object_renaming (par_state, sym_info.block,
+ inner_renamed_entity, inner_renamed_entity_len,
+ inner_renaming_expr, max_depth - 1);
+ break;
+ default:
+ goto BadEncoding;
+ }
+ }
+
+ slice_state = SIMPLE_INDEX;
+ while (*renaming_expr == 'X')
+ {
+ renaming_expr += 1;
+
+ switch (*renaming_expr) {
+ case 'A':
+ renaming_expr += 1;
+ ada_wrap<ada_unop_ind_operation> ();
+ break;
+ case 'L':
+ slice_state = LOWER_BOUND;
+ [[fallthrough]];
+ case 'S':
+ renaming_expr += 1;
+ if (c_isdigit (*renaming_expr))
+ {
+ char *next;
+ long val = strtol (renaming_expr, &next, 10);
+ if (next == renaming_expr)
+ goto BadEncoding;
+ renaming_expr = next;
+ write_int (par_state, val, parse_type (par_state)->builtin_int);
+ }
+ else
+ {
+ const char *end;
+ char *index_name;
+
+ end = strchr (renaming_expr, 'X');
+ if (end == NULL)
+ end = renaming_expr + strlen (renaming_expr);
+
+ index_name = obstack_strndup (&ada_parser->temp_space,
+ renaming_expr,
+ end - renaming_expr);
+ renaming_expr = end;
+
+ block_symbol index_sym_info
+ = ada_lookup_encoded_symbol (index_name, orig_left_context,
+ SEARCH_VFT);
+ if (index_sym_info.symbol == NULL)
+ error (_("Could not find %s"), index_name);
+ else if (index_sym_info.symbol->loc_class () == LOC_TYPEDEF)
+ /* Index is an old-style renaming symbol. */
+ index_sym_info.block = orig_left_context;
+ write_var_from_sym (par_state, index_sym_info);
+ }
+ if (slice_state == SIMPLE_INDEX)
+ ada_funcall (1);
+ else if (slice_state == LOWER_BOUND)
+ slice_state = UPPER_BOUND;
+ else if (slice_state == UPPER_BOUND)
+ {
+ ada_wrap3<ada_ternop_slice_operation> ();
+ slice_state = SIMPLE_INDEX;
+ }
+ break;
+
+ case 'R':
+ {
+ const char *end;
+
+ renaming_expr += 1;
+
+ if (slice_state != SIMPLE_INDEX)
+ goto BadEncoding;
+ end = strchr (renaming_expr, 'X');
+ if (end == NULL)
+ end = renaming_expr + strlen (renaming_expr);
+
+ operation_up arg = ada_pop ();
+ pstate->push_new<ada_structop_operation>
+ (std::move (arg), std::string (renaming_expr,
+ end - renaming_expr));
+ renaming_expr = end;
+ break;
+ }
+
+ default:
+ goto BadEncoding;
+ }
+ }
+ if (slice_state == SIMPLE_INDEX)
+ return;
+
+ BadEncoding:
+ error (_("Internal error in encoding of renaming declaration"));
+}
+
+/* See ada-exp-parser.h. */
+
+const struct block*
+block_lookup (const struct block *context, const char *raw_name)
+{
+ const char *name;
+ struct symtab *symtab;
+ const struct block *result = NULL;
+
+ std::string name_storage;
+ if (raw_name[0] == '\'')
+ {
+ raw_name += 1;
+ name = raw_name;
+ }
+ else
+ {
+ name_storage = ada_encode (raw_name);
+ name = name_storage.c_str ();
+ }
+
+ std::vector<struct block_symbol> syms
+ = ada_lookup_symbol_list (name, context, SEARCH_FUNCTION_DOMAIN);
+
+ if (context == NULL
+ && (syms.empty () || syms[0].symbol->loc_class () != LOC_BLOCK))
+ symtab = lookup_symtab (current_program_space, name);
+ else
+ symtab = NULL;
+
+ if (symtab != NULL)
+ result = symtab->compunit ().blockvector ()->static_block ();
+ else if (syms.empty () || syms[0].symbol->loc_class () != LOC_BLOCK)
+ {
+ if (context == NULL)
+ error (_("No file or function \"%s\"."), raw_name);
+ else
+ error (_("No function \"%s\" in specified context."), raw_name);
+ }
+ else
+ {
+ if (syms.size () > 1)
+ warning (_("Function name \"%s\" ambiguous here"), raw_name);
+ result = syms[0].symbol->value_block ();
+ }
+
+ return result;
+}
+
+static struct symbol*
+select_possible_type_sym (const std::vector<struct block_symbol> &syms)
+{
+ int i;
+ int preferred_index;
+ struct type *preferred_type;
+
+ preferred_index = -1; preferred_type = NULL;
+ for (i = 0; i < syms.size (); i += 1)
+ switch (syms[i].symbol->loc_class ())
+ {
+ case LOC_TYPEDEF:
+ if (ada_prefer_type (syms[i].symbol->type (), preferred_type))
+ {
+ preferred_index = i;
+ preferred_type = syms[i].symbol->type ();
+ }
+ break;
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_COMPUTED:
+ return NULL;
+ default:
+ break;
+ }
+ if (preferred_type == NULL)
+ return NULL;
+ return syms[preferred_index].symbol;
+}
+
+static struct type*
+find_primitive_type (struct parser_state *par_state, const char *name)
+{
+ struct type *type;
+ type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ name);
+ if (type == NULL && streq ("system__address", name))
+ type = type_system_address (par_state);
+
+ if (type != NULL)
+ {
+ /* Check to see if we have a regular definition of this
+ type that just didn't happen to have been read yet. */
+ struct symbol *sym;
+ char *expanded_name =
+ (char *) alloca (strlen (name) + sizeof ("standard__"));
+ strcpy (expanded_name, "standard__");
+ strcat (expanded_name, name);
+ sym = ada_lookup_symbol (expanded_name, NULL, SEARCH_TYPE_DOMAIN).symbol;
+ if (sym != NULL && sym->loc_class () == LOC_TYPEDEF)
+ type = sym->type ();
+ }
+
+ return type;
+}
+
+static int
+chop_selector (const char *name, int end)
+{
+ int i;
+ for (i = end - 1; i > 0; i -= 1)
+ if (name[i] == '.' || (name[i] == '_' && name[i+1] == '_'))
+ return i;
+ return -1;
+}
+
+/* If NAME is a string beginning with a separator (either '__', or
+ '.'), chop this separator and return the result; else, return
+ NAME. */
+
+static const char *
+chop_separator (const char *name)
+{
+ if (*name == '.')
+ return name + 1;
+
+ if (name[0] == '_' && name[1] == '_')
+ return name + 2;
+
+ return name;
+}
+
+/* Given that SELS is a string of the form (<sep><identifier>)*, where
+ <sep> is '__' or '.', write the indicated sequence of
+ STRUCTOP_STRUCT expression operators. Returns a pointer to the
+ last operation that was pushed. */
+static ada_structop_operation *
+write_selectors (struct parser_state *par_state, const char *sels)
+{
+ ada_structop_operation *result = nullptr;
+ while (*sels != '\0')
+ {
+ const char *p = chop_separator (sels);
+ sels = p;
+ while (*sels != '\0' && *sels != '.'
+ && (sels[0] != '_' || sels[1] != '_'))
+ sels += 1;
+ operation_up arg = ada_pop ();
+ result = new ada_structop_operation (std::move (arg),
+ std::string (p, sels - p));
+ pstate->push (operation_up (result));
+ }
+ return result;
+}
+
+/* Write a variable access (OP_VAR_VALUE) to ambiguous encoded name
+ NAME[0..LEN-1], in block context BLOCK, to be resolved later. Writes
+ a temporary symbol that is valid until the next call to ada_parse.
+ */
+static void
+write_ambiguous_var (struct parser_state *par_state,
+ const struct block *block, const char *name, int len)
+{
+ struct symbol *sym = new (&ada_parser->temp_space) symbol ();
+
+ sym->set_domain (UNDEF_DOMAIN);
+ sym->set_linkage_name (obstack_strndup (&ada_parser->temp_space, name, len));
+ sym->set_language (language_ada, nullptr);
+
+ block_symbol bsym { sym, block };
+ par_state->push_new<ada_var_value_operation> (bsym);
+}
+
+/* A convenient wrapper around ada_get_field_index that takes
+ a non NUL-terminated FIELD_NAME0 and a FIELD_NAME_LEN instead
+ of a NUL-terminated field name. */
+
+static int
+ada_nget_field_index (const struct type *type, const char *field_name0,
+ int field_name_len, int maybe_missing)
+{
+ char *field_name = (char *) alloca ((field_name_len + 1) * sizeof (char));
+
+ strncpy (field_name, field_name0, field_name_len);
+ field_name[field_name_len] = '\0';
+ return ada_get_field_index (type, field_name, maybe_missing);
+}
+
+/* If encoded_field_name is the name of a field inside symbol SYM,
+ then return the type of that field. Otherwise, return NULL.
+
+ This function is actually recursive, so if ENCODED_FIELD_NAME
+ doesn't match one of the fields of our symbol, then try to see
+ if ENCODED_FIELD_NAME could not be a succession of field names
+ (in other words, the user entered an expression of the form
+ TYPE_NAME.FIELD1.FIELD2.FIELD3), in which case we evaluate
+ each field name sequentially to obtain the desired field type.
+ In case of failure, we return NULL. */
+
+static struct type *
+get_symbol_field_type (struct symbol *sym, const char *encoded_field_name)
+{
+ const char *field_name = encoded_field_name;
+ const char *subfield_name;
+ struct type *type = sym->type ();
+ int fieldno;
+
+ if (type == NULL || field_name == NULL)
+ return NULL;
+ type = check_typedef (type);
+
+ while (field_name[0] != '\0')
+ {
+ field_name = chop_separator (field_name);
+
+ fieldno = ada_get_field_index (type, field_name, 1);
+ if (fieldno >= 0)
+ return type->field (fieldno).type ();
+
+ subfield_name = field_name;
+ while (*subfield_name != '\0' && *subfield_name != '.'
+ && (subfield_name[0] != '_' || subfield_name[1] != '_'))
+ subfield_name += 1;
+
+ if (subfield_name[0] == '\0')
+ return NULL;
+
+ fieldno = ada_nget_field_index (type, field_name,
+ subfield_name - field_name, 1);
+ if (fieldno < 0)
+ return NULL;
+
+ type = type->field (fieldno).type ();
+ field_name = subfield_name;
+ }
+
+ return NULL;
+}
+
+/* See ada-exp-parser.h. */
+
+struct type*
+write_var_or_type (struct parser_state *par_state,
+ const struct block *block, struct stoken name0)
+{
+ int depth;
+ char *encoded_name;
+ int name_len;
+
+ std::string name_storage = ada_encode (name0.ptr);
+
+ if (block == nullptr)
+ {
+ auto iter = ada_parser->iterated_associations.find (name_storage);
+ if (iter != ada_parser->iterated_associations.end ())
+ {
+ auto op = std::make_unique<ada_index_var_operation> ();
+ iter->second.push_back (op.get ());
+ par_state->push (std::move (op));
+ return nullptr;
+ }
+
+ block = par_state->expression_context_block;
+ }
+
+ name_len = name_storage.size ();
+ encoded_name = obstack_strndup (&ada_parser->temp_space,
+ name_storage.c_str (),
+ name_len);
+ for (depth = 0; depth < MAX_RENAMING_CHAIN_LENGTH; depth += 1)
+ {
+ int tail_index;
+
+ tail_index = name_len;
+ while (tail_index > 0)
+ {
+ struct symbol *type_sym;
+ struct symbol *renaming_sym;
+ const char* renaming;
+ int renaming_len;
+ const char* renaming_expr;
+ int terminator = encoded_name[tail_index];
+
+ encoded_name[tail_index] = '\0';
+ /* In order to avoid double-encoding, we want to only pass
+ the decoded form to lookup functions. */
+ std::string decoded_name = ada_decode (encoded_name);
+ encoded_name[tail_index] = terminator;
+
+ std::vector<struct block_symbol> syms
+ = ada_lookup_symbol_list (decoded_name.c_str (), block,
+ SEARCH_VFT);
+
+ type_sym = select_possible_type_sym (syms);
+
+ if (type_sym != NULL)
+ renaming_sym = type_sym;
+ else if (syms.size () == 1)
+ renaming_sym = syms[0].symbol;
+ else
+ renaming_sym = NULL;
+
+ switch (ada_parse_renaming (renaming_sym, &renaming,
+ &renaming_len, &renaming_expr))
+ {
+ case ADA_NOT_RENAMING:
+ break;
+ case ADA_PACKAGE_RENAMING:
+ case ADA_EXCEPTION_RENAMING:
+ case ADA_SUBPROGRAM_RENAMING:
+ {
+ int alloc_len = renaming_len + name_len - tail_index + 1;
+ char *new_name
+ = (char *) obstack_alloc (&ada_parser->temp_space,
+ alloc_len);
+ strncpy (new_name, renaming, renaming_len);
+ strcpy (new_name + renaming_len, encoded_name + tail_index);
+ encoded_name = new_name;
+ name_len = renaming_len + name_len - tail_index;
+ goto TryAfterRenaming;
+ }
+ case ADA_OBJECT_RENAMING:
+ write_object_renaming (par_state, block, renaming, renaming_len,
+ renaming_expr, MAX_RENAMING_CHAIN_LENGTH);
+ write_selectors (par_state, encoded_name + tail_index);
+ return NULL;
+ default:
+ internal_error (_("impossible value from ada_parse_renaming"));
+ }
+
+ if (type_sym != NULL)
+ {
+ struct type *field_type;
+
+ if (tail_index == name_len)
+ return type_sym->type ();
+
+ /* We have some extraneous characters after the type name.
+ If this is an expression "TYPE_NAME.FIELD0.[...].FIELDN",
+ then try to get the type of FIELDN. */
+ field_type
+ = get_symbol_field_type (type_sym, encoded_name + tail_index);
+ if (field_type != NULL)
+ return field_type;
+ else
+ error (_("Invalid attempt to select from type: \"%s\"."),
+ name0.ptr);
+ }
+ else if (tail_index == name_len && syms.empty ())
+ {
+ struct type *type = find_primitive_type (par_state,
+ encoded_name);
+
+ if (type != NULL)
+ return type;
+ }
+
+ if (syms.size () == 1)
+ {
+ write_var_from_sym (par_state, syms[0]);
+ write_selectors (par_state, encoded_name + tail_index);
+ return NULL;
+ }
+ else if (syms.empty ())
+ {
+ struct objfile *objfile = nullptr;
+ if (block != nullptr)
+ objfile = block->objfile ();
+
+ bound_minimal_symbol msym
+ = ada_lookup_simple_minsym (decoded_name.c_str (), objfile);
+ if (msym.minsym != NULL)
+ {
+ par_state->push_new<ada_var_msym_value_operation> (msym);
+ /* Maybe cause error here rather than later? FIXME? */
+ write_selectors (par_state, encoded_name + tail_index);
+ return NULL;
+ }
+
+ if (tail_index == name_len
+ && strncmp (encoded_name, "standard__",
+ sizeof ("standard__") - 1) == 0)
+ error (_("No definition of \"%s\" found."), name0.ptr);
+
+ tail_index = chop_selector (encoded_name, tail_index);
+ }
+ else
+ {
+ write_ambiguous_var (par_state, block, encoded_name,
+ tail_index);
+ write_selectors (par_state, encoded_name + tail_index);
+ return NULL;
+ }
+ }
+
+ if (!current_program_space->has_full_symbols ()
+ && !current_program_space->has_partial_symbols ()
+ && block == NULL)
+ error (_("No symbol table is loaded. Use the \"%ps\" command."),
+ styled_string (command_style.style (), "file"));
+ if (block == par_state->expression_context_block)
+ error (_("No definition of \"%s\" in current context."), name0.ptr);
+ else
+ error (_("No definition of \"%s\" in specified context."), name0.ptr);
+
+ TryAfterRenaming: ;
+ }
+
+ error (_("Could not find renamed symbol \"%s\""), name0.ptr);
+
+}
+
+/* Because ada_completer_word_break_characters does not contain '.' --
+ and it cannot easily be added, this breaks other completions -- we
+ have to recreate the completion word-splitting here, so that we can
+ provide a prefix that is then used when completing field names.
+ Without this, an attempt like "complete print abc.d" will give a
+ result like "print def" rather than "print abc.def". */
+
+std::string
+ada_parse_state::find_completion_bounds ()
+{
+ const char *end = pstate->lexptr;
+ /* First the end of the prefix. Here we stop at the token start or
+ at '.' or space. */
+ for (; end > m_original_expr && end[-1] != '.' && !c_isspace (end[-1]); --end)
+ {
+ /* Nothing. */
+ }
+ /* Now find the start of the prefix. */
+ const char *ptr = end;
+ /* Here we allow '.'. */
+ for (;
+ ptr > m_original_expr && (ptr[-1] == '.'
+ || ptr[-1] == '_'
+ || (ptr[-1] >= 'a' && ptr[-1] <= 'z')
+ || (ptr[-1] >= 'A' && ptr[-1] <= 'Z')
+ || (ptr[-1] & 0xff) >= 0x80);
+ --ptr)
+ {
+ /* Nothing. */
+ }
+ /* ... except, skip leading spaces. */
+ ptr = skip_spaces (ptr);
+
+ return std::string (ptr, end);
+}
+
+/* See ada-exp-parser.h. */
+
+struct type *
+write_var_or_type_completion (struct parser_state *par_state,
+ const struct block *block, struct stoken name0)
+{
+ int tail_index = chop_selector (name0.ptr, name0.length);
+ /* If there's no separator, just defer to ordinary symbol
+ completion. */
+ if (tail_index == -1)
+ return write_var_or_type (par_state, block, name0);
+
+ std::string copy (name0.ptr, tail_index);
+ struct type *type = write_var_or_type (par_state, block,
+ { copy.c_str (),
+ (int) copy.length () });
+ /* For completion purposes, it's enough that we return a type
+ here. */
+ if (type != nullptr)
+ return type;
+
+ ada_structop_operation *op = write_selectors (par_state,
+ name0.ptr + tail_index);
+ op->set_prefix (ada_parser->find_completion_bounds ());
+ par_state->mark_struct_expression (op);
+ return nullptr;
+}
+
+/* See ada-exp-parser.h. */
+
+void
+write_name_assoc (struct parser_state *par_state, struct stoken name)
+{
+ if (strchr (name.ptr, '.') == NULL)
+ {
+ std::vector<struct block_symbol> syms
+ = ada_lookup_symbol_list (name.ptr,
+ par_state->expression_context_block,
+ SEARCH_VFT);
+
+ if (syms.size () != 1 || syms[0].symbol->loc_class () == LOC_TYPEDEF)
+ pstate->push_new<ada_string_operation> (copy_name (name));
+ else
+ write_var_from_sym (par_state, syms[0]);
+ }
+ else
+ if (write_var_or_type (par_state, NULL, name) != NULL)
+ error (_("Invalid use of type."));
+
+ push_association<ada_name_association> (ada_pop ());
+}
+
+/* See ada-exp-parser.h. */
+
+struct type *
+type_for_char (struct parser_state *par_state, ULONGEST value)
+{
+ if (value <= 0xff)
+ return language_string_char_type (par_state->language (),
+ par_state->gdbarch ());
+ else if (value <= 0xffff)
+ return language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "wide_character");
+ return language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "wide_wide_character");
+}
+
+/* See ada-exp-parser.h. */
+
+struct type *
+type_system_address (struct parser_state *par_state)
+{
+ struct type *type
+ = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ "system__address");
+ return type != NULL ? type : parse_type (par_state)->builtin_data_ptr;
+}
+
+/* See ada-exp-parser.h. */
+
+void
+ada_yyerror (const char *msg)
+{
+ ada_exp_parser::pstate->parse_error (msg);
+}
+
+} /* namespace ada_exp_parser */
+
+/* See ada-exp-parser.h. */
+
+int
+ada_parse (struct parser_state *par_state)
+{
+ using namespace ada_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate, par_state);
+ gdb_assert (par_state != NULL);
+
+ ada_parse_state parser (par_state->lexptr);
+ scoped_restore parser_restore = make_scoped_restore (&ada_parser, &parser);
+
+ scoped_restore restore_yydebug = make_scoped_restore (&ada_yydebug,
+ par_state->debug);
+
+ lexer_init (ada_yyin); /* (Re-)initialize lexer. */
+
+ int result = ada_yyparse ();
+ if (!result)
+ {
+ struct type *context_type = nullptr;
+ if (par_state->void_context_p)
+ context_type = parse_type (par_state)->builtin_void;
+ pstate->set_operation (ada_pop (true, context_type));
+ }
+ return result;
+}
diff --git a/gdb/ada-exp-parser.h b/gdb/ada-exp-parser.h
new file mode 100644
index 000000000000..0f438647437e
--- /dev/null
+++ b/gdb/ada-exp-parser.h
@@ -0,0 +1,427 @@
+/* Support code for the Ada 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_ADA_EXP_PARSER_H
+#define GDB_ADA_EXP_PARSER_H
+
+#include "ada-exp.h"
+#include "parser-defs.h"
+
+/* The character we use to represent the completion point. */
+#define COMPLETE_CHAR '\001'
+
+namespace ada_exp_parser
+{
+
+using ada_assign_up = std::unique_ptr<expr::ada_assign_operation>;
+
+/* Data that must be held for the duration of a parse. */
+
+struct ada_parse_state
+{
+ explicit ada_parse_state (const char *expr)
+ : m_original_expr (expr)
+ {
+ }
+
+ std::string find_completion_bounds ();
+
+ const gdb_mpz *push_integer (gdb_mpz &&val)
+ {
+ auto &result = m_int_storage.emplace_back (new gdb_mpz (std::move (val)));
+ return result.get ();
+ }
+
+ /* The components being constructed during this parse. */
+ std::vector<expr::ada_component_up> components;
+
+ /* The associations being constructed during this parse. */
+ std::vector<expr::ada_association_up> associations;
+
+ /* The stack of currently active assignment expressions. This is used
+ to implement '@', the target name symbol. */
+ std::vector<ada_assign_up> assignments;
+
+ /* Track currently active iterated assignment names. */
+ gdb::unordered_string_map<std::vector<expr::ada_index_var_operation *>>
+ iterated_associations;
+
+ auto_obstack temp_space;
+
+ /* Depth of parentheses, used by the lexer. */
+ int paren_depth = 0;
+
+ /* When completing, we'll return a special character at the end of the
+ input, to signal the completion position to the lexer. This is
+ done because flex does not have a generally useful way to detect
+ EOF in a pattern. This variable records whether the special
+ character has been emitted. */
+ bool returned_complete = false;
+
+private:
+
+ /* We don't have a good way to manage non-POD data in Yacc, so store
+ values here. The storage here is only valid for the duration of
+ the parse. */
+ std::vector<std::unique_ptr<gdb_mpz>> m_int_storage;
+
+ /* The original expression string. */
+ const char *m_original_expr;
+};
+
+/* Expression completer for attributes. */
+struct ada_tick_completer : public expr_completion_base
+{
+ explicit ada_tick_completer (std::string &&name)
+ : m_name (std::move (name))
+ {
+ }
+
+ bool complete (struct expression *exp,
+ completion_tracker &tracker) override;
+
+private:
+
+ std::string m_name;
+};
+
+/* The current state of the parser, used internally when parsing an
+ expression. */
+
+extern struct parser_state *pstate;
+
+/* The current Ada parser object. */
+
+extern struct ada_parse_state *ada_parser;
+
+/* Initialize the lexer for processing new expression.
+
+ This function is implemented in ada-lex.l, because it needs to see some
+ macros in ada-lex-gen.c. */
+
+void lexer_init (FILE *inp);
+
+/* Copy S2 to S1, removing all underscores, and downcasing all letters. */
+
+void canonicalizeNumeral (char *s1, const char *s2);
+
+/* Return TEXT[0..LEN-1], a string literal without surrounding quotes,
+ with special hex character notations replaced with characters.
+ Result valid until the next call to ada_parse. */
+
+stoken processString (const char *text, int len);
+
+/* Interprets the prefix of NUM that consists of digits of the given BASE
+ as an integer of that BASE, with the string EXP as an exponent.
+ Puts value in yylval, and returns INT, if the string is valid. Causes
+ an error if the number is improperly formatted. BASE, if NULL, defaults
+ to "10", and EXP to "1". The EXP does not contain a leading 'e' or 'E'.
+ */
+
+int processInt (parser_state *par_state, const char *base0, const char *num0,
+ const char *exp0);
+
+/* Parse NUM0 as a floating-point literal, store the result in yylval,
+ and return the FLOAT token. */
+
+int processReal (struct parser_state *par_state, const char *num0);
+
+/* Store a canonicalized version of NAME0[0..LEN-1] in yylval.ssym. The
+ resulting string is valid until the next call to ada_parse. If
+ NAME0 contains the substring "___", it is assumed to be already
+ encoded and the resulting name is equal to it. Similarly, if the name
+ starts with '<', it is copied verbatim. Otherwise, it differs
+ from NAME0 in that:
+ + Characters between '...' are transferred verbatim to yylval.ssym.
+ + Trailing "'" characters in quoted sequences are removed (a leading quote is
+ preserved to indicate that the name is not to be GNAT-encoded).
+ + Unquoted whitespace is removed.
+ + Unquoted alphabetic characters are mapped to lower case.
+ Result is returned as a struct stoken, but for convenience, the string
+ is also null-terminated. Result string valid until the next call of
+ ada_parse.
+ */
+
+stoken processId (const char *name0, int len);
+
+/* Return the syntactic code corresponding to the attribute name or
+ abbreviation STR. */
+
+int processAttribute (const char *str);
+
+/* Returns the position within STR of the '.' in a
+ '.{WHITE}*all' component of a dotted name, or -1 if there is none.
+ Note: we actually don't need this routine, since 'all' can never be an
+ Ada identifier. Thus, looking up foo.all or foo.all.x as a name
+ must fail, and will eventually be interpreted as (foo).all or
+ (foo).all.x. However, this does avoid an extraneous lookup. */
+
+int find_dot_all (const char *str);
+
+/* Back up lexptr by yyleng and then to the rightmost occurrence of
+ character CH, case-folded (there must be one). WARNING: since
+ lexptr points to the next input character that Flex has not yet
+ transferred to its internal buffer, the use of this function
+ depends on the assumption that Flex calls YY_INPUT only when it is
+ logically necessary to do so (thus, there is no reading ahead
+ farther than needed to identify the next token.) */
+
+void rewind_to_char (int ch);
+
+/* Like parser_state::pop, but handles Ada type resolution.
+ DEPROCEDURE_P and CONTEXT_TYPE are passed to the resolve method, if
+ called. */
+
+expr::operation_up ada_pop (bool deprocedure_p = true,
+ struct type *context_type = nullptr);
+
+/* Handle operator overloading. Either returns a function all
+ operation wrapping the arguments, or it returns null, leaving the
+ caller to construct the appropriate operation. If RHS is null, a
+ unary operator is assumed. */
+
+expr::operation_up maybe_overload (enum exp_opcode op, expr::operation_up &lhs,
+ expr::operation_up &rhs);
+
+/* Handle Ada type resolution for OP. DEPROCEDURE_P and CONTEXT_TYPE
+ are passed to the resolve method, if called. */
+
+expr::operation_up resolve (expr::operation_up &&op, bool deprocedure_p,
+ struct type *context_type);
+
+/* Pop NARGS operands, then a callee operand, and use these to
+ construct and push a new Ada function call operation. */
+
+void ada_funcall (int nargs);
+
+/* Pop the most recent component from the global stack, and return
+ it. */
+
+expr::ada_component_up pop_component ();
+
+/* Create and push an address-of operation, as appropriate for Ada.
+ If TYPE is not NULL, the resulting operation will be wrapped in a
+ cast to TYPE. */
+
+void ada_addrof (type *type = nullptr);
+
+/* Make a new ada_tick_completer and wrap it in a unique pointer. */
+
+std::unique_ptr<expr_completion_base> make_tick_completer (struct stoken tok);
+
+/* Pop the N most recent components from the global stack, and return
+ them in a vector. */
+
+std::vector<expr::ada_component_up> pop_components (int n);
+
+/* Examine the final element of the 'components' vector, and return it
+ as a pointer to an ada_choices_component. The caller is
+ responsible for ensuring that the final element is in fact an
+ ada_choices_component. */
+
+expr::ada_choices_component *choice_component ();
+
+/* Pop the N most recent associations from the global stack, and
+ return them in a vector. */
+
+std::vector<expr::ada_association_up> pop_associations (int n);
+
+/* Return the type of System.Address for PAR_STATE, or the builtin data
+ pointer type if that type is not defined. */
+
+type *type_system_address (parser_state *par_state);
+
+/* Write integer or boolean constant ARG of type TYPE. */
+
+void write_int (parser_state *par_state, LONGEST arg, type *type);
+
+/* Look up NAME0 (an unencoded identifier or dotted name) in BLOCK (or
+ expression_block_context if NULL). If it denotes a type, return
+ that type. Otherwise, write expression code to evaluate it as an
+ object and return NULL. In this second case, NAME0 will, in general,
+ have the form <name>(.<selector_name>)*, where <name> is an object
+ or renaming encoded in the debugging data. Calls error if no
+ prefix <name> matches a name in the debugging data (i.e., matches
+ either a complete name or, as a wild-card match, the final
+ identifier). */
+
+type *write_var_or_type (parser_state *par_state,
+ const block *block, stoken name0);
+
+/* A wrapper for write_var_or_type that is used specifically when
+ completion is requested for the last of a sequence of
+ identifiers. */
+
+type *write_var_or_type_completion (struct parser_state *par_state,
+ const block *block,
+ struct stoken name0);
+
+/* Look up the block for the function or file named RAW_NAME, in the
+ context of CONTEXT (or the global context if NULL). Calls error if
+ no matching block is found. */
+
+const block *block_lookup (const block *context, const char *raw_name);
+
+/* Write a left side of a component association (e.g., NAME in NAME =>
+ exp). If NAME has the form of a selected component, write it as an
+ ordinary expression. If it is a simple variable that unambiguously
+ corresponds to exactly one symbol that does not denote a type or an
+ object renaming, also write it normally as an OP_VAR_VALUE.
+ Otherwise, write it as an OP_NAME.
+
+ Unfortunately, we don't know at this point whether NAME is supposed
+ to denote a record component name or the value of an array index.
+ Therefore, it is not appropriate to disambiguate an ambiguous name
+ as we normally would, nor to replace a renaming with its referent.
+ As a result, in the (one hopes) rare case that one writes an
+ aggregate such as (R => 42) where R renames an object or is an
+ ambiguous name, one must write instead ((R) => 42). */
+
+void write_name_assoc (parser_state *par_state, stoken name);
+
+/* Return the character type appropriate for the character constant
+ VALUE: a normal, wide, or wide-wide character type depending on the
+ magnitude of VALUE. */
+
+type *type_for_char (parser_state *par_state, ULONGEST value);
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void ada_yyerror (const char *msg);
+
+/* Like parser_state::wrap, but use ada_pop to pop the value. */
+
+template<typename T, typename... Args>
+void
+ada_wrap (Args... args)
+{
+ expr::operation_up arg = ada_pop ();
+ pstate->push_new<T> (std::move (arg), std::forward<Args> (args)...);
+}
+
+/* Like parser_state::wrap, but use ada_pop to pop the value, and
+ handle unary overloading. */
+
+template<typename T>
+void
+ada_wrap_overload (enum exp_opcode op)
+{
+ expr::operation_up arg = ada_pop ();
+ expr::operation_up empty;
+
+ expr::operation_up call = maybe_overload (op, arg, empty);
+ if (call == nullptr)
+ call = expr::make_operation<T> (std::move (arg));
+ pstate->push (std::move (call));
+}
+
+/* A variant of parser_state::wrap2 that uses ada_pop to pop both
+ operands, and then pushes a new Ada-wrapped operation of the
+ template type T. */
+
+template<typename T>
+void
+ada_un_wrap2 (enum exp_opcode op)
+{
+ expr::operation_up rhs = ada_pop ();
+ expr::operation_up lhs = ada_pop ();
+
+ expr::operation_up wrapped = maybe_overload (op, lhs, rhs);
+ if (wrapped == nullptr)
+ {
+ wrapped = expr::make_operation<T> (std::move (lhs), std::move (rhs));
+ wrapped = expr::make_operation<expr::ada_wrapped_operation> (
+ std::move (wrapped));
+ }
+ pstate->push (std::move (wrapped));
+}
+
+/* A variant of parser_state::wrap2 that uses ada_pop to pop both
+ operands. Unlike ada_un_wrap2, ada_wrapped_operation is not
+ used. */
+
+template<typename T>
+void
+ada_wrap2 (enum exp_opcode op)
+{
+ expr::operation_up rhs = ada_pop ();
+ expr::operation_up lhs = ada_pop ();
+ expr::operation_up call = maybe_overload (op, lhs, rhs);
+ if (call == nullptr)
+ call = expr::make_operation<T> (std::move (lhs), std::move (rhs));
+ pstate->push (std::move (call));
+}
+
+/* A variant of parser_state::wrap2 that uses ada_pop to pop both
+ operands. OP is also passed to the constructor of the new binary
+ operation. */
+
+template<typename T>
+void
+ada_wrap_op (enum exp_opcode op)
+{
+ expr::operation_up rhs = ada_pop ();
+ expr::operation_up lhs = ada_pop ();
+ expr::operation_up call = maybe_overload (op, lhs, rhs);
+ if (call == nullptr)
+ call = expr::make_operation<T> (op, std::move (lhs), std::move (rhs));
+ pstate->push (std::move (call));
+}
+
+/* Pop three operands using ada_pop, then construct a new ternary
+ operation of type T and push it. */
+
+template<typename T>
+void
+ada_wrap3 ()
+{
+ expr::operation_up rhs = ada_pop ();
+ expr::operation_up mid = ada_pop ();
+ expr::operation_up lhs = ada_pop ();
+ pstate->push_new<T> (std::move (lhs), std::move (mid), std::move (rhs));
+}
+
+/* Create a new ada_component_up of the indicated type and arguments,
+ and push it on the global 'components' vector. */
+
+template<typename T, typename... Arg>
+void
+push_component (Arg... args)
+{
+ ada_parser->components.emplace_back (new T (std::forward<Arg> (args)...));
+}
+
+/* Create a new ada_association_up of the indicated type and
+ arguments, and push it on the global 'associations' vector. */
+
+template<typename T, typename... Arg>
+void
+push_association (Arg... args)
+{
+ ada_parser->associations.emplace_back (new T (std::forward<Arg> (args)...));
+}
+
+} /* namespace ada_exp_parser */
+
+/* The Ada expression parser entry point. */
+
+int ada_parse (struct parser_state *par_state);
+
+#endif /* GDB_ADA_EXP_PARSER_H */
diff --git a/gdb/ada-exp-parser.y b/gdb/ada-exp-parser.y
index 433293d22ad9..0a629f82cc86 100644
--- a/gdb/ada-exp-parser.y
+++ b/gdb/ada-exp-parser.y
@@ -40,412 +40,19 @@
#include "value.h"
#include "parser-defs.h"
#include "language.h"
+#include "ada-exp-parser.h"
#include "ada-lang.h"
#include "frame.h"
#include "block.h"
#include "ada-exp.h"
+#include "ada-exp-parser.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;
-
using namespace expr;
-
-/* A convenience typedef. */
-typedef std::unique_ptr<ada_assign_operation> ada_assign_up;
-
-/* Data that must be held for the duration of a parse. */
-
-struct ada_parse_state
-{
- explicit ada_parse_state (const char *expr)
- : m_original_expr (expr)
- {
- }
-
- std::string find_completion_bounds ();
-
- const gdb_mpz *push_integer (gdb_mpz &&val)
- {
- auto &result = m_int_storage.emplace_back (new gdb_mpz (std::move (val)));
- return result.get ();
- }
-
- /* The components being constructed during this parse. */
- std::vector<ada_component_up> components;
-
- /* The associations being constructed during this parse. */
- std::vector<ada_association_up> associations;
-
- /* The stack of currently active assignment expressions. This is used
- to implement '@', the target name symbol. */
- std::vector<ada_assign_up> assignments;
-
- /* Track currently active iterated assignment names. */
- gdb::unordered_string_map<std::vector<ada_index_var_operation *>>
- iterated_associations;
-
- auto_obstack temp_space;
-
- /* Depth of parentheses, used by the lexer. */
- int paren_depth = 0;
-
- /* When completing, we'll return a special character at the end of the
- input, to signal the completion position to the lexer. This is
- done because flex does not have a generally useful way to detect
- EOF in a pattern. This variable records whether the special
- character has been emitted. */
- bool returned_complete = false;
-
-private:
-
- /* We don't have a good way to manage non-POD data in Yacc, so store
- values here. The storage here is only valid for the duration of
- the parse. */
- std::vector<std::unique_ptr<gdb_mpz>> m_int_storage;
-
- /* The original expression string. */
- const char *m_original_expr;
-};
-
-/* The current Ada parser object. */
-
-static ada_parse_state *ada_parser;
-
-int yyparse (void);
+using namespace ada_exp_parser;
static int yylex (void);
-static void yyerror (const char *);
-
-static void write_int (struct parser_state *, LONGEST, struct type *);
-
-static void write_object_renaming (struct parser_state *,
- const struct block *, const char *, int,
- const char *, int);
-
-static struct type* write_var_or_type (struct parser_state *,
- const struct block *, struct stoken);
-static struct type *write_var_or_type_completion (struct parser_state *,
- const struct block *,
- struct stoken);
-
-static void write_name_assoc (struct parser_state *, struct stoken);
-
-static const struct block *block_lookup (const struct block *, const char *);
-
-static void write_ambiguous_var (struct parser_state *,
- const struct block *, const char *, int);
-
-static struct type *type_for_char (struct parser_state *, ULONGEST);
-
-static struct type *type_system_address (struct parser_state *);
-
-/* Handle Ada type resolution for OP. DEPROCEDURE_P and CONTEXT_TYPE
- are passed to the resolve method, if called. */
-static operation_up
-resolve (operation_up &&op, bool deprocedure_p, struct type *context_type)
-{
- operation_up result = std::move (op);
- ada_resolvable *res = dynamic_cast<ada_resolvable *> (result.get ());
- if (res != nullptr)
- return res->replace (std::move (result),
- pstate->expout.get (),
- deprocedure_p,
- pstate->parse_completion,
- pstate->block_tracker,
- context_type);
- return result;
-}
-
-/* Like parser_state::pop, but handles Ada type resolution.
- DEPROCEDURE_P and CONTEXT_TYPE are passed to the resolve method, if
- called. */
-static operation_up
-ada_pop (bool deprocedure_p = true, struct type *context_type = nullptr)
-{
- /* Of course it's ok to call parser_state::pop here... */
- return resolve (pstate->pop (), deprocedure_p, context_type);
-}
-
-/* Like parser_state::wrap, but use ada_pop to pop the value. */
-template<typename T, typename... Args>
-void
-ada_wrap (Args... args)
-{
- operation_up arg = ada_pop ();
- pstate->push_new<T> (std::move (arg), std::forward<Args> (args)...);
-}
-
-/* Create and push an address-of operation, as appropriate for Ada.
- If TYPE is not NULL, the resulting operation will be wrapped in a
- cast to TYPE. */
-static void
-ada_addrof (struct type *type = nullptr)
-{
- operation_up arg = ada_pop (false);
- operation_up addr = make_operation<unop_addr_operation> (std::move (arg));
- operation_up wrapped
- = make_operation<ada_wrapped_operation> (std::move (addr));
- if (type != nullptr)
- wrapped = make_operation<unop_cast_operation> (std::move (wrapped), type);
- pstate->push (std::move (wrapped));
-}
-
-/* Handle operator overloading. Either returns a function all
- operation wrapping the arguments, or it returns null, leaving the
- caller to construct the appropriate operation. If RHS is null, a
- unary operator is assumed. */
-static operation_up
-maybe_overload (enum exp_opcode op, operation_up &lhs, operation_up &rhs)
-{
- struct value *args[2];
-
- int nargs = 1;
- args[0] = lhs->evaluate (nullptr, pstate->expout.get (),
- EVAL_AVOID_SIDE_EFFECTS);
- if (rhs == nullptr)
- args[1] = nullptr;
- else
- {
- args[1] = rhs->evaluate (nullptr, pstate->expout.get (),
- EVAL_AVOID_SIDE_EFFECTS);
- ++nargs;
- }
-
- block_symbol fn = ada_find_operator_symbol (op, pstate->parse_completion,
- nargs, args);
- if (fn.symbol == nullptr)
- return {};
-
- if (symbol_read_needs_frame (fn.symbol))
- pstate->block_tracker->update (fn.block, INNERMOST_BLOCK_FOR_SYMBOLS);
- operation_up callee = make_operation<ada_var_value_operation> (fn);
-
- std::vector<operation_up> argvec;
- argvec.push_back (std::move (lhs));
- if (rhs != nullptr)
- argvec.push_back (std::move (rhs));
- return make_operation<ada_funcall_operation> (std::move (callee),
- std::move (argvec));
-}
-
-/* Like parser_state::wrap, but use ada_pop to pop the value, and
- handle unary overloading. */
-template<typename T>
-void
-ada_wrap_overload (enum exp_opcode op)
-{
- operation_up arg = ada_pop ();
- operation_up empty;
-
- operation_up call = maybe_overload (op, arg, empty);
- if (call == nullptr)
- call = make_operation<T> (std::move (arg));
- pstate->push (std::move (call));
-}
-
-/* A variant of parser_state::wrap2 that uses ada_pop to pop both
- operands, and then pushes a new Ada-wrapped operation of the
- template type T. */
-template<typename T>
-void
-ada_un_wrap2 (enum exp_opcode op)
-{
- operation_up rhs = ada_pop ();
- operation_up lhs = ada_pop ();
-
- operation_up wrapped = maybe_overload (op, lhs, rhs);
- if (wrapped == nullptr)
- {
- wrapped = make_operation<T> (std::move (lhs), std::move (rhs));
- wrapped = make_operation<ada_wrapped_operation> (std::move (wrapped));
- }
- pstate->push (std::move (wrapped));
-}
-
-/* A variant of parser_state::wrap2 that uses ada_pop to pop both
- operands. Unlike ada_un_wrap2, ada_wrapped_operation is not
- used. */
-template<typename T>
-void
-ada_wrap2 (enum exp_opcode op)
-{
- operation_up rhs = ada_pop ();
- operation_up lhs = ada_pop ();
- operation_up call = maybe_overload (op, lhs, rhs);
- if (call == nullptr)
- call = make_operation<T> (std::move (lhs), std::move (rhs));
- pstate->push (std::move (call));
-}
-
-/* A variant of parser_state::wrap2 that uses ada_pop to pop both
- operands. OP is also passed to the constructor of the new binary
- operation. */
-template<typename T>
-void
-ada_wrap_op (enum exp_opcode op)
-{
- operation_up rhs = ada_pop ();
- operation_up lhs = ada_pop ();
- operation_up call = maybe_overload (op, lhs, rhs);
- if (call == nullptr)
- call = make_operation<T> (op, std::move (lhs), std::move (rhs));
- pstate->push (std::move (call));
-}
-
-/* Pop three operands using ada_pop, then construct a new ternary
- operation of type T and push it. */
-template<typename T>
-void
-ada_wrap3 ()
-{
- operation_up rhs = ada_pop ();
- operation_up mid = ada_pop ();
- operation_up lhs = ada_pop ();
- pstate->push_new<T> (std::move (lhs), std::move (mid), std::move (rhs));
-}
-
-/* Pop NARGS operands, then a callee operand, and use these to
- construct and push a new Ada function call operation. */
-static void
-ada_funcall (int nargs)
-{
- /* We use the ordinary pop here, because we're going to do
- resolution in a separate step, in order to handle array
- indices. */
- std::vector<operation_up> args = pstate->pop_vector (nargs);
- /* Call parser_state::pop here, because we don't want to
- function-convert the callee slot of a call we're already
- constructing. */
- operation_up callee = pstate->pop ();
-
- ada_var_value_operation *vvo
- = dynamic_cast<ada_var_value_operation *> (callee.get ());
- int array_arity = 0;
- struct type *callee_t = nullptr;
- if (vvo == nullptr
- || vvo->get_symbol ()->domain () != UNDEF_DOMAIN)
- {
- struct value *callee_v = callee->evaluate (nullptr,
- pstate->expout.get (),
- EVAL_AVOID_SIDE_EFFECTS);
- callee_t = ada_check_typedef (callee_v->type ());
- array_arity = ada_array_arity (callee_t);
- }
-
- for (int i = 0; i < nargs; ++i)
- {
- struct type *subtype = nullptr;
- if (i < array_arity)
- subtype = ada_index_type (callee_t, i + 1, "array type");
- args[i] = resolve (std::move (args[i]), true, subtype);
- }
-
- std::unique_ptr<ada_funcall_operation> funcall
- (new ada_funcall_operation (std::move (callee), std::move (args)));
- funcall->resolve (pstate->expout.get (), true, pstate->parse_completion,
- pstate->block_tracker, nullptr);
- pstate->push (std::move (funcall));
-}
-
-/* Create a new ada_component_up of the indicated type and arguments,
- and push it on the global 'components' vector. */
-template<typename T, typename... Arg>
-void
-push_component (Arg... args)
-{
- ada_parser->components.emplace_back (new T (std::forward<Arg> (args)...));
-}
-
-/* Examine the final element of the 'components' vector, and return it
- as a pointer to an ada_choices_component. The caller is
- responsible for ensuring that the final element is in fact an
- ada_choices_component. */
-static ada_choices_component *
-choice_component ()
-{
- ada_component *last = ada_parser->components.back ().get ();
- return gdb::checked_static_cast<ada_choices_component *> (last);
-}
-
-/* Pop the most recent component from the global stack, and return
- it. */
-static ada_component_up
-pop_component ()
-{
- ada_component_up result = std::move (ada_parser->components.back ());
- ada_parser->components.pop_back ();
- return result;
-}
-
-/* Pop the N most recent components from the global stack, and return
- them in a vector. */
-static std::vector<ada_component_up>
-pop_components (int n)
-{
- std::vector<ada_component_up> result (n);
- for (int i = 1; i <= n; ++i)
- result[n - i] = pop_component ();
- return result;
-}
-
-/* Create a new ada_association_up of the indicated type and
- arguments, and push it on the global 'associations' vector. */
-template<typename T, typename... Arg>
-void
-push_association (Arg... args)
-{
- ada_parser->associations.emplace_back (new T (std::forward<Arg> (args)...));
-}
-
-/* Pop the most recent association from the global stack, and return
- it. */
-static ada_association_up
-pop_association ()
-{
- ada_association_up result = std::move (ada_parser->associations.back ());
- ada_parser->associations.pop_back ();
- return result;
-}
-
-/* Pop the N most recent associations from the global stack, and
- return them in a vector. */
-static std::vector<ada_association_up>
-pop_associations (int n)
-{
- std::vector<ada_association_up> result (n);
- for (int i = 1; i <= n; ++i)
- result[n - i] = pop_association ();
- return result;
-}
-
-/* Expression completer for attributes. */
-struct ada_tick_completer : public expr_completion_base
-{
- explicit ada_tick_completer (std::string &&name)
- : m_name (std::move (name))
- {
- }
-
- bool complete (struct expression *exp,
- completion_tracker &tracker) override;
-
-private:
-
- std::string m_name;
-};
-
-/* Make a new ada_tick_completer and wrap it in a unique pointer. */
-static std::unique_ptr<expr_completion_base>
-make_tick_completer (struct stoken tok)
-{
- return (std::unique_ptr<expr_completion_base>
- (new ada_tick_completer (std::string (tok.ptr, tok.length))));
-}
-
%}
%union
@@ -1236,756 +843,3 @@ primary : '*' primary %prec '.'
/* defs.h and non-standard stdlib.h files. */
#define qsort __qsort__dummy
#include "ada-lex-gen.c"
-
-int
-ada_parse (struct parser_state *par_state)
-{
- /* Setting up the parser state. */
- scoped_restore pstate_restore = make_scoped_restore (&pstate, par_state);
- gdb_assert (par_state != NULL);
-
- ada_parse_state parser (par_state->lexptr);
- scoped_restore parser_restore = make_scoped_restore (&ada_parser, &parser);
-
- scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
- par_state->debug);
-
- lexer_init (yyin); /* (Re-)initialize lexer. */
-
- int result = yyparse ();
- if (!result)
- {
- struct type *context_type = nullptr;
- if (par_state->void_context_p)
- context_type = parse_type (par_state)->builtin_void;
- pstate->set_operation (ada_pop (true, context_type));
- }
- return result;
-}
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
-
-/* Emit expression to access an instance of SYM, in block BLOCK (if
- non-NULL). */
-
-static void
-write_var_from_sym (struct parser_state *par_state, block_symbol sym)
-{
- if (symbol_read_needs_frame (sym.symbol))
- par_state->block_tracker->update (sym.block, INNERMOST_BLOCK_FOR_SYMBOLS);
-
- par_state->push_new<ada_var_value_operation> (sym);
-}
-
-/* Write integer or boolean constant ARG of type TYPE. */
-
-static void
-write_int (struct parser_state *par_state, LONGEST arg, struct type *type)
-{
- pstate->push_new<long_const_operation> (type, arg);
- ada_wrap<ada_wrapped_operation> ();
-}
-
-/* Emit expression corresponding to the renamed object named
- designated by RENAMED_ENTITY[0 .. RENAMED_ENTITY_LEN-1] in the
- context of ORIG_LEFT_CONTEXT, to which is applied the operations
- encoded by RENAMING_EXPR. MAX_DEPTH is the maximum number of
- cascaded renamings to allow. If ORIG_LEFT_CONTEXT is null, it
- defaults to the currently selected block. ORIG_SYMBOL is the
- symbol that originally encoded the renaming. It is needed only
- because its prefix also qualifies any index variables used to index
- or slice an array. It should not be necessary once we go to the
- new encoding entirely (FIXME pnh 7/20/2007). */
-
-static void
-write_object_renaming (struct parser_state *par_state,
- const struct block *orig_left_context,
- const char *renamed_entity, int renamed_entity_len,
- const char *renaming_expr, int max_depth)
-{
- char *name;
- enum { SIMPLE_INDEX, LOWER_BOUND, UPPER_BOUND } slice_state;
-
- if (max_depth <= 0)
- error (_("Could not find renamed symbol"));
-
- if (orig_left_context == NULL)
- orig_left_context = get_selected_block ();
-
- name = obstack_strndup (&ada_parser->temp_space, renamed_entity,
- renamed_entity_len);
- block_symbol sym_info = ada_lookup_encoded_symbol (name, orig_left_context,
- SEARCH_VFT);
- if (sym_info.symbol == NULL)
- error (_("Could not find renamed variable: %ps"),
- styled_string (variable_name_style.style (),
- ada_decode (name).c_str ()));
- else if (sym_info.symbol->loc_class () == LOC_TYPEDEF)
- /* We have a renaming of an old-style renaming symbol. Don't
- trust the block information. */
- sym_info.block = orig_left_context;
-
- {
- const char *inner_renamed_entity;
- int inner_renamed_entity_len;
- const char *inner_renaming_expr;
-
- switch (ada_parse_renaming (sym_info.symbol, &inner_renamed_entity,
- &inner_renamed_entity_len,
- &inner_renaming_expr))
- {
- case ADA_NOT_RENAMING:
- write_var_from_sym (par_state, sym_info);
- break;
- case ADA_OBJECT_RENAMING:
- write_object_renaming (par_state, sym_info.block,
- inner_renamed_entity, inner_renamed_entity_len,
- inner_renaming_expr, max_depth - 1);
- break;
- default:
- goto BadEncoding;
- }
- }
-
- slice_state = SIMPLE_INDEX;
- while (*renaming_expr == 'X')
- {
- renaming_expr += 1;
-
- switch (*renaming_expr) {
- case 'A':
- renaming_expr += 1;
- ada_wrap<ada_unop_ind_operation> ();
- break;
- case 'L':
- slice_state = LOWER_BOUND;
- [[fallthrough]];
- case 'S':
- renaming_expr += 1;
- if (c_isdigit (*renaming_expr))
- {
- char *next;
- long val = strtol (renaming_expr, &next, 10);
- if (next == renaming_expr)
- goto BadEncoding;
- renaming_expr = next;
- write_int (par_state, val, parse_type (par_state)->builtin_int);
- }
- else
- {
- const char *end;
- char *index_name;
-
- end = strchr (renaming_expr, 'X');
- if (end == NULL)
- end = renaming_expr + strlen (renaming_expr);
-
- index_name = obstack_strndup (&ada_parser->temp_space,
- renaming_expr,
- end - renaming_expr);
- renaming_expr = end;
-
- block_symbol index_sym_info
- = ada_lookup_encoded_symbol (index_name, orig_left_context,
- SEARCH_VFT);
- if (index_sym_info.symbol == NULL)
- error (_("Could not find %s"), index_name);
- else if (index_sym_info.symbol->loc_class () == LOC_TYPEDEF)
- /* Index is an old-style renaming symbol. */
- index_sym_info.block = orig_left_context;
- write_var_from_sym (par_state, index_sym_info);
- }
- if (slice_state == SIMPLE_INDEX)
- ada_funcall (1);
- else if (slice_state == LOWER_BOUND)
- slice_state = UPPER_BOUND;
- else if (slice_state == UPPER_BOUND)
- {
- ada_wrap3<ada_ternop_slice_operation> ();
- slice_state = SIMPLE_INDEX;
- }
- break;
-
- case 'R':
- {
- const char *end;
-
- renaming_expr += 1;
-
- if (slice_state != SIMPLE_INDEX)
- goto BadEncoding;
- end = strchr (renaming_expr, 'X');
- if (end == NULL)
- end = renaming_expr + strlen (renaming_expr);
-
- operation_up arg = ada_pop ();
- pstate->push_new<ada_structop_operation>
- (std::move (arg), std::string (renaming_expr,
- end - renaming_expr));
- renaming_expr = end;
- break;
- }
-
- default:
- goto BadEncoding;
- }
- }
- if (slice_state == SIMPLE_INDEX)
- return;
-
- BadEncoding:
- error (_("Internal error in encoding of renaming declaration"));
-}
-
-static const struct block*
-block_lookup (const struct block *context, const char *raw_name)
-{
- const char *name;
- struct symtab *symtab;
- const struct block *result = NULL;
-
- std::string name_storage;
- if (raw_name[0] == '\'')
- {
- raw_name += 1;
- name = raw_name;
- }
- else
- {
- name_storage = ada_encode (raw_name);
- name = name_storage.c_str ();
- }
-
- std::vector<struct block_symbol> syms
- = ada_lookup_symbol_list (name, context, SEARCH_FUNCTION_DOMAIN);
-
- if (context == NULL
- && (syms.empty () || syms[0].symbol->loc_class () != LOC_BLOCK))
- symtab = lookup_symtab (current_program_space, name);
- else
- symtab = NULL;
-
- if (symtab != NULL)
- result = symtab->compunit ().blockvector ()->static_block ();
- else if (syms.empty () || syms[0].symbol->loc_class () != LOC_BLOCK)
- {
- if (context == NULL)
- error (_("No file or function \"%s\"."), raw_name);
- else
- error (_("No function \"%s\" in specified context."), raw_name);
- }
- else
- {
- if (syms.size () > 1)
- warning (_("Function name \"%s\" ambiguous here"), raw_name);
- result = syms[0].symbol->value_block ();
- }
-
- return result;
-}
-
-static struct symbol*
-select_possible_type_sym (const std::vector<struct block_symbol> &syms)
-{
- int i;
- int preferred_index;
- struct type *preferred_type;
-
- preferred_index = -1; preferred_type = NULL;
- for (i = 0; i < syms.size (); i += 1)
- switch (syms[i].symbol->loc_class ())
- {
- case LOC_TYPEDEF:
- if (ada_prefer_type (syms[i].symbol->type (), preferred_type))
- {
- preferred_index = i;
- preferred_type = syms[i].symbol->type ();
- }
- break;
- case LOC_REGISTER:
- case LOC_ARG:
- case LOC_REF_ARG:
- case LOC_REGPARM_ADDR:
- case LOC_LOCAL:
- case LOC_COMPUTED:
- return NULL;
- default:
- break;
- }
- if (preferred_type == NULL)
- return NULL;
- return syms[preferred_index].symbol;
-}
-
-static struct type*
-find_primitive_type (struct parser_state *par_state, const char *name)
-{
- struct type *type;
- type = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- name);
- if (type == NULL && streq ("system__address", name))
- type = type_system_address (par_state);
-
- if (type != NULL)
- {
- /* Check to see if we have a regular definition of this
- type that just didn't happen to have been read yet. */
- struct symbol *sym;
- char *expanded_name =
- (char *) alloca (strlen (name) + sizeof ("standard__"));
- strcpy (expanded_name, "standard__");
- strcat (expanded_name, name);
- sym = ada_lookup_symbol (expanded_name, NULL, SEARCH_TYPE_DOMAIN).symbol;
- if (sym != NULL && sym->loc_class () == LOC_TYPEDEF)
- type = sym->type ();
- }
-
- return type;
-}
-
-static int
-chop_selector (const char *name, int end)
-{
- int i;
- for (i = end - 1; i > 0; i -= 1)
- if (name[i] == '.' || (name[i] == '_' && name[i+1] == '_'))
- return i;
- return -1;
-}
-
-/* If NAME is a string beginning with a separator (either '__', or
- '.'), chop this separator and return the result; else, return
- NAME. */
-
-static const char *
-chop_separator (const char *name)
-{
- if (*name == '.')
- return name + 1;
-
- if (name[0] == '_' && name[1] == '_')
- return name + 2;
-
- return name;
-}
-
-/* Given that SELS is a string of the form (<sep><identifier>)*, where
- <sep> is '__' or '.', write the indicated sequence of
- STRUCTOP_STRUCT expression operators. Returns a pointer to the
- last operation that was pushed. */
-static ada_structop_operation *
-write_selectors (struct parser_state *par_state, const char *sels)
-{
- ada_structop_operation *result = nullptr;
- while (*sels != '\0')
- {
- const char *p = chop_separator (sels);
- sels = p;
- while (*sels != '\0' && *sels != '.'
- && (sels[0] != '_' || sels[1] != '_'))
- sels += 1;
- operation_up arg = ada_pop ();
- result = new ada_structop_operation (std::move (arg),
- std::string (p, sels - p));
- pstate->push (operation_up (result));
- }
- return result;
-}
-
-/* Write a variable access (OP_VAR_VALUE) to ambiguous encoded name
- NAME[0..LEN-1], in block context BLOCK, to be resolved later. Writes
- a temporary symbol that is valid until the next call to ada_parse.
- */
-static void
-write_ambiguous_var (struct parser_state *par_state,
- const struct block *block, const char *name, int len)
-{
- struct symbol *sym = new (&ada_parser->temp_space) symbol ();
-
- sym->set_domain (UNDEF_DOMAIN);
- sym->set_linkage_name (obstack_strndup (&ada_parser->temp_space, name, len));
- sym->set_language (language_ada, nullptr);
-
- block_symbol bsym { sym, block };
- par_state->push_new<ada_var_value_operation> (bsym);
-}
-
-/* A convenient wrapper around ada_get_field_index that takes
- a non NUL-terminated FIELD_NAME0 and a FIELD_NAME_LEN instead
- of a NUL-terminated field name. */
-
-static int
-ada_nget_field_index (const struct type *type, const char *field_name0,
- int field_name_len, int maybe_missing)
-{
- char *field_name = (char *) alloca ((field_name_len + 1) * sizeof (char));
-
- strncpy (field_name, field_name0, field_name_len);
- field_name[field_name_len] = '\0';
- return ada_get_field_index (type, field_name, maybe_missing);
-}
-
-/* If encoded_field_name is the name of a field inside symbol SYM,
- then return the type of that field. Otherwise, return NULL.
-
- This function is actually recursive, so if ENCODED_FIELD_NAME
- doesn't match one of the fields of our symbol, then try to see
- if ENCODED_FIELD_NAME could not be a succession of field names
- (in other words, the user entered an expression of the form
- TYPE_NAME.FIELD1.FIELD2.FIELD3), in which case we evaluate
- each field name sequentially to obtain the desired field type.
- In case of failure, we return NULL. */
-
-static struct type *
-get_symbol_field_type (struct symbol *sym, const char *encoded_field_name)
-{
- const char *field_name = encoded_field_name;
- const char *subfield_name;
- struct type *type = sym->type ();
- int fieldno;
-
- if (type == NULL || field_name == NULL)
- return NULL;
- type = check_typedef (type);
-
- while (field_name[0] != '\0')
- {
- field_name = chop_separator (field_name);
-
- fieldno = ada_get_field_index (type, field_name, 1);
- if (fieldno >= 0)
- return type->field (fieldno).type ();
-
- subfield_name = field_name;
- while (*subfield_name != '\0' && *subfield_name != '.'
- && (subfield_name[0] != '_' || subfield_name[1] != '_'))
- subfield_name += 1;
-
- if (subfield_name[0] == '\0')
- return NULL;
-
- fieldno = ada_nget_field_index (type, field_name,
- subfield_name - field_name, 1);
- if (fieldno < 0)
- return NULL;
-
- type = type->field (fieldno).type ();
- field_name = subfield_name;
- }
-
- return NULL;
-}
-
-/* Look up NAME0 (an unencoded identifier or dotted name) in BLOCK (or
- expression_block_context if NULL). If it denotes a type, return
- that type. Otherwise, write expression code to evaluate it as an
- object and return NULL. In this second case, NAME0 will, in general,
- have the form <name>(.<selector_name>)*, where <name> is an object
- or renaming encoded in the debugging data. Calls error if no
- prefix <name> matches a name in the debugging data (i.e., matches
- either a complete name or, as a wild-card match, the final
- identifier). */
-
-static struct type*
-write_var_or_type (struct parser_state *par_state,
- const struct block *block, struct stoken name0)
-{
- int depth;
- char *encoded_name;
- int name_len;
-
- std::string name_storage = ada_encode (name0.ptr);
-
- if (block == nullptr)
- {
- auto iter = ada_parser->iterated_associations.find (name_storage);
- if (iter != ada_parser->iterated_associations.end ())
- {
- auto op = std::make_unique<ada_index_var_operation> ();
- iter->second.push_back (op.get ());
- par_state->push (std::move (op));
- return nullptr;
- }
-
- block = par_state->expression_context_block;
- }
-
- name_len = name_storage.size ();
- encoded_name = obstack_strndup (&ada_parser->temp_space,
- name_storage.c_str (),
- name_len);
- for (depth = 0; depth < MAX_RENAMING_CHAIN_LENGTH; depth += 1)
- {
- int tail_index;
-
- tail_index = name_len;
- while (tail_index > 0)
- {
- struct symbol *type_sym;
- struct symbol *renaming_sym;
- const char* renaming;
- int renaming_len;
- const char* renaming_expr;
- int terminator = encoded_name[tail_index];
-
- encoded_name[tail_index] = '\0';
- /* In order to avoid double-encoding, we want to only pass
- the decoded form to lookup functions. */
- std::string decoded_name = ada_decode (encoded_name);
- encoded_name[tail_index] = terminator;
-
- std::vector<struct block_symbol> syms
- = ada_lookup_symbol_list (decoded_name.c_str (), block,
- SEARCH_VFT);
-
- type_sym = select_possible_type_sym (syms);
-
- if (type_sym != NULL)
- renaming_sym = type_sym;
- else if (syms.size () == 1)
- renaming_sym = syms[0].symbol;
- else
- renaming_sym = NULL;
-
- switch (ada_parse_renaming (renaming_sym, &renaming,
- &renaming_len, &renaming_expr))
- {
- case ADA_NOT_RENAMING:
- break;
- case ADA_PACKAGE_RENAMING:
- case ADA_EXCEPTION_RENAMING:
- case ADA_SUBPROGRAM_RENAMING:
- {
- int alloc_len = renaming_len + name_len - tail_index + 1;
- char *new_name
- = (char *) obstack_alloc (&ada_parser->temp_space,
- alloc_len);
- strncpy (new_name, renaming, renaming_len);
- strcpy (new_name + renaming_len, encoded_name + tail_index);
- encoded_name = new_name;
- name_len = renaming_len + name_len - tail_index;
- goto TryAfterRenaming;
- }
- case ADA_OBJECT_RENAMING:
- write_object_renaming (par_state, block, renaming, renaming_len,
- renaming_expr, MAX_RENAMING_CHAIN_LENGTH);
- write_selectors (par_state, encoded_name + tail_index);
- return NULL;
- default:
- internal_error (_("impossible value from ada_parse_renaming"));
- }
-
- if (type_sym != NULL)
- {
- struct type *field_type;
-
- if (tail_index == name_len)
- return type_sym->type ();
-
- /* We have some extraneous characters after the type name.
- If this is an expression "TYPE_NAME.FIELD0.[...].FIELDN",
- then try to get the type of FIELDN. */
- field_type
- = get_symbol_field_type (type_sym, encoded_name + tail_index);
- if (field_type != NULL)
- return field_type;
- else
- error (_("Invalid attempt to select from type: \"%s\"."),
- name0.ptr);
- }
- else if (tail_index == name_len && syms.empty ())
- {
- struct type *type = find_primitive_type (par_state,
- encoded_name);
-
- if (type != NULL)
- return type;
- }
-
- if (syms.size () == 1)
- {
- write_var_from_sym (par_state, syms[0]);
- write_selectors (par_state, encoded_name + tail_index);
- return NULL;
- }
- else if (syms.empty ())
- {
- struct objfile *objfile = nullptr;
- if (block != nullptr)
- objfile = block->objfile ();
-
- bound_minimal_symbol msym
- = ada_lookup_simple_minsym (decoded_name.c_str (), objfile);
- if (msym.minsym != NULL)
- {
- par_state->push_new<ada_var_msym_value_operation> (msym);
- /* Maybe cause error here rather than later? FIXME? */
- write_selectors (par_state, encoded_name + tail_index);
- return NULL;
- }
-
- if (tail_index == name_len
- && strncmp (encoded_name, "standard__",
- sizeof ("standard__") - 1) == 0)
- error (_("No definition of \"%s\" found."), name0.ptr);
-
- tail_index = chop_selector (encoded_name, tail_index);
- }
- else
- {
- write_ambiguous_var (par_state, block, encoded_name,
- tail_index);
- write_selectors (par_state, encoded_name + tail_index);
- return NULL;
- }
- }
-
- if (!current_program_space->has_full_symbols ()
- && !current_program_space->has_partial_symbols ()
- && block == NULL)
- error (_("No symbol table is loaded. Use the \"%ps\" command."),
- styled_string (command_style.style (), "file"));
- if (block == par_state->expression_context_block)
- error (_("No definition of \"%s\" in current context."), name0.ptr);
- else
- error (_("No definition of \"%s\" in specified context."), name0.ptr);
-
- TryAfterRenaming: ;
- }
-
- error (_("Could not find renamed symbol \"%s\""), name0.ptr);
-
-}
-
-/* Because ada_completer_word_break_characters does not contain '.' --
- and it cannot easily be added, this breaks other completions -- we
- have to recreate the completion word-splitting here, so that we can
- provide a prefix that is then used when completing field names.
- Without this, an attempt like "complete print abc.d" will give a
- result like "print def" rather than "print abc.def". */
-
-std::string
-ada_parse_state::find_completion_bounds ()
-{
- const char *end = pstate->lexptr;
- /* First the end of the prefix. Here we stop at the token start or
- at '.' or space. */
- for (; end > m_original_expr && end[-1] != '.' && !c_isspace (end[-1]); --end)
- {
- /* Nothing. */
- }
- /* Now find the start of the prefix. */
- const char *ptr = end;
- /* Here we allow '.'. */
- for (;
- ptr > m_original_expr && (ptr[-1] == '.'
- || ptr[-1] == '_'
- || (ptr[-1] >= 'a' && ptr[-1] <= 'z')
- || (ptr[-1] >= 'A' && ptr[-1] <= 'Z')
- || (ptr[-1] & 0xff) >= 0x80);
- --ptr)
- {
- /* Nothing. */
- }
- /* ... except, skip leading spaces. */
- ptr = skip_spaces (ptr);
-
- return std::string (ptr, end);
-}
-
-/* A wrapper for write_var_or_type that is used specifically when
- completion is requested for the last of a sequence of
- identifiers. */
-
-static struct type *
-write_var_or_type_completion (struct parser_state *par_state,
- const struct block *block, struct stoken name0)
-{
- int tail_index = chop_selector (name0.ptr, name0.length);
- /* If there's no separator, just defer to ordinary symbol
- completion. */
- if (tail_index == -1)
- return write_var_or_type (par_state, block, name0);
-
- std::string copy (name0.ptr, tail_index);
- struct type *type = write_var_or_type (par_state, block,
- { copy.c_str (),
- (int) copy.length () });
- /* For completion purposes, it's enough that we return a type
- here. */
- if (type != nullptr)
- return type;
-
- ada_structop_operation *op = write_selectors (par_state,
- name0.ptr + tail_index);
- op->set_prefix (ada_parser->find_completion_bounds ());
- par_state->mark_struct_expression (op);
- return nullptr;
-}
-
-/* Write a left side of a component association (e.g., NAME in NAME =>
- exp). If NAME has the form of a selected component, write it as an
- ordinary expression. If it is a simple variable that unambiguously
- corresponds to exactly one symbol that does not denote a type or an
- object renaming, also write it normally as an OP_VAR_VALUE.
- Otherwise, write it as an OP_NAME.
-
- Unfortunately, we don't know at this point whether NAME is supposed
- to denote a record component name or the value of an array index.
- Therefore, it is not appropriate to disambiguate an ambiguous name
- as we normally would, nor to replace a renaming with its referent.
- As a result, in the (one hopes) rare case that one writes an
- aggregate such as (R => 42) where R renames an object or is an
- ambiguous name, one must write instead ((R) => 42). */
-
-static void
-write_name_assoc (struct parser_state *par_state, struct stoken name)
-{
- if (strchr (name.ptr, '.') == NULL)
- {
- std::vector<struct block_symbol> syms
- = ada_lookup_symbol_list (name.ptr,
- par_state->expression_context_block,
- SEARCH_VFT);
-
- if (syms.size () != 1 || syms[0].symbol->loc_class () == LOC_TYPEDEF)
- pstate->push_new<ada_string_operation> (copy_name (name));
- else
- write_var_from_sym (par_state, syms[0]);
- }
- else
- if (write_var_or_type (par_state, NULL, name) != NULL)
- error (_("Invalid use of type."));
-
- push_association<ada_name_association> (ada_pop ());
-}
-
-static struct type *
-type_for_char (struct parser_state *par_state, ULONGEST value)
-{
- if (value <= 0xff)
- return language_string_char_type (par_state->language (),
- par_state->gdbarch ());
- else if (value <= 0xffff)
- return language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "wide_character");
- return language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "wide_wide_character");
-}
-
-static struct type *
-type_system_address (struct parser_state *par_state)
-{
- struct type *type
- = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "system__address");
- return type != NULL ? type : parse_type (par_state)->builtin_data_ptr;
-}
diff --git a/gdb/ada-exp.h b/gdb/ada-exp.h
index 459b7d37b038..76ea3f741502 100644
--- a/gdb/ada-exp.h
+++ b/gdb/ada-exp.h
@@ -453,7 +453,7 @@ class ada_structop_operation
private:
/* We may need to provide a prefix to field name completion. See
- ada-exp-parser.y:find_completion_bounds for details. */
+ ada-exp-parser.c:find_completion_bounds for details. */
std::string m_prefix;
};
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 02410949bcab..0bbd7ff93e84 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
+#include "ada-exp-parser.h"
#include "event-top.h"
#include "exceptions.h"
#include "extract-store-integer.h"
diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h
index e224f99dcc19..cf89a90f2f5e 100644
--- a/gdb/ada-lang.h
+++ b/gdb/ada-lang.h
@@ -153,8 +153,6 @@ extern int ada_get_field_index (const struct type *type,
const char *field_name,
int maybe_missing);
-extern int ada_parse (struct parser_state *); /* Defined in ada-exp-parser.y */
-
/* Defined in ada-typeprint.c */
extern void ada_print_type (struct type *, const char *, struct ui_file *, int,
int, const struct type_print_options *);
diff --git a/gdb/ada-lex.l b/gdb/ada-lex.l
index dab1ad0d0067..62edcd251966 100644
--- a/gdb/ada-lex.l
+++ b/gdb/ada-lex.l
@@ -57,16 +57,6 @@ DIAGNOSTIC_IGNORE_REGISTER
#define NUMERAL_WIDTH 256
#define LONGEST_SIGN ((ULONGEST) 1 << (sizeof(LONGEST) * HOST_CHAR_BIT - 1))
-static void canonicalizeNumeral (char *s1, const char *);
-static struct stoken processString (const char*, int);
-static int processInt (struct parser_state *, const char *, const char *,
- const char *);
-static int processReal (struct parser_state *, const char *);
-static struct stoken processId (const char *, int);
-static int processAttribute (const char *);
-static int find_dot_all (const char *);
-static void rewind_to_char (int);
-
#undef YY_DECL
#define YY_DECL static int yylex ( void )
@@ -74,9 +64,6 @@ static void rewind_to_char (int);
Defining YY_NO_INPUT comments it out. */
#define YY_NO_INPUT
-/* The character we use to represent the completion point. */
-#define COMPLETE_CHAR '\001'
-
#undef YY_INPUT
#define YY_INPUT(BUF, RESULT, MAX_SIZE) \
if ( *pstate->lexptr == '\000' ) \
@@ -335,423 +322,19 @@ false { return FALSEKEYWORD; }
. { error (_("Invalid character '%s' in expression."), yytext); }
%%
-/* Initialize the lexer for processing new expression. */
+namespace ada_exp_parser
+{
+
+/* See ada-exp-parser.h. */
-static void
+void
lexer_init (FILE *inp)
{
BEGIN INITIAL;
yyrestart (inp);
}
-
-/* Copy S2 to S1, removing all underscores, and downcasing all letters. */
-
-static void
-canonicalizeNumeral (char *s1, const char *s2)
-{
- for (; *s2 != '\000'; s2 += 1)
- {
- if (*s2 != '_')
- {
- *s1 = c_tolower(*s2);
- s1 += 1;
- }
- }
- s1[0] = '\000';
-}
-
-/* Interprets the prefix of NUM that consists of digits of the given BASE
- as an integer of that BASE, with the string EXP as an exponent.
- Puts value in yylval, and returns INT, if the string is valid. Causes
- an error if the number is improperly formatted. BASE, if NULL, defaults
- to "10", and EXP to "1". The EXP does not contain a leading 'e' or 'E'.
- */
-
-static int
-processInt (struct parser_state *par_state, const char *base0,
- const char *num0, const char *exp0)
-{
- long exp;
- int base;
- /* For the based literal with an "f" prefix, we'll return a
- floating-point number. This counts the number of "l"s seen,
- to decide the width of the floating-point number to return. -1
- means no "f". */
- int floating_point_l_count = -1;
-
- if (base0 == NULL)
- base = 10;
- else
- {
- char *end_of_base;
- base = strtol (base0, &end_of_base, 10);
- if (base < 2 || base > 16)
- error (_("Invalid base: %d."), base);
- while (*end_of_base == 'l')
- {
- ++floating_point_l_count;
- ++end_of_base;
- }
- /* This assertion is ensured by the pattern. */
- gdb_assert (floating_point_l_count == -1 || *end_of_base == 'f');
- if (*end_of_base == 'f')
- {
- ++end_of_base;
- ++floating_point_l_count;
- }
- /* This assertion is ensured by the pattern. */
- gdb_assert (*end_of_base == '#');
- }
-
- if (exp0 == NULL)
- exp = 0;
- else
- exp = strtol(exp0, (char **) NULL, 10);
-
- gdb_mpz result;
- while (c_isxdigit (*num0))
- {
- int dig = fromhex (*num0);
- if (dig >= base)
- error (_("Invalid digit `%c' in based literal"), *num0);
- result *= base;
- result += dig;
- ++num0;
- }
-
- while (exp > 0)
- {
- result *= base;
- exp -= 1;
- }
-
- if (floating_point_l_count > -1)
- {
- struct type *fp_type;
- if (floating_point_l_count == 0)
- fp_type = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "float");
- else if (floating_point_l_count == 1)
- fp_type = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "long_float");
- else
- {
- /* This assertion is ensured by the pattern. */
- gdb_assert (floating_point_l_count == 2);
- fp_type = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "long_long_float");
- }
-
- yylval.typed_val_float.type = fp_type;
- result.write (gdb::make_array_view (yylval.typed_val_float.val,
- fp_type->length ()),
- type_byte_order (fp_type),
- true);
-
- return FLOAT;
- }
-
- const gdb_mpz *value = ada_parser->push_integer (std::move (result));
-
- 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 ());
-
- if (fits_in_type (1, *value, int_bits, true))
- yylval.typed_val.type = parse_type (par_state)->builtin_int;
- else if (fits_in_type (1, *value, long_bits, true))
- yylval.typed_val.type = parse_type (par_state)->builtin_long;
- else if (fits_in_type (1, *value, long_bits, false))
- yylval.typed_val.type
- = builtin_type (par_state->gdbarch ())->builtin_unsigned_long;
- else if (fits_in_type (1, *value, long_long_bits, true))
- yylval.typed_val.type = parse_type (par_state)->builtin_long_long;
- else if (fits_in_type (1, *value, long_long_bits, false))
- yylval.typed_val.type
- = builtin_type (par_state->gdbarch ())->builtin_unsigned_long_long;
- else if (fits_in_type (1, *value, 128, true))
- yylval.typed_val.type
- = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "long_long_long_integer");
- else if (fits_in_type (1, *value, 128, false))
- yylval.typed_val.type
- = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- "unsigned_long_long_long_integer");
- else
- error (_("Integer literal out of range"));
-
- yylval.typed_val.val = value;
- return INT;
-}
-
-static int
-processReal (struct parser_state *par_state, const char *num0)
-{
- yylval.typed_val_float.type = parse_type (par_state)->builtin_long_double;
-
- bool parsed = parse_float (num0, strlen (num0),
- yylval.typed_val_float.type,
- yylval.typed_val_float.val);
- gdb_assert (parsed);
- return FLOAT;
-}
-
-
-/* Store a canonicalized version of NAME0[0..LEN-1] in yylval.ssym. The
- resulting string is valid until the next call to ada_parse. If
- NAME0 contains the substring "___", it is assumed to be already
- encoded and the resulting name is equal to it. Similarly, if the name
- starts with '<', it is copied verbatim. Otherwise, it differs
- from NAME0 in that:
- + Characters between '...' are transferred verbatim to yylval.ssym.
- + Trailing "'" characters in quoted sequences are removed (a leading quote is
- preserved to indicate that the name is not to be GNAT-encoded).
- + Unquoted whitespace is removed.
- + Unquoted alphabetic characters are mapped to lower case.
- Result is returned as a struct stoken, but for convenience, the string
- is also null-terminated. Result string valid until the next call of
- ada_parse.
- */
-static struct stoken
-processId (const char *name0, int len)
-{
- char *name = (char *) obstack_alloc (&ada_parser->temp_space, len + 11);
- int i0, i;
- struct stoken result;
-
- result.ptr = name;
- while (len > 0 && c_isspace (name0[len-1]))
- len -= 1;
-
- if (name0[0] == '<' || strstr (name0, "___") != NULL)
- {
- strncpy (name, name0, len);
- name[len] = '\000';
- result.length = len;
- return result;
- }
-
- bool in_quotes = false;
- i = i0 = 0;
- while (i0 < len)
- {
- if (name0[i0] == COMPLETE_CHAR)
- {
- /* Just ignore. */
- ++i0;
- }
- else if (in_quotes)
- name[i++] = name0[i0++];
- else if (c_isalnum (name0[i0]))
- {
- name[i] = c_tolower (name0[i0]);
- i += 1; i0 += 1;
- }
- else if (c_isspace (name0[i0]))
- i0 += 1;
- else if (name0[i0] == '\'')
- {
- /* Copy the starting quote, but not the ending quote. */
- if (!in_quotes)
- name[i++] = name0[i0++];
- in_quotes = !in_quotes;
- }
- else
- name[i++] = name0[i0++];
- }
- name[i] = '\000';
-
- result.length = i;
- return result;
-}
-
-/* Return TEXT[0..LEN-1], a string literal without surrounding quotes,
- with special hex character notations replaced with characters.
- Result valid until the next call to ada_parse. */
-
-static struct stoken
-processString (const char *text, int len)
-{
- const char *p;
- char *q;
- const char *lim = text + len;
- struct stoken result;
-
- q = (char *) obstack_alloc (&ada_parser->temp_space, len);
- result.ptr = q;
- p = text;
- while (p < lim)
- {
- if (p[0] == '[' && p[1] == '"' && p+2 < lim)
- {
- if (p[2] == '"') /* "...["""]... */
- {
- *q = '"';
- p += 4;
- }
- else
- {
- const char *end;
- ULONGEST chr = strtoulst (p + 2, &end, 16);
- if (chr > 0xff)
- error (_("wide strings are not yet supported"));
- *q = (char) chr;
- p = end + 1;
- }
- }
- else
- *q = *p;
- q += 1;
- p += 1;
- }
- result.length = q - result.ptr;
- return result;
-}
-
-/* Returns the position within STR of the '.' in a
- '.{WHITE}*all' component of a dotted name, or -1 if there is none.
- Note: we actually don't need this routine, since 'all' can never be an
- Ada identifier. Thus, looking up foo.all or foo.all.x as a name
- must fail, and will eventually be interpreted as (foo).all or
- (foo).all.x. However, this does avoid an extraneous lookup. */
-
-static int
-find_dot_all (const char *str)
-{
- int i;
-
- for (i = 0; str[i] != '\000'; i++)
- if (str[i] == '.')
- {
- int i0 = i;
-
- do
- i += 1;
- while (c_isspace (str[i]));
-
- if (strncasecmp (str + i, "all", 3) == 0
- && !c_isalnum (str[i + 3]) && str[i + 3] != '_')
- return i0;
- }
- return -1;
-}
-
-/* Returns non-zero iff string SUBSEQ matches a subsequence of STR, ignoring
- case. */
-
-static int
-subseqMatch (const char *subseq, const char *str)
-{
- if (subseq[0] == '\0')
- return 1;
- else if (str[0] == '\0')
- return 0;
- else if (c_tolower (subseq[0]) == c_tolower (str[0]))
- return subseqMatch (subseq+1, str+1) || subseqMatch (subseq, str+1);
- else
- return subseqMatch (subseq, str+1);
-}
-
-
-static const struct { const char *name; int code; }
-attributes[] = {
- { "address", TICK_ADDRESS },
- { "unchecked_access", TICK_ACCESS },
- { "unrestricted_access", TICK_ACCESS },
- { "access", TICK_ACCESS },
- { "first", TICK_FIRST },
- { "last", TICK_LAST },
- { "length", TICK_LENGTH },
- { "max", TICK_MAX },
- { "min", TICK_MIN },
- { "modulus", TICK_MODULUS },
- { "object_size", TICK_OBJECT_SIZE },
- { "pos", TICK_POS },
- { "range", TICK_RANGE },
- { "size", TICK_SIZE },
- { "tag", TICK_TAG },
- { "val", TICK_VAL },
- { "enum_rep", TICK_ENUM_REP },
- { "enum_val", TICK_ENUM_VAL },
-};
-
-/* Return the syntactic code corresponding to the attribute name or
- abbreviation STR. */
-
-static int
-processAttribute (const char *str)
-{
- gdb_assert (*str == '\'');
- ++str;
- while (c_isspace (*str))
- ++str;
-
- int len = strlen (str);
- if (len > 0 && str[len - 1] == COMPLETE_CHAR)
- {
- /* This is enforced by YY_INPUT. */
- gdb_assert (pstate->parse_completion);
- yylval.sval.ptr = obstack_strndup (&ada_parser->temp_space,
- str, len - 1);
- yylval.sval.length = len - 1;
- return TICK_COMPLETE;
- }
-
- for (const auto &item : attributes)
- if (strcasecmp (str, item.name) == 0)
- return item.code;
-
- std::optional<int> found;
- for (const auto &item : attributes)
- if (subseqMatch (str, item.name))
- {
- if (!found.has_value ())
- found = item.code;
- else
- error (_("ambiguous attribute name: `%s'"), str);
- }
- if (!found.has_value ())
- error (_("unrecognized attribute: `%s'"), str);
-
- return *found;
-}
-
-bool
-ada_tick_completer::complete (struct expression *exp,
- completion_tracker &tracker)
-{
- completion_list output;
- for (const auto &item : attributes)
- {
- if (strncasecmp (item.name, m_name.c_str (), m_name.length ()) == 0)
- output.emplace_back (xstrdup (item.name));
- }
- tracker.add_completions (std::move (output));
- return true;
-}
-
-/* Back up lexptr by yyleng and then to the rightmost occurrence of
- character CH, case-folded (there must be one). WARNING: since
- lexptr points to the next input character that Flex has not yet
- transferred to its internal buffer, the use of this function
- depends on the assumption that Flex calls YY_INPUT only when it is
- logically necessary to do so (thus, there is no reading ahead
- farther than needed to identify the next token.) */
-
-static void
-rewind_to_char (int ch)
-{
- pstate->lexptr -= yyleng;
- while (c_toupper (*pstate->lexptr) != c_toupper (ch))
- pstate->lexptr -= 1;
- yyrestart (NULL);
-}
+} /* namespace ada_exp_parser */
/* Dummy definition to suppress warnings about unused static definitions. */
typedef void (*dummy_function) ();
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 14/19] gdb: move d-exp-parser.y's support code to d-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (12 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 15/19] gdb: move f-exp-parser.y's support code to f-exp-parser.c simon.marchi
` (4 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Similar to the previous commits, but for the D expression parser.
One wrinkle that was not in previous commits is the type_stack global.
Once it moves into the d_exp_parser namespace and is brought in with a
using-directive, its name clashes with the struct type_stack type. Use a
using declaration specifically for it in the .y prologue to restore the
original name hiding, so the grammar actions can work unmodified.
Put the parser support code inside the d_exp_parser namespace.
Change-Id: I8d45f1ba606fa8748976f34f39808703c8f6843d
---
gdb/Makefile.in | 2 +
gdb/d-exp-parser.c | 1050 ++++++++++++++++++++++++++++++++++++++++++++
gdb/d-exp-parser.h | 83 ++++
gdb/d-exp-parser.y | 1043 +------------------------------------------
gdb/d-lang.c | 1 +
gdb/d-lang.h | 4 -
6 files changed, 1142 insertions(+), 1041 deletions(-)
create mode 100644 gdb/d-exp-parser.c
create mode 100644 gdb/d-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d2cfecb0def9..f861b1f53261 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1084,6 +1084,7 @@ COMMON_SFILES = \
cp-support.c \
cp-valprint.c \
ctfread.c \
+ d-exp-parser.c \
d-lang.c \
d-namespace.c \
d-valprint.c \
@@ -1395,6 +1396,7 @@ HFILES_NO_SRCDIR = \
disasm.h \
disasm-selftests.h \
displaced-stepping.h \
+ d-exp-parser.h \
d-lang.h \
dummy-frame.h \
dwarf2/abbrev.h \
diff --git a/gdb/d-exp-parser.c b/gdb/d-exp-parser.c
new file mode 100644
index 000000000000..e6b5f3af8498
--- /dev/null
+++ b/gdb/d-exp-parser.c
@@ -0,0 +1,1050 @@
+/* Support code for the D expression parser, for GDB.
+
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "d-exp-parser.h"
+#include "d-exp-parser-gen.h"
+#include "block.h"
+#include "c-exp-parser.h"
+#include "c-lang.h"
+#include "charset.h"
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ d-exp-parser-gen.c. Bison produces a declaration for d_yyparse in
+ d-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int d_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for d_yydebug. */
+
+extern int d_yydebug;
+
+namespace d_exp_parser
+{
+
+/* See d-exp-parser.h. */
+
+parser_state *pstate;
+
+/* See d-exp-parser.h. */
+
+struct type_stack *type_stack;
+
+/* See d-exp-parser.h. */
+
+int
+type_aggregate_p (struct type *type)
+{
+ return (type->code () == TYPE_CODE_STRUCT
+ || type->code () == TYPE_CODE_UNION
+ || type->code () == TYPE_CODE_MODULE
+ || (type->code () == TYPE_CODE_ENUM
+ && type->is_declared_class ()));
+}
+
+/* See d-exp-parser.h. */
+
+int
+parse_number (struct parser_state *ps, const char *p,
+ int len, int parsed_float, d_exp_parser_YYSTYPE *putithere)
+{
+ ULONGEST n = 0;
+ ULONGEST prevn = 0;
+ ULONGEST un;
+
+ int i = 0;
+ int c;
+ int base = input_radix;
+ int unsigned_p = 0;
+ int long_p = 0;
+
+ /* We have found a "L" or "U" suffix. */
+ int found_suffix = 0;
+
+ ULONGEST high_bit;
+ struct type *signed_type;
+ struct type *unsigned_type;
+
+ if (parsed_float)
+ {
+ char *s, *sp;
+
+ /* Strip out all embedded '_' before passing to parse_float. */
+ s = (char *) alloca (len + 1);
+ sp = s;
+ while (len-- > 0)
+ {
+ if (*p != '_')
+ *sp++ = *p;
+ p++;
+ }
+ *sp = '\0';
+ len = strlen (s);
+
+ /* Check suffix for `i' , `fi' or `li' (idouble, ifloat or ireal). */
+ if (len >= 1 && c_tolower (s[len - 1]) == 'i')
+ {
+ if (len >= 2 && c_tolower (s[len - 2]) == 'f')
+ {
+ putithere->typed_val_float.type
+ = parse_d_type (ps)->builtin_ifloat;
+ len -= 2;
+ }
+ else if (len >= 2 && c_tolower (s[len - 2]) == 'l')
+ {
+ putithere->typed_val_float.type
+ = parse_d_type (ps)->builtin_ireal;
+ len -= 2;
+ }
+ else
+ {
+ putithere->typed_val_float.type
+ = parse_d_type (ps)->builtin_idouble;
+ len -= 1;
+ }
+ }
+ /* Check suffix for `f' or `l'' (float or real). */
+ else if (len >= 1 && c_tolower (s[len - 1]) == 'f')
+ {
+ putithere->typed_val_float.type
+ = parse_d_type (ps)->builtin_float;
+ len -= 1;
+ }
+ else if (len >= 1 && c_tolower (s[len - 1]) == 'l')
+ {
+ putithere->typed_val_float.type
+ = parse_d_type (ps)->builtin_real;
+ len -= 1;
+ }
+ /* Default type if no suffix. */
+ else
+ {
+ putithere->typed_val_float.type
+ = parse_d_type (ps)->builtin_double;
+ }
+
+ if (!parse_float (s, len,
+ putithere->typed_val_float.type,
+ putithere->typed_val_float.val))
+ return ERROR;
+
+ return FLOAT_LITERAL;
+ }
+
+ /* Handle base-switching prefixes 0x, 0b, 0 */
+ if (p[0] == '0')
+ switch (p[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 'b':
+ case 'B':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 2;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c == '_')
+ continue; /* Ignore embedded '_'. */
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != 'l' && c != 'u')
+ n *= base;
+ if (c >= '0' && c <= '9')
+ {
+ if (found_suffix)
+ return ERROR;
+ n += i = c - '0';
+ }
+ else
+ {
+ if (base > 10 && c >= 'a' && c <= 'f')
+ {
+ if (found_suffix)
+ return ERROR;
+ n += i = c - 'a' + 10;
+ }
+ else if (c == 'l' && long_p == 0)
+ {
+ long_p = 1;
+ found_suffix = 1;
+ }
+ else if (c == 'u' && unsigned_p == 0)
+ {
+ unsigned_p = 1;
+ found_suffix = 1;
+ }
+ else
+ return ERROR; /* Char not a digit */
+ }
+ if (i >= base)
+ return ERROR; /* Invalid digit in this base. */
+ /* Portably test for integer overflow. */
+ if (c != 'l' && c != 'u')
+ {
+ ULONGEST n2 = prevn * base;
+ if ((n2 / base != prevn) || (n2 + i < prevn))
+ error (_("Numeric constant too large."));
+ }
+ prevn = n;
+ }
+
+ /* An integer constant is an int or a long. An L suffix forces it to
+ be long, and a U suffix forces it to be unsigned. To figure out
+ whether it fits, we shift it right and see whether anything remains.
+ Note that we can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or
+ more in one operation, because many compilers will warn about such a
+ shift (which always produces a zero result). To deal with the case
+ where it is we just always shift the value more than once, with fewer
+ bits each time. */
+ un = (ULONGEST) n >> 2;
+ if (long_p == 0 && (un >> 30) == 0)
+ {
+ high_bit = ((ULONGEST) 1) << 31;
+ signed_type = parse_d_type (ps)->builtin_int;
+ /* For decimal notation, keep the sign of the worked out type. */
+ if (base == 10 && !unsigned_p)
+ unsigned_type = parse_d_type (ps)->builtin_long;
+ else
+ unsigned_type = parse_d_type (ps)->builtin_uint;
+ }
+ else
+ {
+ int shift;
+ if (sizeof (ULONGEST) * HOST_CHAR_BIT < 64)
+ /* A long long does not fit in a LONGEST. */
+ shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1);
+ else
+ shift = 63;
+ high_bit = (ULONGEST) 1 << shift;
+ signed_type = parse_d_type (ps)->builtin_long;
+ unsigned_type = parse_d_type (ps)->builtin_ulong;
+ }
+
+ putithere->typed_val_int.val = n;
+
+ /* If the high bit of the worked out type is set then this number
+ has to be unsigned_type. */
+ if (unsigned_p || (n & high_bit))
+ putithere->typed_val_int.type = unsigned_type;
+ else
+ putithere->typed_val_int.type = signed_type;
+
+ return INTEGER_LITERAL;
+}
+
+/* Temporary obstack used for holding strings. */
+static struct obstack tempbuf;
+static int tempbuf_init;
+
+/* Parse a string or character literal from TOKPTR. The string or
+ character may be wide or unicode. *OUTPTR is set to just after the
+ end of the literal in the input string. The resulting token is
+ stored in VALUE. This returns a token value, either STRING or
+ CHAR, depending on what was parsed. *HOST_CHARS is set to the
+ number of host characters in the literal. */
+
+static int
+parse_string_or_char (const char *tokptr, const char **outptr,
+ struct typed_stoken *value, int *host_chars)
+{
+ int quote;
+
+ /* Build the gdb internal form of the input string in tempbuf. Note
+ that the buffer is null byte terminated *only* for the
+ convenience of debugging gdb itself and printing the buffer
+ contents when the buffer contains no embedded nulls. Gdb does
+ not depend upon the buffer being null byte terminated, it uses
+ the length string instead. This allows gdb to handle C strings
+ (as well as strings in other languages) with embedded null
+ bytes */
+
+ if (!tempbuf_init)
+ tempbuf_init = 1;
+ else
+ obstack_free (&tempbuf, NULL);
+ obstack_init (&tempbuf);
+
+ /* Skip the quote. */
+ quote = *tokptr;
+ ++tokptr;
+
+ *host_chars = 0;
+
+ while (*tokptr)
+ {
+ char c = *tokptr;
+ if (c == '\\')
+ {
+ ++tokptr;
+ *host_chars += c_parse_escape (&tokptr, &tempbuf);
+ }
+ else if (c == quote)
+ break;
+ else
+ {
+ obstack_1grow (&tempbuf, c);
+ ++tokptr;
+ /* FIXME: this does the wrong thing with multi-byte host
+ characters. We could use mbrlen here, but that would
+ make "set host-charset" a bit less useful. */
+ ++*host_chars;
+ }
+ }
+
+ if (*tokptr != quote)
+ {
+ if (quote == '"' || quote == '`')
+ error (_("Unterminated string in expression."));
+ else
+ error (_("Unmatched single quote."));
+ }
+ ++tokptr;
+
+ /* FIXME: should instead use own language string_type enum
+ and handle D-specific string suffixes here. */
+ if (quote == '\'')
+ value->type = C_CHAR;
+ else
+ value->type = C_STRING;
+
+ value->ptr = (char *) obstack_base (&tempbuf);
+ value->length = obstack_object_size (&tempbuf);
+
+ *outptr = tokptr;
+
+ return quote == '\'' ? CHARACTER_LITERAL : STRING_LITERAL;
+}
+
+struct d_token
+{
+ const char *oper;
+ int token;
+ enum exp_opcode opcode;
+};
+
+static const struct d_token tokentab3[] =
+ {
+ {"^^=", ASSIGN_MODIFY, BINOP_EXP},
+ {"<<=", ASSIGN_MODIFY, BINOP_LSH},
+ {">>=", ASSIGN_MODIFY, BINOP_RSH},
+ };
+
+static const struct d_token tokentab2[] =
+ {
+ {"+=", ASSIGN_MODIFY, BINOP_ADD},
+ {"-=", ASSIGN_MODIFY, BINOP_SUB},
+ {"*=", ASSIGN_MODIFY, BINOP_MUL},
+ {"/=", ASSIGN_MODIFY, BINOP_DIV},
+ {"%=", ASSIGN_MODIFY, BINOP_REM},
+ {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
+ {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
+ {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
+ {"++", INCREMENT, OP_NULL},
+ {"--", DECREMENT, OP_NULL},
+ {"&&", ANDAND, OP_NULL},
+ {"||", OROR, OP_NULL},
+ {"^^", HATHAT, OP_NULL},
+ {"<<", LSH, OP_NULL},
+ {">>", RSH, OP_NULL},
+ {"==", EQUAL, OP_NULL},
+ {"!=", NOTEQUAL, OP_NULL},
+ {"<=", LEQ, OP_NULL},
+ {">=", GEQ, OP_NULL},
+ {"..", DOTDOT, OP_NULL},
+ };
+
+/* Identifier-like tokens. */
+static const struct d_token ident_tokens[] =
+ {
+ {"is", IDENTITY, OP_NULL},
+ {"!is", NOTIDENTITY, OP_NULL},
+
+ {"cast", CAST_KEYWORD, OP_NULL},
+ {"const", CONST_KEYWORD, OP_NULL},
+ {"immutable", IMMUTABLE_KEYWORD, OP_NULL},
+ {"shared", SHARED_KEYWORD, OP_NULL},
+ {"super", SUPER_KEYWORD, OP_NULL},
+
+ {"null", NULL_KEYWORD, OP_NULL},
+ {"true", TRUE_KEYWORD, OP_NULL},
+ {"false", FALSE_KEYWORD, OP_NULL},
+
+ {"init", INIT_KEYWORD, OP_NULL},
+ {"sizeof", SIZEOF_KEYWORD, OP_NULL},
+ {"typeof", TYPEOF_KEYWORD, OP_NULL},
+ {"typeid", TYPEID_KEYWORD, OP_NULL},
+
+ {"delegate", DELEGATE_KEYWORD, OP_NULL},
+ {"function", FUNCTION_KEYWORD, OP_NULL},
+ {"struct", STRUCT_KEYWORD, OP_NULL},
+ {"union", UNION_KEYWORD, OP_NULL},
+ {"class", CLASS_KEYWORD, OP_NULL},
+ {"interface", INTERFACE_KEYWORD, OP_NULL},
+ {"enum", ENUM_KEYWORD, OP_NULL},
+ {"template", TEMPLATE_KEYWORD, OP_NULL},
+ };
+
+/* This is set if a NAME token appeared at the very end of the input
+ string, with no whitespace separating the name from the EOF. This
+ is used only when parsing to do field name completion. */
+static int saw_name_at_eof;
+
+/* This is set if the previously-returned token was a structure operator.
+ This is used only when parsing to do field name completion. */
+static int last_was_structop;
+
+/* Depth of parentheses. */
+static int paren_depth;
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+lex_one_token (struct parser_state *par_state)
+{
+ int c;
+ int namelen;
+ const char *tokstart;
+ int saw_structop = last_was_structop;
+
+ last_was_structop = 0;
+
+ retry:
+
+ pstate->prev_lexptr = pstate->lexptr;
+
+ tokstart = pstate->lexptr;
+ /* See if it is a special token of length 3. */
+ for (const auto &token : tokentab3)
+ if (strncmp (tokstart, token.oper, 3) == 0)
+ {
+ pstate->lexptr += 3;
+ d_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ /* See if it is a special token of length 2. */
+ for (const auto &token : tokentab2)
+ if (strncmp (tokstart, token.oper, 2) == 0)
+ {
+ pstate->lexptr += 2;
+ d_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ /* If we're parsing for field name completion, and the previous
+ token allows such completion, return a COMPLETE token.
+ Otherwise, we were already scanning the original text, and
+ we're really done. */
+ if (saw_name_at_eof)
+ {
+ saw_name_at_eof = 0;
+ return COMPLETE;
+ }
+ else if (saw_structop)
+ return COMPLETE;
+ else
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ pstate->lexptr++;
+ goto retry;
+
+ case '[':
+ case '(':
+ paren_depth++;
+ pstate->lexptr++;
+ return c;
+
+ case ']':
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ pstate->lexptr++;
+ return c;
+
+ case ',':
+ if (pstate->comma_terminates && paren_depth == 0)
+ return 0;
+ pstate->lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
+ {
+ if (pstate->parse_completion)
+ last_was_structop = 1;
+ goto symbol; /* Nope, must be a symbol. */
+ }
+ [[fallthrough]];
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0, toktype;
+ const char *p = tokstart;
+ int hex = input_radix > 10;
+
+ if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+ {
+ p += 2;
+ hex = 1;
+ }
+
+ for (;; ++p)
+ {
+ /* Hex exponents start with 'p', because 'e' is a valid hex
+ digit and thus does not indicate a floating point number
+ when the radix is hex. */
+ if ((!hex && !got_e && c_tolower (p[0]) == 'e')
+ || (hex && !got_e && c_tolower (p[0] == 'p')))
+ got_dot = got_e = 1;
+ /* A '.' always indicates a decimal floating point number
+ regardless of the radix. If we have a '..' then its the
+ end of the number and the beginning of a slice. */
+ else if (!got_dot && (p[0] == '.' && p[1] != '.'))
+ got_dot = 1;
+ /* This is the sign of the exponent, not the end of the number. */
+ else if (got_e && (c_tolower (p[-1]) == 'e'
+ || c_tolower (p[-1]) == 'p')
+ && (*p == '-' || *p == '+'))
+ continue;
+ /* We will take any letters or digits, ignoring any embedded '_'.
+ parse_number will complain if past the radix, or if L or U are
+ not final. */
+ else if ((*p < '0' || *p > '9') && (*p != '_')
+ && ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z')))
+ break;
+ }
+
+ toktype = parse_number (par_state, tokstart, p - tokstart,
+ got_dot|got_e, &d_yylval);
+ if (toktype == ERROR)
+ error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+ tokstart);
+ pstate->lexptr = p;
+ return toktype;
+ }
+
+ case '@':
+ {
+ const char *p = &tokstart[1];
+ size_t len = strlen ("entry");
+
+ while (c_isspace (*p))
+ p++;
+ if (strncmp (p, "entry", len) == 0 && !c_isalnum (p[len])
+ && p[len] != '_')
+ {
+ pstate->lexptr = &p[len];
+ return ENTRY;
+ }
+ }
+ [[fallthrough]];
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '<':
+ case '>':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ pstate->lexptr++;
+ return c;
+
+ case '\'':
+ case '"':
+ case '`':
+ {
+ int host_len;
+ int result = parse_string_or_char (tokstart, &pstate->lexptr,
+ &d_yylval.tsval, &host_len);
+ if (result == CHARACTER_LITERAL)
+ {
+ if (host_len == 0)
+ error (_("Empty character constant."));
+ else if (host_len > 2 && c == '\'')
+ {
+ ++tokstart;
+ namelen = pstate->lexptr - tokstart - 1;
+ goto tryname;
+ }
+ else if (host_len > 1)
+ error (_("Invalid character constant."));
+ }
+ return result;
+ }
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error (_("Invalid character '%c' in expression"), c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));)
+ c = tokstart[++namelen];
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ return 0;
+
+ /* For the same reason (breakpoint conditions), "thread N"
+ terminates the expression. "thread" could be an identifier, but
+ an identifier is never followed by a number without intervening
+ punctuation. "task" is similar. Handle abbreviations of these,
+ similarly to breakpoint.c:find_condition_and_thread. */
+ if (namelen >= 1
+ && (strncmp (tokstart, "thread", namelen) == 0
+ || strncmp (tokstart, "task", namelen) == 0)
+ && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t'))
+ {
+ const char *p = skip_spaces (tokstart + namelen + 1);
+ if (*p >= '0' && *p <= '9')
+ return 0;
+ }
+
+ pstate->lexptr += namelen;
+
+ tryname:
+
+ d_yylval.sval.ptr = tokstart;
+ d_yylval.sval.length = namelen;
+
+ /* Catch specific keywords. */
+ std::string copy = copy_name (d_yylval.sval);
+ for (const auto &token : ident_tokens)
+ if (copy == token.oper)
+ {
+ /* It is ok to always set this, even though we don't always
+ strictly need to. */
+ d_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ if (*tokstart == '$')
+ return DOLLAR_VARIABLE;
+
+ d_yylval.tsym.type
+ = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (), copy.c_str ());
+ if (d_yylval.tsym.type != NULL)
+ return TYPENAME;
+
+ /* Input names that aren't symbols but ARE valid hex numbers,
+ when the input radix permits them, can be names or numbers
+ depending on the parse. Note we support radixes > 16 here. */
+ if ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
+ || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10))
+ {
+ d_exp_parser_YYSTYPE newlval; /* Its value is ignored. */
+ int hextype = parse_number (par_state, tokstart, namelen, 0, &newlval);
+ if (hextype == INTEGER_LITERAL)
+ return NAME_OR_INT;
+ }
+
+ if (pstate->parse_completion && *pstate->lexptr == '\0')
+ saw_name_at_eof = 1;
+
+ return IDENTIFIER;
+}
+
+/* An object of this type is pushed on a FIFO by the "outer" lexer. */
+struct d_token_and_value
+{
+ int token;
+ d_exp_parser_YYSTYPE value;
+};
+
+
+/* A FIFO of tokens that have been read but not yet returned to the
+ parser. */
+static std::vector<d_token_and_value> token_fifo;
+
+/* Non-zero if the lexer should return tokens from the FIFO. */
+static int popping;
+
+/* Temporary storage for yylex; this holds symbol names as they are
+ built up. */
+static auto_obstack name_obstack;
+
+/* Classify an IDENTIFIER token. The contents of the token are in `yylval'.
+ Updates yylval and returns the new token type. BLOCK is the block
+ in which lookups start; this can be NULL to mean the global scope. */
+
+static int
+classify_name (struct parser_state *par_state, const struct block *block)
+{
+ struct block_symbol sym;
+ struct field_of_this_result is_a_field_of_this;
+
+ std::string copy = copy_name (d_yylval.sval);
+
+ sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
+ if (sym.symbol && sym.symbol->loc_class () == LOC_TYPEDEF)
+ {
+ d_yylval.tsym.type = sym.symbol->type ();
+ return TYPENAME;
+ }
+ else if (sym.symbol == NULL)
+ {
+ /* Look-up first for a module name, then a type. */
+ sym = lookup_symbol (copy.c_str (), block, SEARCH_MODULE_DOMAIN,
+ nullptr);
+ if (sym.symbol == NULL)
+ sym = lookup_symbol (copy.c_str (), block, SEARCH_STRUCT_DOMAIN,
+ nullptr);
+
+ if (sym.symbol != NULL)
+ {
+ d_yylval.tsym.type = sym.symbol->type ();
+ return TYPENAME;
+ }
+
+ return UNKNOWN_NAME;
+ }
+
+ return IDENTIFIER;
+}
+
+/* Like classify_name, but used by the inner loop of the lexer, when a
+ name might have already been seen. CONTEXT is the context type, or
+ NULL if this is the first component of a name. */
+
+static int
+classify_inner_name (struct parser_state *par_state,
+ const struct block *block, struct type *context)
+{
+ struct type *type;
+
+ if (context == NULL)
+ return classify_name (par_state, block);
+
+ type = check_typedef (context);
+ if (!type_aggregate_p (type))
+ return ERROR;
+
+ std::string copy = copy_name (d_yylval.ssym.stoken);
+ d_yylval.ssym.sym = d_lookup_nested_symbol (type, copy.c_str (), block);
+
+ if (d_yylval.ssym.sym.symbol == NULL)
+ return ERROR;
+
+ if (d_yylval.ssym.sym.symbol->loc_class () == LOC_TYPEDEF)
+ {
+ d_yylval.tsym.type = d_yylval.ssym.sym.symbol->type ();
+ return TYPENAME;
+ }
+
+ return IDENTIFIER;
+}
+
+/* See d-exp-parser.h. */
+
+int
+d_yylex (void)
+{
+ d_token_and_value current;
+ int last_was_dot;
+ struct type *context_type = NULL;
+ int last_to_examine, next_to_examine, checkpoint;
+ const struct block *search_block;
+
+ if (popping && !token_fifo.empty ())
+ goto do_pop;
+ popping = 0;
+
+ /* Read the first token and decide what to do. */
+ current.token = lex_one_token (pstate);
+ if (current.token != IDENTIFIER && current.token != '.')
+ return current.token;
+
+ /* Read any sequence of alternating "." and identifier tokens into
+ the token FIFO. */
+ current.value = d_yylval;
+ token_fifo.push_back (current);
+ last_was_dot = current.token == '.';
+
+ while (1)
+ {
+ current.token = lex_one_token (pstate);
+ current.value = d_yylval;
+ token_fifo.push_back (current);
+
+ if ((last_was_dot && current.token != IDENTIFIER)
+ || (!last_was_dot && current.token != '.'))
+ break;
+
+ last_was_dot = !last_was_dot;
+ }
+ popping = 1;
+
+ /* We always read one extra token, so compute the number of tokens
+ to examine accordingly. */
+ last_to_examine = token_fifo.size () - 2;
+ next_to_examine = 0;
+
+ current = token_fifo[next_to_examine];
+ ++next_to_examine;
+
+ /* If we are not dealing with a typename, now is the time to find out. */
+ if (current.token == IDENTIFIER)
+ {
+ d_yylval = current.value;
+ current.token = classify_name (pstate, pstate->expression_context_block);
+ current.value = d_yylval;
+ }
+
+ /* If the IDENTIFIER is not known, it could be a package symbol,
+ first try building up a name until we find the qualified module. */
+ if (current.token == UNKNOWN_NAME)
+ {
+ name_obstack.clear ();
+ obstack_grow (&name_obstack, current.value.sval.ptr,
+ current.value.sval.length);
+
+ last_was_dot = 0;
+
+ while (next_to_examine <= last_to_examine)
+ {
+ d_token_and_value next;
+
+ next = token_fifo[next_to_examine];
+ ++next_to_examine;
+
+ if (next.token == IDENTIFIER && last_was_dot)
+ {
+ /* Update the partial name we are constructing. */
+ obstack_grow_str (&name_obstack, ".");
+ obstack_grow (&name_obstack, next.value.sval.ptr,
+ next.value.sval.length);
+
+ d_yylval.sval.ptr = (char *) obstack_base (&name_obstack);
+ d_yylval.sval.length = obstack_object_size (&name_obstack);
+
+ current.token = classify_name (pstate,
+ pstate->expression_context_block);
+ current.value = d_yylval;
+
+ /* We keep going until we find a TYPENAME. */
+ if (current.token == TYPENAME)
+ {
+ /* Install it as the first token in the FIFO. */
+ token_fifo[0] = current;
+ token_fifo.erase (token_fifo.begin () + 1,
+ token_fifo.begin () + next_to_examine);
+ break;
+ }
+ }
+ else if (next.token == '.' && !last_was_dot)
+ last_was_dot = 1;
+ else
+ {
+ /* We've reached the end of the name. */
+ break;
+ }
+ }
+
+ /* Reset our current token back to the start, if we found nothing
+ this means that we will just jump to do pop. */
+ current = token_fifo[0];
+ next_to_examine = 1;
+ }
+ if (current.token != TYPENAME && current.token != '.')
+ goto do_pop;
+
+ name_obstack.clear ();
+ checkpoint = 0;
+ if (current.token == '.')
+ search_block = NULL;
+ else
+ {
+ gdb_assert (current.token == TYPENAME);
+ search_block = pstate->expression_context_block;
+ obstack_grow (&name_obstack, current.value.sval.ptr,
+ current.value.sval.length);
+ context_type = current.value.tsym.type;
+ checkpoint = 1;
+ }
+
+ last_was_dot = current.token == '.';
+
+ while (next_to_examine <= last_to_examine)
+ {
+ d_token_and_value next;
+
+ next = token_fifo[next_to_examine];
+ ++next_to_examine;
+
+ if (next.token == IDENTIFIER && last_was_dot)
+ {
+ int classification;
+
+ d_yylval = next.value;
+ classification = classify_inner_name (pstate, search_block,
+ context_type);
+ /* We keep going until we either run out of names, or until
+ we have a qualified name which is not a type. */
+ if (classification != TYPENAME && classification != IDENTIFIER)
+ break;
+
+ /* Accept up to this token. */
+ checkpoint = next_to_examine;
+
+ /* Update the partial name we are constructing. */
+ if (context_type != NULL)
+ {
+ /* We don't want to put a leading "." into the name. */
+ obstack_grow_str (&name_obstack, ".");
+ }
+ obstack_grow (&name_obstack, next.value.sval.ptr,
+ next.value.sval.length);
+
+ d_yylval.sval.ptr = (char *) obstack_base (&name_obstack);
+ d_yylval.sval.length = obstack_object_size (&name_obstack);
+ current.value = d_yylval;
+ current.token = classification;
+
+ last_was_dot = 0;
+
+ if (classification == IDENTIFIER)
+ break;
+
+ context_type = d_yylval.tsym.type;
+ }
+ else if (next.token == '.' && !last_was_dot)
+ last_was_dot = 1;
+ else
+ {
+ /* We've reached the end of the name. */
+ break;
+ }
+ }
+
+ /* If we have a replacement token, install it as the first token in
+ the FIFO, and delete the other constituent tokens. */
+ if (checkpoint > 0)
+ {
+ token_fifo[0] = current;
+ if (checkpoint > 1)
+ token_fifo.erase (token_fifo.begin () + 1,
+ token_fifo.begin () + checkpoint);
+ }
+
+ do_pop:
+ current = token_fifo[0];
+ token_fifo.erase (token_fifo.begin ());
+ d_yylval = current.value;
+ return current.token;
+}
+
+/* See d-exp-parser.h. */
+
+void
+d_yyerror (const char *msg)
+{
+ pstate->parse_error (msg);
+}
+
+} /* namespace d_exp_parser */
+
+/* See d-exp-parser.h. */
+
+int
+d_parse (struct parser_state *par_state)
+{
+ using namespace d_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate);
+ gdb_assert (par_state != NULL);
+ pstate = par_state;
+
+ scoped_restore restore_yydebug = make_scoped_restore (&d_yydebug,
+ par_state->debug);
+
+ struct type_stack stack;
+ scoped_restore restore_type_stack
+ = make_scoped_restore (&d_exp_parser::type_stack, &stack);
+
+ /* Initialize some state used by the lexer. */
+ last_was_structop = 0;
+ saw_name_at_eof = 0;
+ paren_depth = 0;
+
+ token_fifo.clear ();
+ popping = 0;
+ name_obstack.clear ();
+
+ int result = d_yyparse ();
+ if (!result)
+ pstate->set_operation (pstate->pop ());
+ return result;
+}
diff --git a/gdb/d-exp-parser.h b/gdb/d-exp-parser.h
new file mode 100644
index 000000000000..d73575eb83b9
--- /dev/null
+++ b/gdb/d-exp-parser.h
@@ -0,0 +1,83 @@
+/* Support code for the D expression parser, for GDB.
+
+ Copyright (C) 2014-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_D_EXP_PARSER_H
+#define GDB_D_EXP_PARSER_H
+
+#include "parser-defs.h"
+#include "type-stack.h"
+#include "d-lang.h"
+
+union d_exp_parser_YYSTYPE;
+
+namespace d_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+ expression. */
+
+extern parser_state *pstate;
+
+/* The current type stack. */
+
+extern struct type_stack *type_stack;
+
+/* Return the D type table for the architecture associated to PS. */
+
+static inline const struct builtin_d_type *
+parse_d_type (parser_state *ps)
+{
+ return builtin_d_type (ps->gdbarch ());
+}
+
+/* Return true if the type is aggregate-like. */
+
+int type_aggregate_p (struct type *type);
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+/*** Needs some error checking for the float case ***/
+
+int parse_number (struct parser_state *ps, const char *p, int len,
+ int parsed_float, d_exp_parser_YYSTYPE *putithere);
+
+/* The outer level of a two-level lexer. This calls the inner lexer
+ to return tokens. It then either returns these tokens, or
+ aggregates them into a larger token. This lets us work around a
+ problem in our parsing approach, where the parser could not
+ distinguish between qualified names and qualified types at the
+ right point. */
+
+int d_yylex ();
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void d_yyerror (const char *msg);
+
+} /* namespace d_exp_parser */
+
+/* Parse a D expression using the lexer input and context held in
+ PAR_STATE. On success, return 0 and leave the resulting operation
+ set on PAR_STATE. On failure, return non-zero. */
+
+int d_parse (struct parser_state *par_state);
+
+#endif /* GDB_D_EXP_PARSER_H */
diff --git a/gdb/d-exp-parser.y b/gdb/d-exp-parser.y
index c35d78b83140..a83b1aaa4ec4 100644
--- a/gdb/d-exp-parser.y
+++ b/gdb/d-exp-parser.y
@@ -42,41 +42,20 @@
#include "value.h"
#include "parser-defs.h"
#include "language.h"
-#include "c-lang.h"
-#include "c-exp-parser.h"
#include "d-lang.h"
-#include "charset.h"
+#include "d-exp-parser.h"
#include "block.h"
#include "type-stack.h"
#include "expop.h"
#include "cli/cli-style.h"
-/* The state of the parser, used internally when we are parsing the
- expression. */
-
-static struct parser_state *pstate = NULL;
-
-/* The current type stack. */
-static struct type_stack *type_stack;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
-
-static int type_aggregate_p (struct type *);
-
-/* Return the D type table for the architecture associated to PS. */
-
-static inline const struct builtin_d_type *
-parse_d_type (parser_state *ps)
-{
- return builtin_d_type (ps->gdbarch ());
-}
-
+using namespace d_exp_parser;
using namespace expr;
+/* Bring the d_exp_parser::type_stack global into this scope, so that it hides
+ the struct type_stack type name. */
+using d_exp_parser::type_stack;
+
%}
/* Although the yacc "value" of an expression is not used,
@@ -105,12 +84,6 @@ using namespace expr;
struct stoken_vector svec;
}
-%{
-/* YYSTYPE gets defined by %union */
-static int parse_number (struct parser_state *, const char *,
- int, int, YYSTYPE *);
-%}
-
%token <sval> IDENTIFIER UNKNOWN_NAME
%token <tsym> TYPENAME
%token <voidval> COMPLETE
@@ -625,1007 +598,3 @@ BasicType:
TYPENAME
{ $$ = $1.type; }
;
-
-%%
-
-/* Return true if the type is aggregate-like. */
-
-static int
-type_aggregate_p (struct type *type)
-{
- return (type->code () == TYPE_CODE_STRUCT
- || type->code () == TYPE_CODE_UNION
- || type->code () == TYPE_CODE_MODULE
- || (type->code () == TYPE_CODE_ENUM
- && type->is_declared_class ()));
-}
-
-/* Take care of parsing a number (anything that starts with a digit).
- Set yylval and return the token type; update lexptr.
- LEN is the number of characters in it. */
-
-/*** Needs some error checking for the float case ***/
-
-static int
-parse_number (struct parser_state *ps, const char *p,
- int len, int parsed_float, YYSTYPE *putithere)
-{
- ULONGEST n = 0;
- ULONGEST prevn = 0;
- ULONGEST un;
-
- int i = 0;
- int c;
- int base = input_radix;
- int unsigned_p = 0;
- int long_p = 0;
-
- /* We have found a "L" or "U" suffix. */
- int found_suffix = 0;
-
- ULONGEST high_bit;
- struct type *signed_type;
- struct type *unsigned_type;
-
- if (parsed_float)
- {
- char *s, *sp;
-
- /* Strip out all embedded '_' before passing to parse_float. */
- s = (char *) alloca (len + 1);
- sp = s;
- while (len-- > 0)
- {
- if (*p != '_')
- *sp++ = *p;
- p++;
- }
- *sp = '\0';
- len = strlen (s);
-
- /* Check suffix for `i' , `fi' or `li' (idouble, ifloat or ireal). */
- if (len >= 1 && c_tolower (s[len - 1]) == 'i')
- {
- if (len >= 2 && c_tolower (s[len - 2]) == 'f')
- {
- putithere->typed_val_float.type
- = parse_d_type (ps)->builtin_ifloat;
- len -= 2;
- }
- else if (len >= 2 && c_tolower (s[len - 2]) == 'l')
- {
- putithere->typed_val_float.type
- = parse_d_type (ps)->builtin_ireal;
- len -= 2;
- }
- else
- {
- putithere->typed_val_float.type
- = parse_d_type (ps)->builtin_idouble;
- len -= 1;
- }
- }
- /* Check suffix for `f' or `l'' (float or real). */
- else if (len >= 1 && c_tolower (s[len - 1]) == 'f')
- {
- putithere->typed_val_float.type
- = parse_d_type (ps)->builtin_float;
- len -= 1;
- }
- else if (len >= 1 && c_tolower (s[len - 1]) == 'l')
- {
- putithere->typed_val_float.type
- = parse_d_type (ps)->builtin_real;
- len -= 1;
- }
- /* Default type if no suffix. */
- else
- {
- putithere->typed_val_float.type
- = parse_d_type (ps)->builtin_double;
- }
-
- if (!parse_float (s, len,
- putithere->typed_val_float.type,
- putithere->typed_val_float.val))
- return ERROR;
-
- return FLOAT_LITERAL;
- }
-
- /* Handle base-switching prefixes 0x, 0b, 0 */
- if (p[0] == '0')
- switch (p[1])
- {
- case 'x':
- case 'X':
- if (len >= 3)
- {
- p += 2;
- base = 16;
- len -= 2;
- }
- break;
-
- case 'b':
- case 'B':
- if (len >= 3)
- {
- p += 2;
- base = 2;
- len -= 2;
- }
- break;
-
- default:
- base = 8;
- break;
- }
-
- while (len-- > 0)
- {
- c = *p++;
- if (c == '_')
- continue; /* Ignore embedded '_'. */
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- if (c != 'l' && c != 'u')
- n *= base;
- if (c >= '0' && c <= '9')
- {
- if (found_suffix)
- return ERROR;
- n += i = c - '0';
- }
- else
- {
- if (base > 10 && c >= 'a' && c <= 'f')
- {
- if (found_suffix)
- return ERROR;
- n += i = c - 'a' + 10;
- }
- else if (c == 'l' && long_p == 0)
- {
- long_p = 1;
- found_suffix = 1;
- }
- else if (c == 'u' && unsigned_p == 0)
- {
- unsigned_p = 1;
- found_suffix = 1;
- }
- else
- return ERROR; /* Char not a digit */
- }
- if (i >= base)
- return ERROR; /* Invalid digit in this base. */
- /* Portably test for integer overflow. */
- if (c != 'l' && c != 'u')
- {
- ULONGEST n2 = prevn * base;
- if ((n2 / base != prevn) || (n2 + i < prevn))
- error (_("Numeric constant too large."));
- }
- prevn = n;
- }
-
- /* An integer constant is an int or a long. An L suffix forces it to
- be long, and a U suffix forces it to be unsigned. To figure out
- whether it fits, we shift it right and see whether anything remains.
- Note that we can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or
- more in one operation, because many compilers will warn about such a
- shift (which always produces a zero result). To deal with the case
- where it is we just always shift the value more than once, with fewer
- bits each time. */
- un = (ULONGEST) n >> 2;
- if (long_p == 0 && (un >> 30) == 0)
- {
- high_bit = ((ULONGEST) 1) << 31;
- signed_type = parse_d_type (ps)->builtin_int;
- /* For decimal notation, keep the sign of the worked out type. */
- if (base == 10 && !unsigned_p)
- unsigned_type = parse_d_type (ps)->builtin_long;
- else
- unsigned_type = parse_d_type (ps)->builtin_uint;
- }
- else
- {
- int shift;
- if (sizeof (ULONGEST) * HOST_CHAR_BIT < 64)
- /* A long long does not fit in a LONGEST. */
- shift = (sizeof (ULONGEST) * HOST_CHAR_BIT - 1);
- else
- shift = 63;
- high_bit = (ULONGEST) 1 << shift;
- signed_type = parse_d_type (ps)->builtin_long;
- unsigned_type = parse_d_type (ps)->builtin_ulong;
- }
-
- putithere->typed_val_int.val = n;
-
- /* If the high bit of the worked out type is set then this number
- has to be unsigned_type. */
- if (unsigned_p || (n & high_bit))
- putithere->typed_val_int.type = unsigned_type;
- else
- putithere->typed_val_int.type = signed_type;
-
- return INTEGER_LITERAL;
-}
-
-/* Temporary obstack used for holding strings. */
-static struct obstack tempbuf;
-static int tempbuf_init;
-
-/* Parse a string or character literal from TOKPTR. The string or
- character may be wide or unicode. *OUTPTR is set to just after the
- end of the literal in the input string. The resulting token is
- stored in VALUE. This returns a token value, either STRING or
- CHAR, depending on what was parsed. *HOST_CHARS is set to the
- number of host characters in the literal. */
-
-static int
-parse_string_or_char (const char *tokptr, const char **outptr,
- struct typed_stoken *value, int *host_chars)
-{
- int quote;
-
- /* Build the gdb internal form of the input string in tempbuf. Note
- that the buffer is null byte terminated *only* for the
- convenience of debugging gdb itself and printing the buffer
- contents when the buffer contains no embedded nulls. Gdb does
- not depend upon the buffer being null byte terminated, it uses
- the length string instead. This allows gdb to handle C strings
- (as well as strings in other languages) with embedded null
- bytes */
-
- if (!tempbuf_init)
- tempbuf_init = 1;
- else
- obstack_free (&tempbuf, NULL);
- obstack_init (&tempbuf);
-
- /* Skip the quote. */
- quote = *tokptr;
- ++tokptr;
-
- *host_chars = 0;
-
- while (*tokptr)
- {
- char c = *tokptr;
- if (c == '\\')
- {
- ++tokptr;
- *host_chars += c_parse_escape (&tokptr, &tempbuf);
- }
- else if (c == quote)
- break;
- else
- {
- obstack_1grow (&tempbuf, c);
- ++tokptr;
- /* FIXME: this does the wrong thing with multi-byte host
- characters. We could use mbrlen here, but that would
- make "set host-charset" a bit less useful. */
- ++*host_chars;
- }
- }
-
- if (*tokptr != quote)
- {
- if (quote == '"' || quote == '`')
- error (_("Unterminated string in expression."));
- else
- error (_("Unmatched single quote."));
- }
- ++tokptr;
-
- /* FIXME: should instead use own language string_type enum
- and handle D-specific string suffixes here. */
- if (quote == '\'')
- value->type = C_CHAR;
- else
- value->type = C_STRING;
-
- value->ptr = (char *) obstack_base (&tempbuf);
- value->length = obstack_object_size (&tempbuf);
-
- *outptr = tokptr;
-
- return quote == '\'' ? CHARACTER_LITERAL : STRING_LITERAL;
-}
-
-struct d_token
-{
- const char *oper;
- int token;
- enum exp_opcode opcode;
-};
-
-static const struct d_token tokentab3[] =
- {
- {"^^=", ASSIGN_MODIFY, BINOP_EXP},
- {"<<=", ASSIGN_MODIFY, BINOP_LSH},
- {">>=", ASSIGN_MODIFY, BINOP_RSH},
- };
-
-static const struct d_token tokentab2[] =
- {
- {"+=", ASSIGN_MODIFY, BINOP_ADD},
- {"-=", ASSIGN_MODIFY, BINOP_SUB},
- {"*=", ASSIGN_MODIFY, BINOP_MUL},
- {"/=", ASSIGN_MODIFY, BINOP_DIV},
- {"%=", ASSIGN_MODIFY, BINOP_REM},
- {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
- {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
- {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
- {"++", INCREMENT, OP_NULL},
- {"--", DECREMENT, OP_NULL},
- {"&&", ANDAND, OP_NULL},
- {"||", OROR, OP_NULL},
- {"^^", HATHAT, OP_NULL},
- {"<<", LSH, OP_NULL},
- {">>", RSH, OP_NULL},
- {"==", EQUAL, OP_NULL},
- {"!=", NOTEQUAL, OP_NULL},
- {"<=", LEQ, OP_NULL},
- {">=", GEQ, OP_NULL},
- {"..", DOTDOT, OP_NULL},
- };
-
-/* Identifier-like tokens. */
-static const struct d_token ident_tokens[] =
- {
- {"is", IDENTITY, OP_NULL},
- {"!is", NOTIDENTITY, OP_NULL},
-
- {"cast", CAST_KEYWORD, OP_NULL},
- {"const", CONST_KEYWORD, OP_NULL},
- {"immutable", IMMUTABLE_KEYWORD, OP_NULL},
- {"shared", SHARED_KEYWORD, OP_NULL},
- {"super", SUPER_KEYWORD, OP_NULL},
-
- {"null", NULL_KEYWORD, OP_NULL},
- {"true", TRUE_KEYWORD, OP_NULL},
- {"false", FALSE_KEYWORD, OP_NULL},
-
- {"init", INIT_KEYWORD, OP_NULL},
- {"sizeof", SIZEOF_KEYWORD, OP_NULL},
- {"typeof", TYPEOF_KEYWORD, OP_NULL},
- {"typeid", TYPEID_KEYWORD, OP_NULL},
-
- {"delegate", DELEGATE_KEYWORD, OP_NULL},
- {"function", FUNCTION_KEYWORD, OP_NULL},
- {"struct", STRUCT_KEYWORD, OP_NULL},
- {"union", UNION_KEYWORD, OP_NULL},
- {"class", CLASS_KEYWORD, OP_NULL},
- {"interface", INTERFACE_KEYWORD, OP_NULL},
- {"enum", ENUM_KEYWORD, OP_NULL},
- {"template", TEMPLATE_KEYWORD, OP_NULL},
- };
-
-/* This is set if a NAME token appeared at the very end of the input
- string, with no whitespace separating the name from the EOF. This
- is used only when parsing to do field name completion. */
-static int saw_name_at_eof;
-
-/* This is set if the previously-returned token was a structure operator.
- This is used only when parsing to do field name completion. */
-static int last_was_structop;
-
-/* Depth of parentheses. */
-static int paren_depth;
-
-/* Read one token, getting characters through lexptr. */
-
-static int
-lex_one_token (struct parser_state *par_state)
-{
- int c;
- int namelen;
- const char *tokstart;
- int saw_structop = last_was_structop;
-
- last_was_structop = 0;
-
- retry:
-
- pstate->prev_lexptr = pstate->lexptr;
-
- tokstart = pstate->lexptr;
- /* See if it is a special token of length 3. */
- for (const auto &token : tokentab3)
- if (strncmp (tokstart, token.oper, 3) == 0)
- {
- pstate->lexptr += 3;
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- /* See if it is a special token of length 2. */
- for (const auto &token : tokentab2)
- if (strncmp (tokstart, token.oper, 2) == 0)
- {
- pstate->lexptr += 2;
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- switch (c = *tokstart)
- {
- case 0:
- /* If we're parsing for field name completion, and the previous
- token allows such completion, return a COMPLETE token.
- Otherwise, we were already scanning the original text, and
- we're really done. */
- if (saw_name_at_eof)
- {
- saw_name_at_eof = 0;
- return COMPLETE;
- }
- else if (saw_structop)
- return COMPLETE;
- else
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- pstate->lexptr++;
- goto retry;
-
- case '[':
- case '(':
- paren_depth++;
- pstate->lexptr++;
- return c;
-
- case ']':
- case ')':
- if (paren_depth == 0)
- return 0;
- paren_depth--;
- pstate->lexptr++;
- return c;
-
- case ',':
- if (pstate->comma_terminates && paren_depth == 0)
- return 0;
- pstate->lexptr++;
- return c;
-
- case '.':
- /* Might be a floating point number. */
- if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
- {
- if (pstate->parse_completion)
- last_was_structop = 1;
- goto symbol; /* Nope, must be a symbol. */
- }
- [[fallthrough]];
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- /* It's a number. */
- int got_dot = 0, got_e = 0, toktype;
- const char *p = tokstart;
- int hex = input_radix > 10;
-
- if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
- {
- p += 2;
- hex = 1;
- }
-
- for (;; ++p)
- {
- /* Hex exponents start with 'p', because 'e' is a valid hex
- digit and thus does not indicate a floating point number
- when the radix is hex. */
- if ((!hex && !got_e && c_tolower (p[0]) == 'e')
- || (hex && !got_e && c_tolower (p[0] == 'p')))
- got_dot = got_e = 1;
- /* A '.' always indicates a decimal floating point number
- regardless of the radix. If we have a '..' then its the
- end of the number and the beginning of a slice. */
- else if (!got_dot && (p[0] == '.' && p[1] != '.'))
- got_dot = 1;
- /* This is the sign of the exponent, not the end of the number. */
- else if (got_e && (c_tolower (p[-1]) == 'e'
- || c_tolower (p[-1]) == 'p')
- && (*p == '-' || *p == '+'))
- continue;
- /* We will take any letters or digits, ignoring any embedded '_'.
- parse_number will complain if past the radix, or if L or U are
- not final. */
- else if ((*p < '0' || *p > '9') && (*p != '_')
- && ((*p < 'a' || *p > 'z') && (*p < 'A' || *p > 'Z')))
- break;
- }
-
- toktype = parse_number (par_state, tokstart, p - tokstart,
- got_dot|got_e, &yylval);
- if (toktype == ERROR)
- error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
- tokstart);
- pstate->lexptr = p;
- return toktype;
- }
-
- case '@':
- {
- const char *p = &tokstart[1];
- size_t len = strlen ("entry");
-
- while (c_isspace (*p))
- p++;
- if (strncmp (p, "entry", len) == 0 && !c_isalnum (p[len])
- && p[len] != '_')
- {
- pstate->lexptr = &p[len];
- return ENTRY;
- }
- }
- [[fallthrough]];
- case '+':
- case '-':
- case '*':
- case '/':
- case '%':
- case '|':
- case '&':
- case '^':
- case '~':
- case '!':
- case '<':
- case '>':
- case '?':
- case ':':
- case '=':
- case '{':
- case '}':
- symbol:
- pstate->lexptr++;
- return c;
-
- case '\'':
- case '"':
- case '`':
- {
- int host_len;
- int result = parse_string_or_char (tokstart, &pstate->lexptr,
- &yylval.tsval, &host_len);
- if (result == CHARACTER_LITERAL)
- {
- if (host_len == 0)
- error (_("Empty character constant."));
- else if (host_len > 2 && c == '\'')
- {
- ++tokstart;
- namelen = pstate->lexptr - tokstart - 1;
- goto tryname;
- }
- else if (host_len > 1)
- error (_("Invalid character constant."));
- }
- return result;
- }
- }
-
- if (!(c == '_' || c == '$'
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
- /* We must have come across a bad character (e.g. ';'). */
- error (_("Invalid character '%c' in expression"), c);
-
- /* It's a name. See how long it is. */
- namelen = 0;
- for (c = tokstart[namelen];
- (c == '_' || c == '$' || (c >= '0' && c <= '9')
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));)
- c = tokstart[++namelen];
-
- /* The token "if" terminates the expression and is NOT
- removed from the input stream. */
- if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
- return 0;
-
- /* For the same reason (breakpoint conditions), "thread N"
- terminates the expression. "thread" could be an identifier, but
- an identifier is never followed by a number without intervening
- punctuation. "task" is similar. Handle abbreviations of these,
- similarly to breakpoint.c:find_condition_and_thread. */
- if (namelen >= 1
- && (strncmp (tokstart, "thread", namelen) == 0
- || strncmp (tokstart, "task", namelen) == 0)
- && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t'))
- {
- const char *p = skip_spaces (tokstart + namelen + 1);
- if (*p >= '0' && *p <= '9')
- return 0;
- }
-
- pstate->lexptr += namelen;
-
- tryname:
-
- yylval.sval.ptr = tokstart;
- yylval.sval.length = namelen;
-
- /* Catch specific keywords. */
- std::string copy = copy_name (yylval.sval);
- for (const auto &token : ident_tokens)
- if (copy == token.oper)
- {
- /* It is ok to always set this, even though we don't always
- strictly need to. */
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- if (*tokstart == '$')
- return DOLLAR_VARIABLE;
-
- yylval.tsym.type
- = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (), copy.c_str ());
- if (yylval.tsym.type != NULL)
- return TYPENAME;
-
- /* Input names that aren't symbols but ARE valid hex numbers,
- when the input radix permits them, can be names or numbers
- depending on the parse. Note we support radixes > 16 here. */
- if ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
- || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10))
- {
- YYSTYPE newlval; /* Its value is ignored. */
- int hextype = parse_number (par_state, tokstart, namelen, 0, &newlval);
- if (hextype == INTEGER_LITERAL)
- return NAME_OR_INT;
- }
-
- if (pstate->parse_completion && *pstate->lexptr == '\0')
- saw_name_at_eof = 1;
-
- return IDENTIFIER;
-}
-
-/* An object of this type is pushed on a FIFO by the "outer" lexer. */
-struct d_token_and_value
-{
- int token;
- YYSTYPE value;
-};
-
-
-/* A FIFO of tokens that have been read but not yet returned to the
- parser. */
-static std::vector<d_token_and_value> token_fifo;
-
-/* Non-zero if the lexer should return tokens from the FIFO. */
-static int popping;
-
-/* Temporary storage for yylex; this holds symbol names as they are
- built up. */
-static auto_obstack name_obstack;
-
-/* Classify an IDENTIFIER token. The contents of the token are in `yylval'.
- Updates yylval and returns the new token type. BLOCK is the block
- in which lookups start; this can be NULL to mean the global scope. */
-
-static int
-classify_name (struct parser_state *par_state, const struct block *block)
-{
- struct block_symbol sym;
- struct field_of_this_result is_a_field_of_this;
-
- std::string copy = copy_name (yylval.sval);
-
- sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
- if (sym.symbol && sym.symbol->loc_class () == LOC_TYPEDEF)
- {
- yylval.tsym.type = sym.symbol->type ();
- return TYPENAME;
- }
- else if (sym.symbol == NULL)
- {
- /* Look-up first for a module name, then a type. */
- sym = lookup_symbol (copy.c_str (), block, SEARCH_MODULE_DOMAIN,
- nullptr);
- if (sym.symbol == NULL)
- sym = lookup_symbol (copy.c_str (), block, SEARCH_STRUCT_DOMAIN,
- nullptr);
-
- if (sym.symbol != NULL)
- {
- yylval.tsym.type = sym.symbol->type ();
- return TYPENAME;
- }
-
- return UNKNOWN_NAME;
- }
-
- return IDENTIFIER;
-}
-
-/* Like classify_name, but used by the inner loop of the lexer, when a
- name might have already been seen. CONTEXT is the context type, or
- NULL if this is the first component of a name. */
-
-static int
-classify_inner_name (struct parser_state *par_state,
- const struct block *block, struct type *context)
-{
- struct type *type;
-
- if (context == NULL)
- return classify_name (par_state, block);
-
- type = check_typedef (context);
- if (!type_aggregate_p (type))
- return ERROR;
-
- std::string copy = copy_name (yylval.ssym.stoken);
- yylval.ssym.sym = d_lookup_nested_symbol (type, copy.c_str (), block);
-
- if (yylval.ssym.sym.symbol == NULL)
- return ERROR;
-
- if (yylval.ssym.sym.symbol->loc_class () == LOC_TYPEDEF)
- {
- yylval.tsym.type = yylval.ssym.sym.symbol->type ();
- return TYPENAME;
- }
-
- return IDENTIFIER;
-}
-
-/* The outer level of a two-level lexer. This calls the inner lexer
- to return tokens. It then either returns these tokens, or
- aggregates them into a larger token. This lets us work around a
- problem in our parsing approach, where the parser could not
- distinguish between qualified names and qualified types at the
- right point. */
-
-static int
-yylex (void)
-{
- d_token_and_value current;
- int last_was_dot;
- struct type *context_type = NULL;
- int last_to_examine, next_to_examine, checkpoint;
- const struct block *search_block;
-
- if (popping && !token_fifo.empty ())
- goto do_pop;
- popping = 0;
-
- /* Read the first token and decide what to do. */
- current.token = lex_one_token (pstate);
- if (current.token != IDENTIFIER && current.token != '.')
- return current.token;
-
- /* Read any sequence of alternating "." and identifier tokens into
- the token FIFO. */
- current.value = yylval;
- token_fifo.push_back (current);
- last_was_dot = current.token == '.';
-
- while (1)
- {
- current.token = lex_one_token (pstate);
- current.value = yylval;
- token_fifo.push_back (current);
-
- if ((last_was_dot && current.token != IDENTIFIER)
- || (!last_was_dot && current.token != '.'))
- break;
-
- last_was_dot = !last_was_dot;
- }
- popping = 1;
-
- /* We always read one extra token, so compute the number of tokens
- to examine accordingly. */
- last_to_examine = token_fifo.size () - 2;
- next_to_examine = 0;
-
- current = token_fifo[next_to_examine];
- ++next_to_examine;
-
- /* If we are not dealing with a typename, now is the time to find out. */
- if (current.token == IDENTIFIER)
- {
- yylval = current.value;
- current.token = classify_name (pstate, pstate->expression_context_block);
- current.value = yylval;
- }
-
- /* If the IDENTIFIER is not known, it could be a package symbol,
- first try building up a name until we find the qualified module. */
- if (current.token == UNKNOWN_NAME)
- {
- name_obstack.clear ();
- obstack_grow (&name_obstack, current.value.sval.ptr,
- current.value.sval.length);
-
- last_was_dot = 0;
-
- while (next_to_examine <= last_to_examine)
- {
- d_token_and_value next;
-
- next = token_fifo[next_to_examine];
- ++next_to_examine;
-
- if (next.token == IDENTIFIER && last_was_dot)
- {
- /* Update the partial name we are constructing. */
- obstack_grow_str (&name_obstack, ".");
- obstack_grow (&name_obstack, next.value.sval.ptr,
- next.value.sval.length);
-
- yylval.sval.ptr = (char *) obstack_base (&name_obstack);
- yylval.sval.length = obstack_object_size (&name_obstack);
-
- current.token = classify_name (pstate,
- pstate->expression_context_block);
- current.value = yylval;
-
- /* We keep going until we find a TYPENAME. */
- if (current.token == TYPENAME)
- {
- /* Install it as the first token in the FIFO. */
- token_fifo[0] = current;
- token_fifo.erase (token_fifo.begin () + 1,
- token_fifo.begin () + next_to_examine);
- break;
- }
- }
- else if (next.token == '.' && !last_was_dot)
- last_was_dot = 1;
- else
- {
- /* We've reached the end of the name. */
- break;
- }
- }
-
- /* Reset our current token back to the start, if we found nothing
- this means that we will just jump to do pop. */
- current = token_fifo[0];
- next_to_examine = 1;
- }
- if (current.token != TYPENAME && current.token != '.')
- goto do_pop;
-
- name_obstack.clear ();
- checkpoint = 0;
- if (current.token == '.')
- search_block = NULL;
- else
- {
- gdb_assert (current.token == TYPENAME);
- search_block = pstate->expression_context_block;
- obstack_grow (&name_obstack, current.value.sval.ptr,
- current.value.sval.length);
- context_type = current.value.tsym.type;
- checkpoint = 1;
- }
-
- last_was_dot = current.token == '.';
-
- while (next_to_examine <= last_to_examine)
- {
- d_token_and_value next;
-
- next = token_fifo[next_to_examine];
- ++next_to_examine;
-
- if (next.token == IDENTIFIER && last_was_dot)
- {
- int classification;
-
- yylval = next.value;
- classification = classify_inner_name (pstate, search_block,
- context_type);
- /* We keep going until we either run out of names, or until
- we have a qualified name which is not a type. */
- if (classification != TYPENAME && classification != IDENTIFIER)
- break;
-
- /* Accept up to this token. */
- checkpoint = next_to_examine;
-
- /* Update the partial name we are constructing. */
- if (context_type != NULL)
- {
- /* We don't want to put a leading "." into the name. */
- obstack_grow_str (&name_obstack, ".");
- }
- obstack_grow (&name_obstack, next.value.sval.ptr,
- next.value.sval.length);
-
- yylval.sval.ptr = (char *) obstack_base (&name_obstack);
- yylval.sval.length = obstack_object_size (&name_obstack);
- current.value = yylval;
- current.token = classification;
-
- last_was_dot = 0;
-
- if (classification == IDENTIFIER)
- break;
-
- context_type = yylval.tsym.type;
- }
- else if (next.token == '.' && !last_was_dot)
- last_was_dot = 1;
- else
- {
- /* We've reached the end of the name. */
- break;
- }
- }
-
- /* If we have a replacement token, install it as the first token in
- the FIFO, and delete the other constituent tokens. */
- if (checkpoint > 0)
- {
- token_fifo[0] = current;
- if (checkpoint > 1)
- token_fifo.erase (token_fifo.begin () + 1,
- token_fifo.begin () + checkpoint);
- }
-
- do_pop:
- current = token_fifo[0];
- token_fifo.erase (token_fifo.begin ());
- yylval = current.value;
- return current.token;
-}
-
-int
-d_parse (struct parser_state *par_state)
-{
- /* Setting up the parser state. */
- scoped_restore pstate_restore = make_scoped_restore (&pstate);
- gdb_assert (par_state != NULL);
- pstate = par_state;
-
- scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
- par_state->debug);
-
- struct type_stack stack;
- scoped_restore restore_type_stack = make_scoped_restore (&type_stack,
- &stack);
-
- /* Initialize some state used by the lexer. */
- last_was_structop = 0;
- saw_name_at_eof = 0;
- paren_depth = 0;
-
- token_fifo.clear ();
- popping = 0;
- name_obstack.clear ();
-
- int result = yyparse ();
- if (!result)
- pstate->set_operation (pstate->pop ());
- return result;
-}
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index dc1f41512a26..4793eb8826ed 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -21,6 +21,7 @@
#include "language.h"
#include "varobj.h"
#include "d-lang.h"
+#include "d-exp-parser.h"
#include "c-lang.h"
#include "demangle.h"
#include "cp-support.h"
diff --git a/gdb/d-lang.h b/gdb/d-lang.h
index e8752ac95306..9d424c06f223 100644
--- a/gdb/d-lang.h
+++ b/gdb/d-lang.h
@@ -54,10 +54,6 @@ struct builtin_d_type
struct type *builtin_dchar = nullptr;
};
-/* Defined in d-exp-parser.y. */
-
-extern int d_parse (struct parser_state *);
-
/* Defined in d-lang.c */
extern const char *d_main_name (void);
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 15/19] gdb: move f-exp-parser.y's support code to f-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (13 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 14/19] gdb: move d-exp-parser.y's support code to d-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 16/19] gdb: move go-exp-parser.y's support code to go-exp-parser.c simon.marchi
` (3 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Similar to the previous commits, but for the Fortran expression parser.
Since the Fortran parser is entered through the f_language::parser method
rather than a free function, add a free function f_parse as the entry point
(like the other parsers) and turn f_language::parser into a thin wrapper
around it, defined in f-lang.c. This keeps things more consistent.
Like the D parser, the Fortran parser keeps a type_stack global whose name
clashes with the struct type_stack type once it lives in the namespace and
is brought in with a using-directive. A using-declaration for it in the .y
prologue restores the original name hiding, so the grammar actions can keep
referring to it unqualified.
The Fortran support code uses malloc, realloc and free directly. These used
to be rewritten to their x-variants by post-process-parser-output.sh when the
code was part of the generated parser. Replace them with their x-variants
in the moved code.
Put the parser support code inside the f_exp_parser namespace.
Change-Id: Ic682bf04ff4058223d0b174effc427f5b22eeb1e
---
gdb/Makefile.in | 2 +
gdb/f-exp-parser.c | 989 ++++++++++++++++++++++++++++++++++++++++++++
gdb/f-exp-parser.h | 102 +++++
gdb/f-exp-parser.y | 995 +--------------------------------------------
gdb/f-lang.c | 9 +
5 files changed, 1108 insertions(+), 989 deletions(-)
create mode 100644 gdb/f-exp-parser.c
create mode 100644 gdb/f-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f861b1f53261..0a35f9507def 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1103,6 +1103,7 @@ COMMON_SFILES = \
expanded-symbol.c \
expprint.c \
extension.c \
+ f-exp-parser.c \
f-lang.c \
f-typeprint.c \
f-valprint.c \
@@ -1455,6 +1456,7 @@ HFILES_NO_SRCDIR = \
filesystem.h \
find-memory-region.h \
finish-thread-state.h \
+ f-exp-parser.h \
f-lang.h \
frame-base.h \
frame.h \
diff --git a/gdb/f-exp-parser.c b/gdb/f-exp-parser.c
new file mode 100644
index 000000000000..9e0c7aa6650c
--- /dev/null
+++ b/gdb/f-exp-parser.c
@@ -0,0 +1,989 @@
+/* YACC parser support code for Fortran expressions, 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 "f-exp-parser.h"
+#include "f-exp-parser-gen.h"
+#include "block.h"
+#include "expression.h"
+#include "f-exp.h"
+#include "language.h"
+#include "parser-defs.h"
+#include "value.h"
+#include <algorithm>
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ f-exp-parser-gen.c. Bison produces a declaration for f_yyparse in
+ f-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int f_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for f_yydebug. */
+
+extern int f_yydebug;
+
+using namespace expr;
+
+namespace f_exp_parser
+{
+
+/* See f-exp-parser.h. */
+
+parser_state *pstate;
+
+/* Depth of parentheses. */
+
+static int paren_depth;
+
+/* See f-exp-parser.h. */
+
+struct type_stack *type_stack;
+
+/* A helper that pops two operations (similar to wrap2), evaluates the last one
+ assuming it is a kind parameter, and wraps them in some other operation
+ pushing it to the stack. */
+
+template<typename T>
+static void
+fortran_wrap2_kind (type *base_type)
+{
+ operation_up kind_arg = pstate->pop ();
+ operation_up arg = pstate->pop ();
+
+ value *val = kind_arg->evaluate (nullptr, pstate->expout.get (),
+ EVAL_AVOID_SIDE_EFFECTS);
+ gdb_assert (val != nullptr);
+
+ type *follow_type = convert_to_kind_type (base_type, value_as_long (val));
+
+ pstate->push_new<T> (std::move (arg), follow_type);
+}
+
+/* A helper that pops three operations, evaluates the last one assuming it is a
+ kind parameter, and wraps them in some other operation pushing it to the
+ stack. */
+
+template<typename T>
+static void
+fortran_wrap3_kind (type *base_type)
+{
+ operation_up kind_arg = pstate->pop ();
+ operation_up arg2 = pstate->pop ();
+ operation_up arg1 = pstate->pop ();
+
+ value *val = kind_arg->evaluate (nullptr, pstate->expout.get (),
+ EVAL_AVOID_SIDE_EFFECTS);
+ gdb_assert (val != nullptr);
+
+ type *follow_type = convert_to_kind_type (base_type, value_as_long (val));
+
+ pstate->push_new<T> (std::move (arg1), std::move (arg2), follow_type);
+}
+
+/* See f-exp-parser.h. */
+
+void
+wrap_unop_intrinsic (exp_opcode code)
+{
+ switch (code)
+ {
+ case UNOP_ABS:
+ pstate->wrap<fortran_abs_operation> ();
+ break;
+ case FORTRAN_FLOOR:
+ pstate->wrap<fortran_floor_operation_1arg> ();
+ break;
+ case FORTRAN_CEILING:
+ pstate->wrap<fortran_ceil_operation_1arg> ();
+ break;
+ case UNOP_FORTRAN_ALLOCATED:
+ pstate->wrap<fortran_allocated_operation> ();
+ break;
+ case UNOP_FORTRAN_RANK:
+ pstate->wrap<fortran_rank_operation> ();
+ break;
+ case UNOP_FORTRAN_SHAPE:
+ pstate->wrap<fortran_array_shape_operation> ();
+ break;
+ case UNOP_FORTRAN_LOC:
+ pstate->wrap<fortran_loc_operation> ();
+ break;
+ case FORTRAN_ASSOCIATED:
+ pstate->wrap<fortran_associated_1arg> ();
+ break;
+ case FORTRAN_ARRAY_SIZE:
+ pstate->wrap<fortran_array_size_1arg> ();
+ break;
+ case FORTRAN_CMPLX:
+ pstate->wrap<fortran_cmplx_operation_1arg> ();
+ break;
+ case FORTRAN_LBOUND:
+ case FORTRAN_UBOUND:
+ pstate->push_new<fortran_bound_1arg> (code, pstate->pop ());
+ break;
+ default:
+ gdb_assert_not_reached ("unhandled intrinsic");
+ }
+}
+
+/* See f-exp-parser.h. */
+
+void
+wrap_binop_intrinsic (exp_opcode code)
+{
+ switch (code)
+ {
+ case FORTRAN_FLOOR:
+ fortran_wrap2_kind<fortran_floor_operation_2arg>
+ (parse_f_type (pstate)->builtin_integer);
+ break;
+ case FORTRAN_CEILING:
+ fortran_wrap2_kind<fortran_ceil_operation_2arg>
+ (parse_f_type (pstate)->builtin_integer);
+ break;
+ case BINOP_MOD:
+ pstate->wrap2<fortran_mod_operation> ();
+ break;
+ case BINOP_FORTRAN_MODULO:
+ pstate->wrap2<fortran_modulo_operation> ();
+ break;
+ case FORTRAN_CMPLX:
+ pstate->wrap2<fortran_cmplx_operation_2arg> ();
+ break;
+ case FORTRAN_ASSOCIATED:
+ pstate->wrap2<fortran_associated_2arg> ();
+ break;
+ case FORTRAN_ARRAY_SIZE:
+ pstate->wrap2<fortran_array_size_2arg> ();
+ break;
+ case FORTRAN_LBOUND:
+ case FORTRAN_UBOUND:
+ {
+ operation_up arg2 = pstate->pop ();
+ operation_up arg1 = pstate->pop ();
+ pstate->push_new<fortran_bound_2arg> (code, std::move (arg1),
+ std::move (arg2));
+ }
+ break;
+ default:
+ gdb_assert_not_reached ("unhandled intrinsic");
+ }
+}
+
+/* See f-exp-parser.h. */
+
+void
+wrap_ternop_intrinsic (exp_opcode code)
+{
+ switch (code)
+ {
+ case FORTRAN_LBOUND:
+ case FORTRAN_UBOUND:
+ {
+ operation_up kind_arg = pstate->pop ();
+ operation_up arg2 = pstate->pop ();
+ operation_up arg1 = pstate->pop ();
+
+ value *val = kind_arg->evaluate (nullptr, pstate->expout.get (),
+ EVAL_AVOID_SIDE_EFFECTS);
+ gdb_assert (val != nullptr);
+
+ type *follow_type
+ = convert_to_kind_type (parse_f_type (pstate)->builtin_integer,
+ value_as_long (val));
+
+ pstate->push_new<fortran_bound_3arg> (code, std::move (arg1),
+ std::move (arg2), follow_type);
+ }
+ break;
+ case FORTRAN_ARRAY_SIZE:
+ fortran_wrap3_kind<fortran_array_size_3arg>
+ (parse_f_type (pstate)->builtin_integer);
+ break;
+ case FORTRAN_CMPLX:
+ fortran_wrap3_kind<fortran_cmplx_operation_3arg>
+ (parse_f_type (pstate)->builtin_complex);
+ break;
+ default:
+ gdb_assert_not_reached ("unhandled intrinsic");
+ }
+}
+
+/* See f-exp-parser.h. */
+
+int
+parse_number (struct parser_state *par_state,
+ const char *p, int len, int parsed_float,
+ f_exp_parser_YYSTYPE *putithere)
+{
+ ULONGEST n = 0;
+ ULONGEST prevn = 0;
+ int c;
+ int base = input_radix;
+ int unsigned_p = 0;
+ int long_p = 0;
+ ULONGEST high_bit;
+ struct type *signed_type;
+ struct type *unsigned_type;
+
+ if (parsed_float)
+ {
+ /* It's a float since it contains a point or an exponent. */
+ /* [dD] is not understood as an exponent by parse_float,
+ change it to 'e'. */
+ char *tmp, *tmp2;
+
+ tmp = xstrdup (p);
+ for (tmp2 = tmp; *tmp2; ++tmp2)
+ if (*tmp2 == 'd' || *tmp2 == 'D')
+ *tmp2 = 'e';
+
+ /* FIXME: Should this use different types? */
+ putithere->typed_val_float.type = parse_f_type (pstate)->builtin_real_s8;
+ bool parsed = parse_float (tmp, len,
+ putithere->typed_val_float.type,
+ putithere->typed_val_float.val);
+ xfree (tmp);
+ return parsed? FLOAT : ERROR;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
+ if (p[0] == '0' && len > 1)
+ switch (p[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ case 'd':
+ case 'D':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 10;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c_isupper (c))
+ c = c_tolower (c);
+ if (len == 0 && c == 'l')
+ long_p = 1;
+ else if (len == 0 && c == 'u')
+ unsigned_p = 1;
+ else
+ {
+ int i;
+ if (c >= '0' && c <= '9')
+ i = c - '0';
+ else if (c >= 'a' && c <= 'f')
+ i = c - 'a' + 10;
+ else
+ return ERROR; /* Char not a digit */
+ if (i >= base)
+ return ERROR; /* Invalid digit in this base */
+ n *= base;
+ n += i;
+ }
+ /* Test for overflow. */
+ if (prevn == 0 && n == 0)
+ ;
+ else if (RANGE_CHECK && prevn >= n)
+ range_error (_("Overflow on numeric constant."));
+ prevn = n;
+ }
+
+ /* If the number is too big to be an int, or it's got an l suffix
+ then it's a long. Work out if this has to be a long by
+ shifting right and seeing if anything remains, and the
+ target int size is different to the target long size.
+
+ In the expression below, we could have tested
+ (n >> gdbarch_int_bit (parse_gdbarch))
+ to see if it was zero,
+ but too many compilers warn about that, when ints and longs
+ are the same size. So we shift it twice, with fewer bits
+ each time, for the same result. */
+
+ int bits_available;
+ if ((gdbarch_int_bit (par_state->gdbarch ())
+ != gdbarch_long_bit (par_state->gdbarch ())
+ && ((n >> 2)
+ >> (gdbarch_int_bit (par_state->gdbarch ())-2))) /* Avoid
+ shift warning */
+ || long_p)
+ {
+ bits_available = gdbarch_long_bit (par_state->gdbarch ());
+ unsigned_type = parse_type (par_state)->builtin_unsigned_long;
+ signed_type = parse_type (par_state)->builtin_long;
+ }
+ else
+ {
+ bits_available = gdbarch_int_bit (par_state->gdbarch ());
+ unsigned_type = parse_type (par_state)->builtin_unsigned_int;
+ signed_type = parse_type (par_state)->builtin_int;
+ }
+ high_bit = ((ULONGEST)1) << (bits_available - 1);
+
+ if (RANGE_CHECK
+ && ((n >> 2) >> (bits_available - 2)))
+ range_error (_("Overflow on numeric constant."));
+
+ putithere->typed_val.val = n;
+
+ /* If the high bit of the worked out type is set then this number
+ has to be unsigned. */
+
+ if (unsigned_p || (n & high_bit))
+ putithere->typed_val.type = unsigned_type;
+ else
+ putithere->typed_val.type = signed_type;
+
+ return INT;
+}
+
+/* See f-exp-parser.h. */
+
+void
+push_kind_type (LONGEST val, struct type *type)
+{
+ int ival;
+
+ if (type->is_unsigned ())
+ {
+ ULONGEST uval = static_cast <ULONGEST> (val);
+ if (uval > INT_MAX)
+ error (_("kind value out of range"));
+ ival = static_cast <int> (uval);
+ }
+ else
+ {
+ if (val > INT_MAX || val < 0)
+ error (_("kind value out of range"));
+ ival = static_cast <int> (val);
+ }
+
+ type_stack->push (tp_kind, ival);
+}
+
+/* Helper function for convert_to_kind_type. */
+static struct type *
+convert_to_kind_type_1 (struct type *basetype, int kind)
+{
+ if (basetype == parse_f_type (pstate)->builtin_character)
+ {
+ /* Character of kind 1 is a special case, this is the same as the
+ base character type. */
+ if (kind == 1)
+ return parse_f_type (pstate)->builtin_character;
+ }
+ else if (basetype == parse_f_type (pstate)->builtin_complex)
+ {
+ if (kind == 4)
+ return parse_f_type (pstate)->builtin_complex;
+ else if (kind == 8)
+ return parse_f_type (pstate)->builtin_complex_s8;
+ else if (kind == 16)
+ return parse_f_type (pstate)->builtin_complex_s16;
+ }
+ else if (basetype == parse_f_type (pstate)->builtin_real)
+ {
+ if (kind == 4)
+ return parse_f_type (pstate)->builtin_real;
+ else if (kind == 8)
+ return parse_f_type (pstate)->builtin_real_s8;
+ else if (kind == 16)
+ return parse_f_type (pstate)->builtin_real_s16;
+ }
+ else if (basetype == parse_f_type (pstate)->builtin_logical)
+ {
+ if (kind == 1)
+ return parse_f_type (pstate)->builtin_logical_s1;
+ else if (kind == 2)
+ return parse_f_type (pstate)->builtin_logical_s2;
+ else if (kind == 4)
+ return parse_f_type (pstate)->builtin_logical;
+ else if (kind == 8)
+ return parse_f_type (pstate)->builtin_logical_s8;
+ }
+ else if (basetype == parse_f_type (pstate)->builtin_integer)
+ {
+ if (kind == 1)
+ return parse_f_type (pstate)->builtin_integer_s1;
+ else if (kind == 2)
+ return parse_f_type (pstate)->builtin_integer_s2;
+ else if (kind == 4)
+ return parse_f_type (pstate)->builtin_integer;
+ else if (kind == 8)
+ return parse_f_type (pstate)->builtin_integer_s8;
+ }
+
+ return nullptr;
+}
+
+/* See f-exp-parser.h. */
+
+struct type *
+convert_to_kind_type (struct type *basetype, int kind)
+{
+ struct type *res = convert_to_kind_type_1 (basetype, kind);
+
+ if (res == nullptr || res->code () == TYPE_CODE_ERROR)
+ error (_("unsupported kind %d for type %s"),
+ kind, basetype->safe_name ());
+
+ return res;
+}
+
+struct f_token
+{
+ /* The string to match against. */
+ const char *oper;
+
+ /* The lexer token to return. */
+ int token;
+
+ /* The expression opcode to embed within the token. */
+ enum exp_opcode opcode;
+
+ /* When this is true the string in OPER is matched exactly including
+ case, when this is false OPER is matched case insensitively. */
+ bool case_sensitive;
+};
+
+/* List of Fortran operators. */
+
+static const struct f_token fortran_operators[] =
+{
+ { ".and.", BOOL_AND, OP_NULL, false },
+ { ".or.", BOOL_OR, OP_NULL, false },
+ { ".not.", BOOL_NOT, OP_NULL, false },
+ { ".eq.", EQUAL, OP_NULL, false },
+ { ".eqv.", EQUAL, OP_NULL, false },
+ { ".neqv.", NOTEQUAL, OP_NULL, false },
+ { ".xor.", NOTEQUAL, OP_NULL, false },
+ { "==", EQUAL, OP_NULL, false },
+ { ".ne.", NOTEQUAL, OP_NULL, false },
+ { "/=", NOTEQUAL, OP_NULL, false },
+ { ".le.", LEQ, OP_NULL, false },
+ { "<=", LEQ, OP_NULL, false },
+ { ".ge.", GEQ, OP_NULL, false },
+ { ">=", GEQ, OP_NULL, false },
+ { ".gt.", GREATERTHAN, OP_NULL, false },
+ { ">", GREATERTHAN, OP_NULL, false },
+ { ".lt.", LESSTHAN, OP_NULL, false },
+ { "<", LESSTHAN, OP_NULL, false },
+ { "**", STARSTAR, BINOP_EXP, false },
+};
+
+/* Holds the Fortran representation of a boolean, and the integer value we
+ substitute in when one of the matching strings is parsed. */
+struct f77_boolean_val
+{
+ /* The string representing a Fortran boolean. */
+ const char *name;
+
+ /* The integer value to replace it with. */
+ int value;
+};
+
+/* The set of Fortran booleans. These are matched case insensitively. */
+static const struct f77_boolean_val boolean_values[] =
+{
+ { ".true.", 1 },
+ { ".false.", 0 }
+};
+
+static const struct f_token f_intrinsics[] =
+{
+ /* The following correspond to actual functions in Fortran and are case
+ insensitive. */
+ { "kind", KIND, OP_NULL, false },
+ { "abs", UNOP_INTRINSIC, UNOP_ABS, false },
+ { "mod", BINOP_INTRINSIC, BINOP_MOD, false },
+ { "floor", UNOP_OR_BINOP_INTRINSIC, FORTRAN_FLOOR, false },
+ { "ceiling", UNOP_OR_BINOP_INTRINSIC, FORTRAN_CEILING, false },
+ { "modulo", BINOP_INTRINSIC, BINOP_FORTRAN_MODULO, false },
+ { "cmplx", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_CMPLX, false },
+ { "lbound", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_LBOUND, false },
+ { "ubound", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_UBOUND, false },
+ { "allocated", UNOP_INTRINSIC, UNOP_FORTRAN_ALLOCATED, false },
+ { "associated", UNOP_OR_BINOP_INTRINSIC, FORTRAN_ASSOCIATED, false },
+ { "rank", UNOP_INTRINSIC, UNOP_FORTRAN_RANK, false },
+ { "size", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_ARRAY_SIZE, false },
+ { "shape", UNOP_INTRINSIC, UNOP_FORTRAN_SHAPE, false },
+ { "loc", UNOP_INTRINSIC, UNOP_FORTRAN_LOC, false },
+ { "sizeof", SIZEOF, OP_NULL, false },
+};
+
+static const f_token f_keywords[] =
+{
+ /* Historically these have always been lowercase only in GDB. */
+ { "character", CHARACTER, OP_NULL, true },
+ { "complex", COMPLEX_KEYWORD, OP_NULL, true },
+ { "complex_4", COMPLEX_S4_KEYWORD, OP_NULL, true },
+ { "complex_8", COMPLEX_S8_KEYWORD, OP_NULL, true },
+ { "complex_16", COMPLEX_S16_KEYWORD, OP_NULL, true },
+ { "integer_1", INT_S1_KEYWORD, OP_NULL, true },
+ { "integer_2", INT_S2_KEYWORD, OP_NULL, true },
+ { "integer_4", INT_S4_KEYWORD, OP_NULL, true },
+ { "integer", INT_KEYWORD, OP_NULL, true },
+ { "integer_8", INT_S8_KEYWORD, OP_NULL, true },
+ { "logical_1", LOGICAL_S1_KEYWORD, OP_NULL, true },
+ { "logical_2", LOGICAL_S2_KEYWORD, OP_NULL, true },
+ { "logical", LOGICAL_KEYWORD, OP_NULL, true },
+ { "logical_4", LOGICAL_S4_KEYWORD, OP_NULL, true },
+ { "logical_8", LOGICAL_S8_KEYWORD, OP_NULL, true },
+ { "real", REAL_KEYWORD, OP_NULL, true },
+ { "real_4", REAL_S4_KEYWORD, OP_NULL, true },
+ { "real_8", REAL_S8_KEYWORD, OP_NULL, true },
+ { "real_16", REAL_S16_KEYWORD, OP_NULL, true },
+ { "single", SINGLE, OP_NULL, true },
+ { "double", DOUBLE, OP_NULL, true },
+ { "precision", PRECISION, OP_NULL, true },
+};
+
+/* Implementation of a dynamically expandable buffer for processing input
+ characters acquired through lexptr and building a value to return in
+ yylval. Ripped off from ch-exp.y */
+
+static char *tempbuf; /* Current buffer contents */
+static int tempbufsize; /* Size of allocated buffer */
+static int tempbufindex; /* Current index into buffer */
+
+#define GROWBY_MIN_SIZE 64 /* Minimum amount to grow buffer by */
+
+#define CHECKBUF(size) \
+ do { \
+ if (tempbufindex + (size) >= tempbufsize) \
+ { \
+ growbuf_by_size (size); \
+ } \
+ } while (0);
+
+/* Grow the static temp buffer if necessary, including allocating the
+ first one on demand. */
+
+static void
+growbuf_by_size (int count)
+{
+ int growby;
+
+ growby = std::max (count, GROWBY_MIN_SIZE);
+ tempbufsize += growby;
+ if (tempbuf == NULL)
+ tempbuf = (char *) xmalloc (tempbufsize);
+ else
+ tempbuf = (char *) xrealloc (tempbuf, tempbufsize);
+}
+
+/* Blatantly ripped off from ch-exp.y. This routine recognizes F77
+ string-literals.
+
+ Recognize a string literal. A string literal is a nonzero sequence
+ of characters enclosed in matching single quotes, except that
+ a single character inside single quotes is a character literal, which
+ we reject as a string literal. To embed the terminator character inside
+ a string, it is simply doubled (I.E. 'this''is''one''string') */
+
+static int
+match_string_literal (void)
+{
+ const char *tokptr = pstate->lexptr;
+
+ for (tempbufindex = 0, tokptr++; *tokptr != '\0'; tokptr++)
+ {
+ CHECKBUF (1);
+ if (*tokptr == *pstate->lexptr)
+ {
+ if (*(tokptr + 1) == *pstate->lexptr)
+ tokptr++;
+ else
+ break;
+ }
+ tempbuf[tempbufindex++] = *tokptr;
+ }
+ if (*tokptr == '\0' /* no terminator */
+ || tempbufindex == 0) /* no string */
+ return 0;
+ else
+ {
+ tempbuf[tempbufindex] = '\0';
+ f_yylval.sval.ptr = tempbuf;
+ f_yylval.sval.length = tempbufindex;
+ pstate->lexptr = ++tokptr;
+ return STRING_LITERAL;
+ }
+}
+
+/* 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 bool saw_name_at_eof;
+
+/* This is set if the previously-returned token was a structure
+ operator '%'. */
+static bool last_was_structop;
+
+/* See f-exp-parser.h. */
+
+int
+f_yylex (void)
+{
+ int c;
+ int namelen;
+ unsigned int token;
+ const char *tokstart;
+ bool saw_structop = last_was_structop;
+
+ last_was_structop = false;
+
+ retry:
+
+ pstate->prev_lexptr = pstate->lexptr;
+
+ tokstart = pstate->lexptr;
+
+ /* First of all, let us make sure we are not dealing with the
+ special tokens .true. and .false. which evaluate to 1 and 0. */
+
+ if (*pstate->lexptr == '.')
+ {
+ for (const auto &candidate : boolean_values)
+ {
+ if (strncasecmp (tokstart, candidate.name,
+ strlen (candidate.name)) == 0)
+ {
+ pstate->lexptr += strlen (candidate.name);
+ f_yylval.lval = candidate.value;
+ return BOOLEAN_LITERAL;
+ }
+ }
+ }
+
+ /* See if it is a Fortran operator. */
+ for (const auto &candidate : fortran_operators)
+ if (strncasecmp (tokstart, candidate.oper,
+ strlen (candidate.oper)) == 0)
+ {
+ gdb_assert (!candidate.case_sensitive);
+ pstate->lexptr += strlen (candidate.oper);
+ f_yylval.opcode = candidate.opcode;
+ return candidate.token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ if (saw_name_at_eof)
+ {
+ saw_name_at_eof = false;
+ return COMPLETE;
+ }
+ else if (pstate->parse_completion && saw_structop)
+ return COMPLETE;
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ pstate->lexptr++;
+ goto retry;
+
+ case '\'':
+ token = match_string_literal ();
+ if (token != 0)
+ return (token);
+ break;
+
+ case '(':
+ paren_depth++;
+ pstate->lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ pstate->lexptr++;
+ return c;
+
+ case ',':
+ if (pstate->comma_terminates && paren_depth == 0)
+ return 0;
+ pstate->lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
+ 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_d = 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;
+ }
+
+ for (;; ++p)
+ {
+ if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ else if (!hex && !got_d && (*p == 'd' || *p == 'D'))
+ got_dot = got_d = 1;
+ else if (!hex && !got_dot && *p == '.')
+ got_dot = 1;
+ else if (((got_e && (p[-1] == 'e' || p[-1] == 'E'))
+ || (got_d && (p[-1] == 'd' || p[-1] == 'D')))
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ /* We will take any letters or digits. parse_number will
+ complain if past the radix, or if L or U are not final. */
+ else if ((*p < '0' || *p > '9')
+ && ((*p < 'a' || *p > 'z')
+ && (*p < 'A' || *p > 'Z')))
+ break;
+ }
+ toktype = parse_number (pstate, tokstart, p - tokstart,
+ got_dot|got_e|got_d,
+ &f_yylval);
+ if (toktype == ERROR)
+ error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+ tokstart);
+ pstate->lexptr = p;
+ return toktype;
+ }
+
+ case '%':
+ last_was_structop = true;
+ [[fallthrough]];
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '@':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ pstate->lexptr++;
+ return c;
+ }
+
+ if (!(c == '_' || c == '$' || c ==':'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error (_("Invalid character '%c' in expression."), c);
+
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || c == ':' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen]);
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ return 0;
+
+ pstate->lexptr += namelen;
+
+ /* Catch specific keywords. */
+
+ for (const auto &keyword : f_keywords)
+ if (strlen (keyword.oper) == namelen
+ && ((!keyword.case_sensitive
+ && strncasecmp (tokstart, keyword.oper, namelen) == 0)
+ || (keyword.case_sensitive
+ && strncmp (tokstart, keyword.oper, namelen) == 0)))
+ {
+ f_yylval.opcode = keyword.opcode;
+ return keyword.token;
+ }
+
+ f_yylval.sval.ptr = tokstart;
+ f_yylval.sval.length = namelen;
+
+ if (*tokstart == '$')
+ return DOLLAR_VARIABLE;
+
+ /* Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+ std::string tmp = copy_name (f_yylval.sval);
+ struct block_symbol result;
+ const domain_search_flags lookup_domains[] =
+ {
+ SEARCH_VFT,
+ SEARCH_STRUCT_DOMAIN,
+ SEARCH_MODULE_DOMAIN
+ };
+ int hextype;
+
+ for (const auto &domain : lookup_domains)
+ {
+ result = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
+ domain, NULL);
+ if (result.symbol && result.symbol->loc_class () == LOC_TYPEDEF)
+ {
+ f_yylval.tsym.type = result.symbol->type ();
+ return TYPENAME;
+ }
+
+ if (result.symbol)
+ break;
+ }
+
+ f_yylval.tsym.type
+ = language_lookup_primitive_type (pstate->language (),
+ pstate->gdbarch (), tmp.c_str ());
+ if (f_yylval.tsym.type != NULL)
+ return TYPENAME;
+
+ /* This is post the symbol search as symbols can hide intrinsics. Also,
+ give Fortran intrinsics priority over C symbols. This prevents
+ non-Fortran symbols from hiding intrinsics, for example abs. */
+ if (!result.symbol || result.symbol->language () != language_fortran)
+ for (const auto &intrinsic : f_intrinsics)
+ {
+ gdb_assert (!intrinsic.case_sensitive);
+ if (strlen (intrinsic.oper) == namelen
+ && strncasecmp (tokstart, intrinsic.oper, namelen) == 0)
+ {
+ f_yylval.opcode = intrinsic.opcode;
+ return intrinsic.token;
+ }
+ }
+
+ /* 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 (!result.symbol
+ && ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
+ || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
+ {
+ f_exp_parser_YYSTYPE newlval; /* Its value is ignored. */
+ hextype = parse_number (pstate, tokstart, namelen, 0, &newlval);
+ if (hextype == INT)
+ {
+ f_yylval.ssym.sym = result;
+ f_yylval.ssym.is_a_field_of_this = false;
+ return NAME_OR_INT;
+ }
+ }
+
+ if (pstate->parse_completion && *pstate->lexptr == '\0')
+ saw_name_at_eof = true;
+
+ /* Any other kind of symbol */
+ f_yylval.ssym.sym = result;
+ f_yylval.ssym.is_a_field_of_this = false;
+ return NAME;
+ }
+}
+
+/* See f-exp-parser.h. */
+
+void
+f_yyerror (const char *msg)
+{
+ pstate->parse_error (msg);
+}
+
+} /* namespace f_exp_parser */
+
+/* See f-exp-parser.h. */
+
+int
+f_parse (struct parser_state *par_state)
+{
+ using namespace f_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate);
+ scoped_restore restore_yydebug = make_scoped_restore (&f_yydebug,
+ par_state->debug);
+ gdb_assert (par_state != NULL);
+ pstate = par_state;
+ last_was_structop = false;
+ saw_name_at_eof = false;
+ paren_depth = 0;
+
+ struct type_stack stack;
+ scoped_restore restore_type_stack
+ = make_scoped_restore (&f_exp_parser::type_stack, &stack);
+
+ int result = f_yyparse ();
+ if (!result)
+ pstate->set_operation (pstate->pop ());
+ return result;
+}
diff --git a/gdb/f-exp-parser.h b/gdb/f-exp-parser.h
new file mode 100644
index 000000000000..8f504b6e8a85
--- /dev/null
+++ b/gdb/f-exp-parser.h
@@ -0,0 +1,102 @@
+/* YACC parser support code for Fortran expressions, 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_F_EXP_PARSER_H
+#define GDB_F_EXP_PARSER_H
+
+#include "parser-defs.h"
+#include "type-stack.h"
+#include "f-lang.h"
+
+union f_exp_parser_YYSTYPE;
+
+namespace f_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+ expression. */
+
+extern parser_state *pstate;
+
+/* The current type stack. */
+
+extern struct type_stack *type_stack;
+
+/* Return the Fortran type table for the architecture associated to PS. */
+
+static inline const struct builtin_f_type *
+parse_f_type (parser_state *ps)
+{
+ return builtin_f_type (ps->gdbarch ());
+}
+
+/* Called to match intrinsic function calls with one argument to their
+ respective implementation and push the operation. */
+
+void wrap_unop_intrinsic (exp_opcode opcode);
+
+/* Called to match intrinsic function calls with two arguments to their
+ respective implementation and push the operation. */
+
+void wrap_binop_intrinsic (exp_opcode opcode);
+
+/* Called to match intrinsic function calls with three arguments to their
+ respective implementation and push the operation. */
+
+void wrap_ternop_intrinsic (exp_opcode opcode);
+
+/* 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 *p, int len,
+ int parsed_float, f_exp_parser_YYSTYPE *putithere);
+
+/* Called to setup the type stack when we encounter a '(kind=N)' type
+ modifier, performs some bounds checking on 'N' and then pushes this to
+ the type stack followed by the 'tp_kind' marker. */
+
+void push_kind_type (LONGEST val, struct type *type);
+
+/* Called when a type has a '(kind=N)' modifier after it, for example
+ 'character(kind=1)'. The BASETYPE is the type described by 'character'
+ in our example, and KIND is the integer '1'. This function returns a
+ new type that represents the basetype of a specific kind. */
+
+struct type *convert_to_kind_type (struct type *basetype, int kind);
+
+/* Read one token, getting characters through lexptr. */
+
+int f_yylex ();
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void f_yyerror (const char *msg);
+
+} /* namespace f_exp_parser */
+
+/* Parse a Fortran 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 f_parse (struct parser_state *par_state);
+
+#endif /* GDB_F_EXP_PARSER_H */
diff --git a/gdb/f-exp-parser.y b/gdb/f-exp-parser.y
index ad1a9253d7f9..6c252cb3eb92 100644
--- a/gdb/f-exp-parser.y
+++ b/gdb/f-exp-parser.y
@@ -47,57 +47,18 @@
#include "parser-defs.h"
#include "language.h"
#include "f-lang.h"
+#include "f-exp-parser.h"
#include "block.h"
#include <algorithm>
#include "type-stack.h"
#include "f-exp.h"
-/* The state of the parser, used internally when we are parsing the
- expression. */
-
-static struct parser_state *pstate = NULL;
-
-/* Depth of parentheses. */
-static int paren_depth;
-
-/* The current type stack. */
-static struct type_stack *type_stack;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
-
-static void growbuf_by_size (int);
-
-static int match_string_literal (void);
-
-static void push_kind_type (LONGEST val, struct type *type);
-
-static struct type *convert_to_kind_type (struct type *basetype, int kind);
-
-static void wrap_unop_intrinsic (exp_opcode opcode);
-
-static void wrap_binop_intrinsic (exp_opcode opcode);
-
-static void wrap_ternop_intrinsic (exp_opcode opcode);
-
-template<typename T>
-static void fortran_wrap2_kind (type *base_type);
-
-template<typename T>
-static void fortran_wrap3_kind (type *base_type);
-
-/* Return the Fortran type table for the architecture associated to PS. */
-
-static inline const struct builtin_f_type *
-parse_f_type (parser_state *ps)
-{
- return builtin_f_type (ps->gdbarch ());
-}
-
+using namespace f_exp_parser;
using namespace expr;
+
+/* Bring the f_exp_parser::type_stack global into this scope, so that it hides
+ the struct type_stack type name. */
+using f_exp_parser::type_stack;
%}
/* Although the yacc "value" of an expression is not used,
@@ -128,12 +89,6 @@ using namespace expr;
int *ivec;
}
-%{
-/* YYSTYPE gets defined by %union */
-static int parse_number (struct parser_state *, const char *, int,
- int, YYSTYPE *);
-%}
-
%type <voidval> exp type_exp start variable
%type <tval> type typebase
%type <tvec> nonempty_typelist
@@ -809,941 +764,3 @@ name_not_typename : NAME
| NAME_OR_INT
*/
;
-
-%%
-
-/* Called to match intrinsic function calls with one argument to their
- respective implementation and push the operation. */
-
-static void
-wrap_unop_intrinsic (exp_opcode code)
-{
- switch (code)
- {
- case UNOP_ABS:
- pstate->wrap<fortran_abs_operation> ();
- break;
- case FORTRAN_FLOOR:
- pstate->wrap<fortran_floor_operation_1arg> ();
- break;
- case FORTRAN_CEILING:
- pstate->wrap<fortran_ceil_operation_1arg> ();
- break;
- case UNOP_FORTRAN_ALLOCATED:
- pstate->wrap<fortran_allocated_operation> ();
- break;
- case UNOP_FORTRAN_RANK:
- pstate->wrap<fortran_rank_operation> ();
- break;
- case UNOP_FORTRAN_SHAPE:
- pstate->wrap<fortran_array_shape_operation> ();
- break;
- case UNOP_FORTRAN_LOC:
- pstate->wrap<fortran_loc_operation> ();
- break;
- case FORTRAN_ASSOCIATED:
- pstate->wrap<fortran_associated_1arg> ();
- break;
- case FORTRAN_ARRAY_SIZE:
- pstate->wrap<fortran_array_size_1arg> ();
- break;
- case FORTRAN_CMPLX:
- pstate->wrap<fortran_cmplx_operation_1arg> ();
- break;
- case FORTRAN_LBOUND:
- case FORTRAN_UBOUND:
- pstate->push_new<fortran_bound_1arg> (code, pstate->pop ());
- break;
- default:
- gdb_assert_not_reached ("unhandled intrinsic");
- }
-}
-
-/* Called to match intrinsic function calls with two arguments to their
- respective implementation and push the operation. */
-
-static void
-wrap_binop_intrinsic (exp_opcode code)
-{
- switch (code)
- {
- case FORTRAN_FLOOR:
- fortran_wrap2_kind<fortran_floor_operation_2arg>
- (parse_f_type (pstate)->builtin_integer);
- break;
- case FORTRAN_CEILING:
- fortran_wrap2_kind<fortran_ceil_operation_2arg>
- (parse_f_type (pstate)->builtin_integer);
- break;
- case BINOP_MOD:
- pstate->wrap2<fortran_mod_operation> ();
- break;
- case BINOP_FORTRAN_MODULO:
- pstate->wrap2<fortran_modulo_operation> ();
- break;
- case FORTRAN_CMPLX:
- pstate->wrap2<fortran_cmplx_operation_2arg> ();
- break;
- case FORTRAN_ASSOCIATED:
- pstate->wrap2<fortran_associated_2arg> ();
- break;
- case FORTRAN_ARRAY_SIZE:
- pstate->wrap2<fortran_array_size_2arg> ();
- break;
- case FORTRAN_LBOUND:
- case FORTRAN_UBOUND:
- {
- operation_up arg2 = pstate->pop ();
- operation_up arg1 = pstate->pop ();
- pstate->push_new<fortran_bound_2arg> (code, std::move (arg1),
- std::move (arg2));
- }
- break;
- default:
- gdb_assert_not_reached ("unhandled intrinsic");
- }
-}
-
-/* Called to match intrinsic function calls with three arguments to their
- respective implementation and push the operation. */
-
-static void
-wrap_ternop_intrinsic (exp_opcode code)
-{
- switch (code)
- {
- case FORTRAN_LBOUND:
- case FORTRAN_UBOUND:
- {
- operation_up kind_arg = pstate->pop ();
- operation_up arg2 = pstate->pop ();
- operation_up arg1 = pstate->pop ();
-
- value *val = kind_arg->evaluate (nullptr, pstate->expout.get (),
- EVAL_AVOID_SIDE_EFFECTS);
- gdb_assert (val != nullptr);
-
- type *follow_type
- = convert_to_kind_type (parse_f_type (pstate)->builtin_integer,
- value_as_long (val));
-
- pstate->push_new<fortran_bound_3arg> (code, std::move (arg1),
- std::move (arg2), follow_type);
- }
- break;
- case FORTRAN_ARRAY_SIZE:
- fortran_wrap3_kind<fortran_array_size_3arg>
- (parse_f_type (pstate)->builtin_integer);
- break;
- case FORTRAN_CMPLX:
- fortran_wrap3_kind<fortran_cmplx_operation_3arg>
- (parse_f_type (pstate)->builtin_complex);
- break;
- default:
- gdb_assert_not_reached ("unhandled intrinsic");
- }
-}
-
-/* A helper that pops two operations (similar to wrap2), evaluates the last one
- assuming it is a kind parameter, and wraps them in some other operation
- pushing it to the stack. */
-
-template<typename T>
-static void
-fortran_wrap2_kind (type *base_type)
-{
- operation_up kind_arg = pstate->pop ();
- operation_up arg = pstate->pop ();
-
- value *val = kind_arg->evaluate (nullptr, pstate->expout.get (),
- EVAL_AVOID_SIDE_EFFECTS);
- gdb_assert (val != nullptr);
-
- type *follow_type = convert_to_kind_type (base_type, value_as_long (val));
-
- pstate->push_new<T> (std::move (arg), follow_type);
-}
-
-/* A helper that pops three operations, evaluates the last one assuming it is a
- kind parameter, and wraps them in some other operation pushing it to the
- stack. */
-
-template<typename T>
-static void
-fortran_wrap3_kind (type *base_type)
-{
- operation_up kind_arg = pstate->pop ();
- operation_up arg2 = pstate->pop ();
- operation_up arg1 = pstate->pop ();
-
- value *val = kind_arg->evaluate (nullptr, pstate->expout.get (),
- EVAL_AVOID_SIDE_EFFECTS);
- gdb_assert (val != nullptr);
-
- type *follow_type = convert_to_kind_type (base_type, value_as_long (val));
-
- pstate->push_new<T> (std::move (arg1), std::move (arg2), follow_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 *p, int len, int parsed_float, YYSTYPE *putithere)
-{
- ULONGEST n = 0;
- ULONGEST prevn = 0;
- int c;
- int base = input_radix;
- int unsigned_p = 0;
- int long_p = 0;
- ULONGEST high_bit;
- struct type *signed_type;
- struct type *unsigned_type;
-
- if (parsed_float)
- {
- /* It's a float since it contains a point or an exponent. */
- /* [dD] is not understood as an exponent by parse_float,
- change it to 'e'. */
- char *tmp, *tmp2;
-
- tmp = xstrdup (p);
- for (tmp2 = tmp; *tmp2; ++tmp2)
- if (*tmp2 == 'd' || *tmp2 == 'D')
- *tmp2 = 'e';
-
- /* FIXME: Should this use different types? */
- putithere->typed_val_float.type = parse_f_type (pstate)->builtin_real_s8;
- bool parsed = parse_float (tmp, len,
- putithere->typed_val_float.type,
- putithere->typed_val_float.val);
- free (tmp);
- return parsed? FLOAT : ERROR;
- }
-
- /* Handle base-switching prefixes 0x, 0t, 0d, 0 */
- if (p[0] == '0' && len > 1)
- switch (p[1])
- {
- case 'x':
- case 'X':
- if (len >= 3)
- {
- p += 2;
- base = 16;
- len -= 2;
- }
- break;
-
- case 't':
- case 'T':
- case 'd':
- case 'D':
- if (len >= 3)
- {
- p += 2;
- base = 10;
- len -= 2;
- }
- break;
-
- default:
- base = 8;
- break;
- }
-
- while (len-- > 0)
- {
- c = *p++;
- if (c_isupper (c))
- c = c_tolower (c);
- if (len == 0 && c == 'l')
- long_p = 1;
- else if (len == 0 && c == 'u')
- unsigned_p = 1;
- else
- {
- int i;
- if (c >= '0' && c <= '9')
- i = c - '0';
- else if (c >= 'a' && c <= 'f')
- i = c - 'a' + 10;
- else
- return ERROR; /* Char not a digit */
- if (i >= base)
- return ERROR; /* Invalid digit in this base */
- n *= base;
- n += i;
- }
- /* Test for overflow. */
- if (prevn == 0 && n == 0)
- ;
- else if (RANGE_CHECK && prevn >= n)
- range_error (_("Overflow on numeric constant."));
- prevn = n;
- }
-
- /* If the number is too big to be an int, or it's got an l suffix
- then it's a long. Work out if this has to be a long by
- shifting right and seeing if anything remains, and the
- target int size is different to the target long size.
-
- In the expression below, we could have tested
- (n >> gdbarch_int_bit (parse_gdbarch))
- to see if it was zero,
- but too many compilers warn about that, when ints and longs
- are the same size. So we shift it twice, with fewer bits
- each time, for the same result. */
-
- int bits_available;
- if ((gdbarch_int_bit (par_state->gdbarch ())
- != gdbarch_long_bit (par_state->gdbarch ())
- && ((n >> 2)
- >> (gdbarch_int_bit (par_state->gdbarch ())-2))) /* Avoid
- shift warning */
- || long_p)
- {
- bits_available = gdbarch_long_bit (par_state->gdbarch ());
- unsigned_type = parse_type (par_state)->builtin_unsigned_long;
- signed_type = parse_type (par_state)->builtin_long;
- }
- else
- {
- bits_available = gdbarch_int_bit (par_state->gdbarch ());
- unsigned_type = parse_type (par_state)->builtin_unsigned_int;
- signed_type = parse_type (par_state)->builtin_int;
- }
- high_bit = ((ULONGEST)1) << (bits_available - 1);
-
- if (RANGE_CHECK
- && ((n >> 2) >> (bits_available - 2)))
- range_error (_("Overflow on numeric constant."));
-
- putithere->typed_val.val = n;
-
- /* If the high bit of the worked out type is set then this number
- has to be unsigned. */
-
- if (unsigned_p || (n & high_bit))
- putithere->typed_val.type = unsigned_type;
- else
- putithere->typed_val.type = signed_type;
-
- return INT;
-}
-
-/* Called to setup the type stack when we encounter a '(kind=N)' type
- modifier, performs some bounds checking on 'N' and then pushes this to
- the type stack followed by the 'tp_kind' marker. */
-static void
-push_kind_type (LONGEST val, struct type *type)
-{
- int ival;
-
- if (type->is_unsigned ())
- {
- ULONGEST uval = static_cast <ULONGEST> (val);
- if (uval > INT_MAX)
- error (_("kind value out of range"));
- ival = static_cast <int> (uval);
- }
- else
- {
- if (val > INT_MAX || val < 0)
- error (_("kind value out of range"));
- ival = static_cast <int> (val);
- }
-
- type_stack->push (tp_kind, ival);
-}
-
-/* Helper function for convert_to_kind_type. */
-static struct type *
-convert_to_kind_type_1 (struct type *basetype, int kind)
-{
- if (basetype == parse_f_type (pstate)->builtin_character)
- {
- /* Character of kind 1 is a special case, this is the same as the
- base character type. */
- if (kind == 1)
- return parse_f_type (pstate)->builtin_character;
- }
- else if (basetype == parse_f_type (pstate)->builtin_complex)
- {
- if (kind == 4)
- return parse_f_type (pstate)->builtin_complex;
- else if (kind == 8)
- return parse_f_type (pstate)->builtin_complex_s8;
- else if (kind == 16)
- return parse_f_type (pstate)->builtin_complex_s16;
- }
- else if (basetype == parse_f_type (pstate)->builtin_real)
- {
- if (kind == 4)
- return parse_f_type (pstate)->builtin_real;
- else if (kind == 8)
- return parse_f_type (pstate)->builtin_real_s8;
- else if (kind == 16)
- return parse_f_type (pstate)->builtin_real_s16;
- }
- else if (basetype == parse_f_type (pstate)->builtin_logical)
- {
- if (kind == 1)
- return parse_f_type (pstate)->builtin_logical_s1;
- else if (kind == 2)
- return parse_f_type (pstate)->builtin_logical_s2;
- else if (kind == 4)
- return parse_f_type (pstate)->builtin_logical;
- else if (kind == 8)
- return parse_f_type (pstate)->builtin_logical_s8;
- }
- else if (basetype == parse_f_type (pstate)->builtin_integer)
- {
- if (kind == 1)
- return parse_f_type (pstate)->builtin_integer_s1;
- else if (kind == 2)
- return parse_f_type (pstate)->builtin_integer_s2;
- else if (kind == 4)
- return parse_f_type (pstate)->builtin_integer;
- else if (kind == 8)
- return parse_f_type (pstate)->builtin_integer_s8;
- }
-
- return nullptr;
-}
-
-/* Called when a type has a '(kind=N)' modifier after it, for example
- 'character(kind=1)'. The BASETYPE is the type described by 'character'
- in our example, and KIND is the integer '1'. This function returns a
- new type that represents the basetype of a specific kind. */
-static struct type *
-convert_to_kind_type (struct type *basetype, int kind)
-{
- struct type *res = convert_to_kind_type_1 (basetype, kind);
-
- if (res == nullptr || res->code () == TYPE_CODE_ERROR)
- error (_("unsupported kind %d for type %s"),
- kind, basetype->safe_name ());
-
- return res;
-}
-
-struct f_token
-{
- /* The string to match against. */
- const char *oper;
-
- /* The lexer token to return. */
- int token;
-
- /* The expression opcode to embed within the token. */
- enum exp_opcode opcode;
-
- /* When this is true the string in OPER is matched exactly including
- case, when this is false OPER is matched case insensitively. */
- bool case_sensitive;
-};
-
-/* List of Fortran operators. */
-
-static const struct f_token fortran_operators[] =
-{
- { ".and.", BOOL_AND, OP_NULL, false },
- { ".or.", BOOL_OR, OP_NULL, false },
- { ".not.", BOOL_NOT, OP_NULL, false },
- { ".eq.", EQUAL, OP_NULL, false },
- { ".eqv.", EQUAL, OP_NULL, false },
- { ".neqv.", NOTEQUAL, OP_NULL, false },
- { ".xor.", NOTEQUAL, OP_NULL, false },
- { "==", EQUAL, OP_NULL, false },
- { ".ne.", NOTEQUAL, OP_NULL, false },
- { "/=", NOTEQUAL, OP_NULL, false },
- { ".le.", LEQ, OP_NULL, false },
- { "<=", LEQ, OP_NULL, false },
- { ".ge.", GEQ, OP_NULL, false },
- { ">=", GEQ, OP_NULL, false },
- { ".gt.", GREATERTHAN, OP_NULL, false },
- { ">", GREATERTHAN, OP_NULL, false },
- { ".lt.", LESSTHAN, OP_NULL, false },
- { "<", LESSTHAN, OP_NULL, false },
- { "**", STARSTAR, BINOP_EXP, false },
-};
-
-/* Holds the Fortran representation of a boolean, and the integer value we
- substitute in when one of the matching strings is parsed. */
-struct f77_boolean_val
-{
- /* The string representing a Fortran boolean. */
- const char *name;
-
- /* The integer value to replace it with. */
- int value;
-};
-
-/* The set of Fortran booleans. These are matched case insensitively. */
-static const struct f77_boolean_val boolean_values[] =
-{
- { ".true.", 1 },
- { ".false.", 0 }
-};
-
-static const struct f_token f_intrinsics[] =
-{
- /* The following correspond to actual functions in Fortran and are case
- insensitive. */
- { "kind", KIND, OP_NULL, false },
- { "abs", UNOP_INTRINSIC, UNOP_ABS, false },
- { "mod", BINOP_INTRINSIC, BINOP_MOD, false },
- { "floor", UNOP_OR_BINOP_INTRINSIC, FORTRAN_FLOOR, false },
- { "ceiling", UNOP_OR_BINOP_INTRINSIC, FORTRAN_CEILING, false },
- { "modulo", BINOP_INTRINSIC, BINOP_FORTRAN_MODULO, false },
- { "cmplx", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_CMPLX, false },
- { "lbound", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_LBOUND, false },
- { "ubound", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_UBOUND, false },
- { "allocated", UNOP_INTRINSIC, UNOP_FORTRAN_ALLOCATED, false },
- { "associated", UNOP_OR_BINOP_INTRINSIC, FORTRAN_ASSOCIATED, false },
- { "rank", UNOP_INTRINSIC, UNOP_FORTRAN_RANK, false },
- { "size", UNOP_OR_BINOP_OR_TERNOP_INTRINSIC, FORTRAN_ARRAY_SIZE, false },
- { "shape", UNOP_INTRINSIC, UNOP_FORTRAN_SHAPE, false },
- { "loc", UNOP_INTRINSIC, UNOP_FORTRAN_LOC, false },
- { "sizeof", SIZEOF, OP_NULL, false },
-};
-
-static const f_token f_keywords[] =
-{
- /* Historically these have always been lowercase only in GDB. */
- { "character", CHARACTER, OP_NULL, true },
- { "complex", COMPLEX_KEYWORD, OP_NULL, true },
- { "complex_4", COMPLEX_S4_KEYWORD, OP_NULL, true },
- { "complex_8", COMPLEX_S8_KEYWORD, OP_NULL, true },
- { "complex_16", COMPLEX_S16_KEYWORD, OP_NULL, true },
- { "integer_1", INT_S1_KEYWORD, OP_NULL, true },
- { "integer_2", INT_S2_KEYWORD, OP_NULL, true },
- { "integer_4", INT_S4_KEYWORD, OP_NULL, true },
- { "integer", INT_KEYWORD, OP_NULL, true },
- { "integer_8", INT_S8_KEYWORD, OP_NULL, true },
- { "logical_1", LOGICAL_S1_KEYWORD, OP_NULL, true },
- { "logical_2", LOGICAL_S2_KEYWORD, OP_NULL, true },
- { "logical", LOGICAL_KEYWORD, OP_NULL, true },
- { "logical_4", LOGICAL_S4_KEYWORD, OP_NULL, true },
- { "logical_8", LOGICAL_S8_KEYWORD, OP_NULL, true },
- { "real", REAL_KEYWORD, OP_NULL, true },
- { "real_4", REAL_S4_KEYWORD, OP_NULL, true },
- { "real_8", REAL_S8_KEYWORD, OP_NULL, true },
- { "real_16", REAL_S16_KEYWORD, OP_NULL, true },
- { "single", SINGLE, OP_NULL, true },
- { "double", DOUBLE, OP_NULL, true },
- { "precision", PRECISION, OP_NULL, true },
-};
-
-/* Implementation of a dynamically expandable buffer for processing input
- characters acquired through lexptr and building a value to return in
- yylval. Ripped off from ch-exp.y */
-
-static char *tempbuf; /* Current buffer contents */
-static int tempbufsize; /* Size of allocated buffer */
-static int tempbufindex; /* Current index into buffer */
-
-#define GROWBY_MIN_SIZE 64 /* Minimum amount to grow buffer by */
-
-#define CHECKBUF(size) \
- do { \
- if (tempbufindex + (size) >= tempbufsize) \
- { \
- growbuf_by_size (size); \
- } \
- } while (0);
-
-
-/* Grow the static temp buffer if necessary, including allocating the
- first one on demand. */
-
-static void
-growbuf_by_size (int count)
-{
- int growby;
-
- growby = std::max (count, GROWBY_MIN_SIZE);
- tempbufsize += growby;
- if (tempbuf == NULL)
- tempbuf = (char *) malloc (tempbufsize);
- else
- tempbuf = (char *) realloc (tempbuf, tempbufsize);
-}
-
-/* Blatantly ripped off from ch-exp.y. This routine recognizes F77
- string-literals.
-
- Recognize a string literal. A string literal is a nonzero sequence
- of characters enclosed in matching single quotes, except that
- a single character inside single quotes is a character literal, which
- we reject as a string literal. To embed the terminator character inside
- a string, it is simply doubled (I.E. 'this''is''one''string') */
-
-static int
-match_string_literal (void)
-{
- const char *tokptr = pstate->lexptr;
-
- for (tempbufindex = 0, tokptr++; *tokptr != '\0'; tokptr++)
- {
- CHECKBUF (1);
- if (*tokptr == *pstate->lexptr)
- {
- if (*(tokptr + 1) == *pstate->lexptr)
- tokptr++;
- else
- break;
- }
- tempbuf[tempbufindex++] = *tokptr;
- }
- if (*tokptr == '\0' /* no terminator */
- || tempbufindex == 0) /* no string */
- return 0;
- else
- {
- tempbuf[tempbufindex] = '\0';
- yylval.sval.ptr = tempbuf;
- yylval.sval.length = tempbufindex;
- pstate->lexptr = ++tokptr;
- return STRING_LITERAL;
- }
-}
-
-/* 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 bool saw_name_at_eof;
-
-/* This is set if the previously-returned token was a structure
- operator '%'. */
-static bool last_was_structop;
-
-/* Read one token, getting characters through lexptr. */
-
-static int
-yylex (void)
-{
- int c;
- int namelen;
- unsigned int token;
- const char *tokstart;
- bool saw_structop = last_was_structop;
-
- last_was_structop = false;
-
- retry:
-
- pstate->prev_lexptr = pstate->lexptr;
-
- tokstart = pstate->lexptr;
-
- /* First of all, let us make sure we are not dealing with the
- special tokens .true. and .false. which evaluate to 1 and 0. */
-
- if (*pstate->lexptr == '.')
- {
- for (const auto &candidate : boolean_values)
- {
- if (strncasecmp (tokstart, candidate.name,
- strlen (candidate.name)) == 0)
- {
- pstate->lexptr += strlen (candidate.name);
- yylval.lval = candidate.value;
- return BOOLEAN_LITERAL;
- }
- }
- }
-
- /* See if it is a Fortran operator. */
- for (const auto &candidate : fortran_operators)
- if (strncasecmp (tokstart, candidate.oper,
- strlen (candidate.oper)) == 0)
- {
- gdb_assert (!candidate.case_sensitive);
- pstate->lexptr += strlen (candidate.oper);
- yylval.opcode = candidate.opcode;
- return candidate.token;
- }
-
- switch (c = *tokstart)
- {
- case 0:
- if (saw_name_at_eof)
- {
- saw_name_at_eof = false;
- return COMPLETE;
- }
- else if (pstate->parse_completion && saw_structop)
- return COMPLETE;
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- pstate->lexptr++;
- goto retry;
-
- case '\'':
- token = match_string_literal ();
- if (token != 0)
- return (token);
- break;
-
- case '(':
- paren_depth++;
- pstate->lexptr++;
- return c;
-
- case ')':
- if (paren_depth == 0)
- return 0;
- paren_depth--;
- pstate->lexptr++;
- return c;
-
- case ',':
- if (pstate->comma_terminates && paren_depth == 0)
- return 0;
- pstate->lexptr++;
- return c;
-
- case '.':
- /* Might be a floating point number. */
- if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
- 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_d = 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;
- }
-
- for (;; ++p)
- {
- if (!hex && !got_e && (*p == 'e' || *p == 'E'))
- got_dot = got_e = 1;
- else if (!hex && !got_d && (*p == 'd' || *p == 'D'))
- got_dot = got_d = 1;
- else if (!hex && !got_dot && *p == '.')
- got_dot = 1;
- else if (((got_e && (p[-1] == 'e' || p[-1] == 'E'))
- || (got_d && (p[-1] == 'd' || p[-1] == 'D')))
- && (*p == '-' || *p == '+'))
- /* This is the sign of the exponent, not the end of the
- number. */
- continue;
- /* We will take any letters or digits. parse_number will
- complain if past the radix, or if L or U are not final. */
- else if ((*p < '0' || *p > '9')
- && ((*p < 'a' || *p > 'z')
- && (*p < 'A' || *p > 'Z')))
- break;
- }
- toktype = parse_number (pstate, tokstart, p - tokstart,
- got_dot|got_e|got_d,
- &yylval);
- if (toktype == ERROR)
- error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
- tokstart);
- pstate->lexptr = p;
- return toktype;
- }
-
- case '%':
- last_was_structop = true;
- [[fallthrough]];
- case '+':
- case '-':
- case '*':
- case '/':
- case '|':
- case '&':
- case '^':
- case '~':
- case '!':
- case '@':
- case '<':
- case '>':
- case '[':
- case ']':
- case '?':
- case ':':
- case '=':
- case '{':
- case '}':
- symbol:
- pstate->lexptr++;
- return c;
- }
-
- if (!(c == '_' || c == '$' || c ==':'
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
- /* We must have come across a bad character (e.g. ';'). */
- error (_("Invalid character '%c' in expression."), c);
-
- namelen = 0;
- for (c = tokstart[namelen];
- (c == '_' || c == '$' || c == ':' || (c >= '0' && c <= '9')
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
- c = tokstart[++namelen]);
-
- /* The token "if" terminates the expression and is NOT
- removed from the input stream. */
-
- if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
- return 0;
-
- pstate->lexptr += namelen;
-
- /* Catch specific keywords. */
-
- for (const auto &keyword : f_keywords)
- if (strlen (keyword.oper) == namelen
- && ((!keyword.case_sensitive
- && strncasecmp (tokstart, keyword.oper, namelen) == 0)
- || (keyword.case_sensitive
- && strncmp (tokstart, keyword.oper, namelen) == 0)))
- {
- yylval.opcode = keyword.opcode;
- return keyword.token;
- }
-
- yylval.sval.ptr = tokstart;
- yylval.sval.length = namelen;
-
- if (*tokstart == '$')
- return DOLLAR_VARIABLE;
-
- /* Use token-type TYPENAME for symbols that happen to be defined
- currently as names of types; NAME for other symbols.
- The caller is not constrained to care about the distinction. */
- {
- std::string tmp = copy_name (yylval.sval);
- struct block_symbol result;
- const domain_search_flags lookup_domains[] =
- {
- SEARCH_VFT,
- SEARCH_STRUCT_DOMAIN,
- SEARCH_MODULE_DOMAIN
- };
- int hextype;
-
- for (const auto &domain : lookup_domains)
- {
- result = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
- domain, NULL);
- if (result.symbol && result.symbol->loc_class () == LOC_TYPEDEF)
- {
- yylval.tsym.type = result.symbol->type ();
- return TYPENAME;
- }
-
- if (result.symbol)
- break;
- }
-
- yylval.tsym.type
- = language_lookup_primitive_type (pstate->language (),
- pstate->gdbarch (), tmp.c_str ());
- if (yylval.tsym.type != NULL)
- return TYPENAME;
-
- /* This is post the symbol search as symbols can hide intrinsics. Also,
- give Fortran intrinsics priority over C symbols. This prevents
- non-Fortran symbols from hiding intrinsics, for example abs. */
- if (!result.symbol || result.symbol->language () != language_fortran)
- for (const auto &intrinsic : f_intrinsics)
- {
- gdb_assert (!intrinsic.case_sensitive);
- if (strlen (intrinsic.oper) == namelen
- && strncasecmp (tokstart, intrinsic.oper, namelen) == 0)
- {
- yylval.opcode = intrinsic.opcode;
- return intrinsic.token;
- }
- }
-
- /* 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 (!result.symbol
- && ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
- || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
- {
- YYSTYPE newlval; /* Its value is ignored. */
- hextype = parse_number (pstate, tokstart, namelen, 0, &newlval);
- if (hextype == INT)
- {
- yylval.ssym.sym = result;
- yylval.ssym.is_a_field_of_this = false;
- return NAME_OR_INT;
- }
- }
-
- if (pstate->parse_completion && *pstate->lexptr == '\0')
- saw_name_at_eof = true;
-
- /* Any other kind of symbol */
- yylval.ssym.sym = result;
- yylval.ssym.is_a_field_of_this = false;
- return NAME;
- }
-}
-
-int
-f_language::parser (struct parser_state *par_state) const
-{
- /* Setting up the parser state. */
- scoped_restore pstate_restore = make_scoped_restore (&pstate);
- scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
- par_state->debug);
- gdb_assert (par_state != NULL);
- pstate = par_state;
- last_was_structop = false;
- saw_name_at_eof = false;
- paren_depth = 0;
-
- struct type_stack stack;
- scoped_restore restore_type_stack = make_scoped_restore (&type_stack,
- &stack);
-
- int result = yyparse ();
- if (!result)
- pstate->set_operation (pstate->pop ());
- return result;
-}
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 5a40995f3103..19ea1645970d 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -28,6 +28,7 @@
#include "varobj.h"
#include "gdbcore.h"
#include "f-lang.h"
+#include "f-exp-parser.h"
#include "valprint.h"
#include "value.h"
#include "cp-support.h"
@@ -1632,6 +1633,14 @@ fortran_structop_operation::evaluate (struct type *expect_type,
/* See language.h. */
+int
+f_language::parser (struct parser_state *ps) const
+{
+ return f_parse (ps);
+}
+
+/* See language.h. */
+
void
f_language::print_array_index (struct type *index_type, LONGEST index,
struct ui_file *stream,
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 16/19] gdb: move go-exp-parser.y's support code to go-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (14 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 15/19] gdb: move f-exp-parser.y's support code to f-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 17/19] gdb: move m2-exp-parser.y's support code to m2-exp-parser.c simon.marchi
` (2 subsequent siblings)
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Similar to the previous commits, but for the Go expression parser.
Like the Fortran parser, the Go parser is entered through the
go_language::parser method rather than a free function, so add a free
function go_parse as the entry point (like the other parsers) and turn
go_language::parser into a thin wrapper around it, defined in go-lang.c.
Put the parser support code inside the go_exp_parser namespace.
Change-Id: Icbe5290d86e783d979e4301ce55ef256d59fbea1
---
gdb/Makefile.in | 2 +
gdb/go-exp-parser.c | 955 ++++++++++++++++++++++++++++++++++++++++++++
gdb/go-exp-parser.h | 59 +++
gdb/go-exp-parser.y | 936 +------------------------------------------
gdb/go-lang.c | 9 +
5 files changed, 1028 insertions(+), 933 deletions(-)
create mode 100644 gdb/go-exp-parser.c
create mode 100644 gdb/go-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0a35f9507def..4289c5151fd0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1120,6 +1120,7 @@ COMMON_SFILES = \
gmp-utils.c \
gnu-v2-abi.c \
gnu-v3-abi.c \
+ go-exp-parser.c \
go-lang.c \
go-typeprint.c \
go-valprint.c \
@@ -1482,6 +1483,7 @@ HFILES_NO_SRCDIR = \
gmp-utils.h \
gnu-nat.h \
gnu-nat-mig.h \
+ go-exp-parser.h \
go-lang.h \
gregset.h \
guile/guile.h \
diff --git a/gdb/go-exp-parser.c b/gdb/go-exp-parser.c
new file mode 100644
index 000000000000..96418dfae4fc
--- /dev/null
+++ b/gdb/go-exp-parser.c
@@ -0,0 +1,955 @@
+/* YACC parser support code for Go expressions, for GDB.
+
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "go-exp-parser.h"
+#include "go-exp-parser-gen.h"
+#include "block.h"
+#include "c-exp-parser.h"
+#include "c-lang.h"
+#include "charset.h"
+#include "go-lang.h"
+#include "language.h"
+#include "parser-defs.h"
+#include "value.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ go-exp-parser-gen.c. Bison produces a declaration for go_yyparse in
+ go-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int go_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for go_yydebug. */
+
+extern int go_yydebug;
+
+namespace go_exp_parser
+{
+
+/* See go-exp-parser.h. */
+
+parser_state *pstate;
+
+/* See go-exp-parser.h. */
+
+int
+parse_number (struct parser_state *par_state,
+ const char *p, int len, int parsed_float,
+ go_exp_parser_YYSTYPE *putithere)
+{
+ ULONGEST n = 0;
+ ULONGEST prevn = 0;
+
+ int i = 0;
+ int c;
+ int base = input_radix;
+ int unsigned_p = 0;
+
+ /* Number of "L" suffixes encountered. */
+ int long_p = 0;
+
+ /* We have found a "L" or "U" suffix. */
+ int found_suffix = 0;
+
+ if (parsed_float)
+ {
+ const struct builtin_go_type *builtin_go_types
+ = builtin_go_type (par_state->gdbarch ());
+
+ /* Handle suffixes: 'f' for float32, 'l' for long double.
+ FIXME: This appears to be an extension -- do we want this? */
+ if (len >= 1 && c_tolower (p[len - 1]) == 'f')
+ {
+ putithere->typed_val_float.type
+ = builtin_go_types->builtin_float32;
+ len--;
+ }
+ else if (len >= 1 && c_tolower (p[len - 1]) == 'l')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_long_double;
+ len--;
+ }
+ /* Default type for floating-point literals is float64. */
+ else
+ {
+ putithere->typed_val_float.type
+ = builtin_go_types->builtin_float64;
+ }
+
+ if (!parse_float (p, len,
+ putithere->typed_val_float.type,
+ putithere->typed_val_float.val))
+ return ERROR;
+ return FLOAT;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 0. */
+ if (p[0] == '0' && len > 1)
+ switch (p[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 'b':
+ case 'B':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 2;
+ len -= 2;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ case 'd':
+ case 'D':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 10;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != 'l' && c != 'u')
+ n *= base;
+ if (c >= '0' && c <= '9')
+ {
+ if (found_suffix)
+ return ERROR;
+ n += i = c - '0';
+ }
+ else
+ {
+ if (base > 10 && c >= 'a' && c <= 'f')
+ {
+ if (found_suffix)
+ return ERROR;
+ n += i = c - 'a' + 10;
+ }
+ else if (c == 'l')
+ {
+ ++long_p;
+ found_suffix = 1;
+ }
+ else if (c == 'u')
+ {
+ unsigned_p = 1;
+ found_suffix = 1;
+ }
+ else
+ return ERROR; /* Char not a digit */
+ }
+ if (i >= base)
+ return ERROR; /* Invalid digit in this base. */
+
+ if (c != 'l' && c != 'u')
+ {
+ /* Test for overflow. */
+ if (n == 0 && prevn == 0)
+ ;
+ else if (prevn >= n)
+ error (_("Numeric constant too large."));
+ }
+ prevn = n;
+ }
+
+ /* An integer constant is an int, a long, or a long long. An L
+ suffix forces it to be long; an LL suffix forces it to be long
+ long. If not forced to a larger size, it gets the first type of
+ the above that it fits in. To figure out whether it fits, we
+ shift it right and see whether anything remains. Note that we
+ can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or more in one
+ operation, because many compilers will warn about such a shift
+ (which always produces a zero result). Sometimes gdbarch_int_bit
+ or gdbarch_long_bit will be that big, sometimes not. To deal with
+ the case where it is we just always shift the value more than
+ once, with fewer bits each time. */
+
+ int int_bits = gdbarch_int_bit (par_state->gdbarch ());
+ int long_bits = gdbarch_long_bit (par_state->gdbarch ());
+ int long_long_bits = gdbarch_long_long_bit (par_state->gdbarch ());
+ bool have_signed = !unsigned_p;
+ bool have_int = long_p == 0;
+ bool have_long = long_p <= 1;
+ if (have_int && have_signed && fits_in_type (1, n, int_bits, true))
+ putithere->typed_val_int.type = parse_type (par_state)->builtin_int;
+ else if (have_int && fits_in_type (1, n, int_bits, false))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_unsigned_int;
+ else if (have_long && have_signed && fits_in_type (1, n, long_bits, true))
+ putithere->typed_val_int.type = parse_type (par_state)->builtin_long;
+ else if (have_long && fits_in_type (1, n, long_bits, false))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_unsigned_long;
+ else if (have_signed && fits_in_type (1, n, long_long_bits, true))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_long_long;
+ else if (fits_in_type (1, n, long_long_bits, false))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_unsigned_long_long;
+ else
+ error (_("Numeric constant too large."));
+ putithere->typed_val_int.val = n;
+
+ return INT;
+}
+
+/* Temporary obstack used for holding strings. */
+static struct obstack tempbuf;
+static int tempbuf_init;
+
+/* Parse a string or character literal from TOKPTR. The string or
+ character may be wide or unicode. *OUTPTR is set to just after the
+ end of the literal in the input string. The resulting token is
+ stored in VALUE. This returns a token value, either STRING or
+ CHAR, depending on what was parsed. *HOST_CHARS is set to the
+ number of host characters in the literal. */
+
+static int
+parse_string_or_char (const char *tokptr, const char **outptr,
+ struct typed_stoken *value, int *host_chars)
+{
+ int quote;
+
+ /* Build the gdb internal form of the input string in tempbuf. Note
+ that the buffer is null byte terminated *only* for the
+ convenience of debugging gdb itself and printing the buffer
+ contents when the buffer contains no embedded nulls. Gdb does
+ not depend upon the buffer being null byte terminated, it uses
+ the length string instead. This allows gdb to handle C strings
+ (as well as strings in other languages) with embedded null
+ bytes */
+
+ if (!tempbuf_init)
+ tempbuf_init = 1;
+ else
+ obstack_free (&tempbuf, NULL);
+ obstack_init (&tempbuf);
+
+ /* Skip the quote. */
+ quote = *tokptr;
+ ++tokptr;
+
+ *host_chars = 0;
+
+ while (*tokptr)
+ {
+ char c = *tokptr;
+ if (c == '\\')
+ {
+ ++tokptr;
+ *host_chars += c_parse_escape (&tokptr, &tempbuf);
+ }
+ else if (c == quote)
+ break;
+ else
+ {
+ obstack_1grow (&tempbuf, c);
+ ++tokptr;
+ /* FIXME: this does the wrong thing with multi-byte host
+ characters. We could use mbrlen here, but that would
+ make "set host-charset" a bit less useful. */
+ ++*host_chars;
+ }
+ }
+
+ if (*tokptr != quote)
+ {
+ if (quote == '"')
+ error (_("Unterminated string in expression."));
+ else
+ error (_("Unmatched single quote."));
+ }
+ ++tokptr;
+
+ value->type = (int) C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/
+ value->ptr = (char *) obstack_base (&tempbuf);
+ value->length = obstack_object_size (&tempbuf);
+
+ *outptr = tokptr;
+
+ return quote == '\'' ? CHAR : STRING;
+}
+
+struct go_token
+{
+ const char *oper;
+ int token;
+ enum exp_opcode opcode;
+};
+
+static const struct go_token tokentab3[] =
+ {
+ {">>=", ASSIGN_MODIFY, BINOP_RSH},
+ {"<<=", ASSIGN_MODIFY, BINOP_LSH},
+ /*{"&^=", ASSIGN_MODIFY, BINOP_BITWISE_ANDNOT}, TODO */
+ {"...", DOTDOTDOT, OP_NULL},
+ };
+
+static const struct go_token tokentab2[] =
+ {
+ {"+=", ASSIGN_MODIFY, BINOP_ADD},
+ {"-=", ASSIGN_MODIFY, BINOP_SUB},
+ {"*=", ASSIGN_MODIFY, BINOP_MUL},
+ {"/=", ASSIGN_MODIFY, BINOP_DIV},
+ {"%=", ASSIGN_MODIFY, BINOP_REM},
+ {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
+ {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
+ {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
+ {"++", INCREMENT, OP_NULL},
+ {"--", DECREMENT, OP_NULL},
+ /*{"->", RIGHT_ARROW, OP_NULL}, Doesn't exist in Go. */
+ {"<-", LEFT_ARROW, OP_NULL},
+ {"&&", ANDAND, OP_NULL},
+ {"||", OROR, OP_NULL},
+ {"<<", LSH, OP_NULL},
+ {">>", RSH, OP_NULL},
+ {"==", EQUAL, OP_NULL},
+ {"!=", NOTEQUAL, OP_NULL},
+ {"<=", LEQ, OP_NULL},
+ {">=", GEQ, OP_NULL},
+ /*{"&^", ANDNOT, OP_NULL}, TODO */
+ };
+
+/* Identifier-like tokens. */
+static const struct go_token ident_tokens[] =
+ {
+ {"true", TRUE_KEYWORD, OP_NULL},
+ {"false", FALSE_KEYWORD, OP_NULL},
+ {"nil", NIL_KEYWORD, OP_NULL},
+ {"const", CONST_KEYWORD, OP_NULL},
+ {"struct", STRUCT_KEYWORD, OP_NULL},
+ {"type", TYPE_KEYWORD, OP_NULL},
+ {"interface", INTERFACE_KEYWORD, OP_NULL},
+ {"chan", CHAN_KEYWORD, OP_NULL},
+ {"byte", BYTE_KEYWORD, OP_NULL}, /* An alias of uint8. */
+ {"len", LEN_KEYWORD, OP_NULL},
+ {"cap", CAP_KEYWORD, OP_NULL},
+ {"new", NEW_KEYWORD, OP_NULL},
+ {"iota", IOTA_KEYWORD, OP_NULL},
+ };
+
+/* This is set if a NAME token appeared at the very end of the input
+ string, with no whitespace separating the name from the EOF. This
+ is used only when parsing to do field name completion. */
+static int saw_name_at_eof;
+
+/* This is set if the previously-returned token was a structure
+ operator -- either '.' or ARROW. This is used only when parsing to
+ do field name completion. */
+static int last_was_structop;
+
+/* Depth of parentheses. */
+static int paren_depth;
+
+/* Read one token, getting characters through lexptr. */
+
+static int
+lex_one_token (struct parser_state *par_state)
+{
+ int c;
+ int namelen;
+ const char *tokstart;
+ int saw_structop = last_was_structop;
+
+ last_was_structop = 0;
+
+ retry:
+
+ par_state->prev_lexptr = par_state->lexptr;
+
+ tokstart = par_state->lexptr;
+ /* See if it is a special token of length 3. */
+ for (const auto &token : tokentab3)
+ if (strncmp (tokstart, token.oper, 3) == 0)
+ {
+ par_state->lexptr += 3;
+ go_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ /* See if it is a special token of length 2. */
+ for (const auto &token : tokentab2)
+ if (strncmp (tokstart, token.oper, 2) == 0)
+ {
+ par_state->lexptr += 2;
+ go_yylval.opcode = token.opcode;
+ /* NOTE: -> doesn't exist in Go, so we don't need to watch for
+ setting last_was_structop here. */
+ return token.token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ if (saw_name_at_eof)
+ {
+ saw_name_at_eof = 0;
+ return COMPLETE;
+ }
+ else if (saw_structop)
+ return COMPLETE;
+ else
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ par_state->lexptr++;
+ goto retry;
+
+ case '[':
+ case '(':
+ paren_depth++;
+ par_state->lexptr++;
+ return c;
+
+ case ']':
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ par_state->lexptr++;
+ return c;
+
+ case ',':
+ if (pstate->comma_terminates
+ && paren_depth == 0)
+ return 0;
+ par_state->lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (par_state->lexptr[1] < '0' || par_state->lexptr[1] > '9')
+ {
+ if (pstate->parse_completion)
+ last_was_structop = 1;
+ goto symbol; /* Nope, must be a symbol. */
+ }
+ [[fallthrough]];
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0, toktype;
+ const char *p = tokstart;
+ int hex = input_radix > 10;
+
+ if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+ {
+ p += 2;
+ hex = 1;
+ }
+
+ for (;; ++p)
+ {
+ /* This test includes !hex because 'e' is a valid hex digit
+ and thus does not indicate a floating point number when
+ the radix is hex. */
+ if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ /* This test does not include !hex, because a '.' always indicates
+ a decimal floating point number regardless of the radix. */
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ /* We will take any letters or digits. parse_number will
+ complain if past the radix, or if L or U are not final. */
+ else if ((*p < '0' || *p > '9')
+ && ((*p < 'a' || *p > 'z')
+ && (*p < 'A' || *p > 'Z')))
+ break;
+ }
+ toktype = parse_number (par_state, tokstart, p - tokstart,
+ got_dot|got_e, &go_yylval);
+ if (toktype == ERROR)
+ error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+ tokstart);
+ par_state->lexptr = p;
+ return toktype;
+ }
+
+ case '@':
+ {
+ const char *p = &tokstart[1];
+ size_t len = strlen ("entry");
+
+ while (c_isspace (*p))
+ p++;
+ if (strncmp (p, "entry", len) == 0 && !c_isalnum (p[len])
+ && p[len] != '_')
+ {
+ par_state->lexptr = &p[len];
+ return ENTRY;
+ }
+ }
+ [[fallthrough]];
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '%':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '<':
+ case '>':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ par_state->lexptr++;
+ return c;
+
+ case '\'':
+ case '"':
+ case '`':
+ {
+ int host_len;
+ int result = parse_string_or_char (tokstart, &par_state->lexptr,
+ &go_yylval.tsval, &host_len);
+ if (result == CHAR)
+ {
+ if (host_len == 0)
+ error (_("Empty character constant."));
+ else if (host_len > 2 && c == '\'')
+ {
+ ++tokstart;
+ namelen = par_state->lexptr - tokstart - 1;
+ goto tryname;
+ }
+ else if (host_len > 1)
+ error (_("Invalid character constant."));
+ }
+ return result;
+ }
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error (_("Invalid character '%c' in expression."), c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));)
+ {
+ c = tokstart[++namelen];
+ }
+
+ /* The token "if" terminates the expression and is NOT removed from
+ the input stream. It doesn't count if it appears in the
+ expansion of a macro. */
+ if (namelen == 2
+ && tokstart[0] == 'i'
+ && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ /* For the same reason (breakpoint conditions), "thread N"
+ terminates the expression. "thread" could be an identifier, but
+ an identifier is never followed by a number without intervening
+ punctuation.
+ Handle abbreviations of these, similarly to
+ breakpoint.c:find_condition_and_thread.
+ TODO: Watch for "goroutine" here? */
+ if (namelen >= 1
+ && strncmp (tokstart, "thread", namelen) == 0
+ && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t'))
+ {
+ const char *p = skip_spaces (tokstart + namelen + 1);
+ if (*p >= '0' && *p <= '9')
+ return 0;
+ }
+
+ par_state->lexptr += namelen;
+
+ tryname:
+
+ go_yylval.sval.ptr = tokstart;
+ go_yylval.sval.length = namelen;
+
+ /* Catch specific keywords. */
+ std::string copy = copy_name (go_yylval.sval);
+ for (const auto &token : ident_tokens)
+ if (copy == token.oper)
+ {
+ /* It is ok to always set this, even though we don't always
+ strictly need to. */
+ go_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ if (*tokstart == '$')
+ return DOLLAR_VARIABLE;
+
+ if (pstate->parse_completion && *par_state->lexptr == '\0')
+ saw_name_at_eof = 1;
+ return NAME;
+}
+
+/* An object of this type is pushed on a FIFO by the "outer" lexer. */
+struct go_token_and_value
+{
+ int token;
+ go_exp_parser_YYSTYPE value;
+};
+
+/* A FIFO of tokens that have been read but not yet returned to the
+ parser. */
+static std::vector<go_token_and_value> token_fifo;
+
+/* Non-zero if the lexer should return tokens from the FIFO. */
+static int popping;
+
+/* Temporary storage for yylex; this holds symbol names as they are
+ built up. */
+static auto_obstack name_obstack;
+
+/* Build "package.name" in name_obstack.
+ For convenience of the caller, the name is NUL-terminated,
+ but the NUL is not included in the recorded length. */
+
+static struct stoken
+build_packaged_name (const char *package, int package_len,
+ const char *name, int name_len)
+{
+ struct stoken result;
+
+ name_obstack.clear ();
+ obstack_grow (&name_obstack, package, package_len);
+ obstack_grow_str (&name_obstack, ".");
+ obstack_grow (&name_obstack, name, name_len);
+ obstack_grow (&name_obstack, "", 1);
+ result.ptr = (char *) obstack_base (&name_obstack);
+ result.length = obstack_object_size (&name_obstack) - 1;
+
+ return result;
+}
+
+/* Return non-zero if NAME is a package name.
+ BLOCK is the scope in which to interpret NAME; this can be NULL
+ to mean the global scope. */
+
+static int
+package_name_p (const char *name, const struct block *block)
+{
+ struct symbol *sym;
+ struct field_of_this_result is_a_field_of_this;
+
+ sym = lookup_symbol (name, block, SEARCH_TYPE_DOMAIN,
+ &is_a_field_of_this).symbol;
+
+ if (sym
+ && sym->loc_class () == LOC_TYPEDEF
+ && sym->type ()->code () == TYPE_CODE_MODULE)
+ return 1;
+
+ return 0;
+}
+
+/* Classify a (potential) function in the "unsafe" package.
+ We fold these into "keywords" to keep things simple, at least until
+ something more complex is warranted. */
+
+static int
+classify_unsafe_function (struct stoken function_name)
+{
+ std::string copy = copy_name (function_name);
+
+ if (copy == "Sizeof")
+ {
+ go_yylval.sval = function_name;
+ return SIZEOF_KEYWORD;
+ }
+
+ error (_("Unknown function in `unsafe' package: %s"), copy.c_str ());
+}
+
+/* Classify token(s) "name1.name2" where name1 is known to be a package.
+ The contents of the token are in `yylval'.
+ Updates yylval and returns the new token type.
+
+ The result is one of NAME, NAME_OR_INT, or TYPENAME. */
+
+static int
+classify_packaged_name (const struct block *block)
+{
+ struct block_symbol sym;
+ struct field_of_this_result is_a_field_of_this;
+
+ std::string copy = copy_name (go_yylval.sval);
+
+ sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
+
+ if (sym.symbol)
+ {
+ go_yylval.ssym.sym = sym;
+ go_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ }
+
+ return NAME;
+}
+
+/* Classify a NAME token.
+ The contents of the token are in `yylval'.
+ Updates yylval and returns the new token type.
+ BLOCK is the block in which lookups start; this can be NULL
+ to mean the global scope.
+
+ The result is one of NAME, NAME_OR_INT, or TYPENAME. */
+
+static int
+classify_name (struct parser_state *par_state, const struct block *block)
+{
+ struct type *type;
+ struct block_symbol sym;
+ struct field_of_this_result is_a_field_of_this;
+
+ std::string copy = copy_name (go_yylval.sval);
+
+ /* Try primitive types first so they win over bad/weird debug info. */
+ type = language_lookup_primitive_type (par_state->language (),
+ par_state->gdbarch (),
+ copy.c_str ());
+ if (type != NULL)
+ {
+ /* NOTE: We take advantage of the fact that yylval coming in was a
+ NAME, and that struct ttype is a compatible extension of struct
+ stoken, so yylval.tsym.stoken is already filled in. */
+ go_yylval.tsym.type = type;
+ return TYPENAME;
+ }
+
+ /* TODO: What about other types? */
+
+ sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
+
+ if (sym.symbol)
+ {
+ go_yylval.ssym.sym = sym;
+ go_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ return NAME;
+ }
+
+ /* If we didn't find a symbol, look again in the current package.
+ This is to, e.g., make "p global_var" work without having to specify
+ the package name. We intentionally only looks for objects in the
+ current package. */
+
+ {
+ gdb::unique_xmalloc_ptr<char> current_package_name
+ = go_block_package_name (block);
+
+ if (current_package_name != NULL)
+ {
+ struct stoken sval =
+ build_packaged_name (current_package_name.get (),
+ strlen (current_package_name.get ()),
+ copy.c_str (), copy.size ());
+
+ sym = lookup_symbol (sval.ptr, block, SEARCH_VFT,
+ &is_a_field_of_this);
+ if (sym.symbol)
+ {
+ go_yylval.ssym.stoken = sval;
+ go_yylval.ssym.sym = sym;
+ go_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ return NAME;
+ }
+ }
+ }
+
+ /* Input names that aren't symbols but ARE valid hex numbers, when
+ the input radix permits them, can be names or numbers depending
+ on the parse. Note we support radixes > 16 here. */
+ if ((copy[0] >= 'a' && copy[0] < 'a' + input_radix - 10)
+ || (copy[0] >= 'A' && copy[0] < 'A' + input_radix - 10))
+ {
+ go_exp_parser_YYSTYPE newlval; /* Its value is ignored. */
+ int hextype = parse_number (par_state, copy.c_str (),
+ go_yylval.sval.length, 0, &newlval);
+ if (hextype == INT)
+ {
+ go_yylval.ssym.sym.symbol = NULL;
+ go_yylval.ssym.sym.block = NULL;
+ go_yylval.ssym.is_a_field_of_this = 0;
+ return NAME_OR_INT;
+ }
+ }
+
+ go_yylval.ssym.sym.symbol = NULL;
+ go_yylval.ssym.sym.block = NULL;
+ go_yylval.ssym.is_a_field_of_this = 0;
+ return NAME;
+}
+
+/* See go-exp-parser.h. */
+
+int
+go_yylex (void)
+{
+ go_token_and_value current, next;
+
+ if (popping && !token_fifo.empty ())
+ {
+ go_token_and_value tv = token_fifo[0];
+ token_fifo.erase (token_fifo.begin ());
+ go_yylval = tv.value;
+ /* There's no need to fall through to handle package.name
+ as that can never happen here. In theory. */
+ return tv.token;
+ }
+ popping = 0;
+
+ current.token = lex_one_token (pstate);
+
+ /* TODO: Need a way to force specifying name1 as a package.
+ .name1.name2 ? */
+
+ if (current.token != NAME)
+ return current.token;
+
+ /* See if we have "name1 . name2". */
+
+ current.value = go_yylval;
+ next.token = lex_one_token (pstate);
+ next.value = go_yylval;
+
+ if (next.token == '.')
+ {
+ go_token_and_value name2;
+
+ name2.token = lex_one_token (pstate);
+ name2.value = go_yylval;
+
+ if (name2.token == NAME)
+ {
+ /* Ok, we have "name1 . name2". */
+ std::string copy = copy_name (current.value.sval);
+
+ if (copy == "unsafe")
+ {
+ popping = 1;
+ return classify_unsafe_function (name2.value.sval);
+ }
+
+ if (package_name_p (copy.c_str (), pstate->expression_context_block))
+ {
+ popping = 1;
+ go_yylval.sval = build_packaged_name (current.value.sval.ptr,
+ current.value.sval.length,
+ name2.value.sval.ptr,
+ name2.value.sval.length);
+ return classify_packaged_name (pstate->expression_context_block);
+ }
+ }
+
+ token_fifo.push_back (next);
+ token_fifo.push_back (name2);
+ }
+ else
+ token_fifo.push_back (next);
+
+ /* If we arrive here we don't have a package-qualified name. */
+
+ popping = 1;
+ go_yylval = current.value;
+ return classify_name (pstate, pstate->expression_context_block);
+}
+
+/* See go-exp-parser.h. */
+
+void
+go_yyerror (const char *msg)
+{
+ pstate->parse_error (msg);
+}
+
+} /* namespace go_exp_parser */
+
+/* See go-exp-parser.h. */
+
+int
+go_parse (struct parser_state *par_state)
+{
+ using namespace go_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate);
+ gdb_assert (par_state != NULL);
+ pstate = par_state;
+
+ scoped_restore restore_yydebug = make_scoped_restore (&go_yydebug,
+ par_state->debug);
+
+ /* Initialize some state used by the lexer. */
+ last_was_structop = 0;
+ saw_name_at_eof = 0;
+ paren_depth = 0;
+
+ token_fifo.clear ();
+ popping = 0;
+ name_obstack.clear ();
+
+ int result = go_yyparse ();
+ if (!result)
+ pstate->set_operation (pstate->pop ());
+ return result;
+}
diff --git a/gdb/go-exp-parser.h b/gdb/go-exp-parser.h
new file mode 100644
index 000000000000..525f02d7f792
--- /dev/null
+++ b/gdb/go-exp-parser.h
@@ -0,0 +1,59 @@
+/* YACC parser support code for Go expressions, for GDB.
+
+ Copyright (C) 2012-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_GO_EXP_PARSER_H
+#define GDB_GO_EXP_PARSER_H
+
+#include "parser-defs.h"
+
+union go_exp_parser_YYSTYPE;
+
+namespace go_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+ expression. */
+
+extern parser_state *pstate;
+
+/* Take care of parsing a number (anything that starts with a digit).
+ Set yylval and return the token type; update lexptr.
+ LEN is the number of characters in it. */
+
+int parse_number (struct parser_state *par_state, const char *p, int len,
+ int parsed_float, go_exp_parser_YYSTYPE *putithere);
+
+/* This is taken from c-exp-parser.y mostly to get something working.
+ The basic structure has been kept because we may yet need some of it. */
+
+int go_yylex ();
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void go_yyerror (const char *msg);
+
+} /* namespace go_exp_parser */
+
+/* Parse a Go expression using the lexer input and context held in
+ PAR_STATE. On success, return 0 and leave the resulting operation set
+ on PAR_STATE. On failure, return non-zero. */
+
+int go_parse (struct parser_state *par_state);
+
+#endif /* GDB_GO_EXP_PARSER_H */
diff --git a/gdb/go-exp-parser.y b/gdb/go-exp-parser.y
index 2ae357081b36..c7f4e4f2da2e 100644
--- a/gdb/go-exp-parser.y
+++ b/gdb/go-exp-parser.y
@@ -55,23 +55,13 @@
#include "value.h"
#include "parser-defs.h"
#include "language.h"
-#include "c-lang.h"
-#include "c-exp-parser.h"
#include "go-lang.h"
-#include "charset.h"
+#include "go-exp-parser.h"
#include "block.h"
#include "expop.h"
-/* The state of the parser, used internally when we are parsing the
- expression. */
-
-static struct parser_state *pstate = NULL;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
+using namespace go_exp_parser;
+using namespace expr;
%}
@@ -101,14 +91,6 @@ static void yyerror (const char *);
struct stoken_vector svec;
}
-%{
-/* YYSTYPE gets defined by %union. */
-static int parse_number (struct parser_state *,
- const char *, int, int, YYSTYPE *);
-
-using namespace expr;
-%}
-
%type <voidval> exp exp1 type_exp start variable lcurly
%type <lval> rcurly
%type <tval> type
@@ -619,915 +601,3 @@ name_not_typename
| NAME_OR_INT
*/
;
-
-%%
-
-/* Take care of parsing a number (anything that starts with a digit).
- Set yylval and return the token type; update lexptr.
- LEN is the number of characters in it. */
-
-/* FIXME: Needs some error checking for the float case. */
-/* FIXME(dje): IWBN to use c-exp-parser.y's parse_number if we could.
- That will require moving the guts into a function that we both call
- as our YYSTYPE is different than c-exp-parser.y's */
-
-static int
-parse_number (struct parser_state *par_state,
- const char *p, int len, int parsed_float, YYSTYPE *putithere)
-{
- ULONGEST n = 0;
- ULONGEST prevn = 0;
-
- int i = 0;
- int c;
- int base = input_radix;
- int unsigned_p = 0;
-
- /* Number of "L" suffixes encountered. */
- int long_p = 0;
-
- /* We have found a "L" or "U" suffix. */
- int found_suffix = 0;
-
- if (parsed_float)
- {
- const struct builtin_go_type *builtin_go_types
- = builtin_go_type (par_state->gdbarch ());
-
- /* Handle suffixes: 'f' for float32, 'l' for long double.
- FIXME: This appears to be an extension -- do we want this? */
- if (len >= 1 && c_tolower (p[len - 1]) == 'f')
- {
- putithere->typed_val_float.type
- = builtin_go_types->builtin_float32;
- len--;
- }
- else if (len >= 1 && c_tolower (p[len - 1]) == 'l')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_long_double;
- len--;
- }
- /* Default type for floating-point literals is float64. */
- else
- {
- putithere->typed_val_float.type
- = builtin_go_types->builtin_float64;
- }
-
- if (!parse_float (p, len,
- putithere->typed_val_float.type,
- putithere->typed_val_float.val))
- return ERROR;
- return FLOAT;
- }
-
- /* Handle base-switching prefixes 0x, 0t, 0d, 0. */
- if (p[0] == '0' && len > 1)
- switch (p[1])
- {
- case 'x':
- case 'X':
- if (len >= 3)
- {
- p += 2;
- base = 16;
- len -= 2;
- }
- break;
-
- case 'b':
- case 'B':
- if (len >= 3)
- {
- p += 2;
- base = 2;
- len -= 2;
- }
- break;
-
- case 't':
- case 'T':
- case 'd':
- case 'D':
- if (len >= 3)
- {
- p += 2;
- base = 10;
- len -= 2;
- }
- break;
-
- default:
- base = 8;
- break;
- }
-
- while (len-- > 0)
- {
- c = *p++;
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- if (c != 'l' && c != 'u')
- n *= base;
- if (c >= '0' && c <= '9')
- {
- if (found_suffix)
- return ERROR;
- n += i = c - '0';
- }
- else
- {
- if (base > 10 && c >= 'a' && c <= 'f')
- {
- if (found_suffix)
- return ERROR;
- n += i = c - 'a' + 10;
- }
- else if (c == 'l')
- {
- ++long_p;
- found_suffix = 1;
- }
- else if (c == 'u')
- {
- unsigned_p = 1;
- found_suffix = 1;
- }
- else
- return ERROR; /* Char not a digit */
- }
- if (i >= base)
- return ERROR; /* Invalid digit in this base. */
-
- if (c != 'l' && c != 'u')
- {
- /* Test for overflow. */
- if (n == 0 && prevn == 0)
- ;
- else if (prevn >= n)
- error (_("Numeric constant too large."));
- }
- prevn = n;
- }
-
- /* An integer constant is an int, a long, or a long long. An L
- suffix forces it to be long; an LL suffix forces it to be long
- long. If not forced to a larger size, it gets the first type of
- the above that it fits in. To figure out whether it fits, we
- shift it right and see whether anything remains. Note that we
- can't shift sizeof (LONGEST) * HOST_CHAR_BIT bits or more in one
- operation, because many compilers will warn about such a shift
- (which always produces a zero result). Sometimes gdbarch_int_bit
- or gdbarch_long_bit will be that big, sometimes not. To deal with
- the case where it is we just always shift the value more than
- once, with fewer bits each time. */
-
- int int_bits = gdbarch_int_bit (par_state->gdbarch ());
- int long_bits = gdbarch_long_bit (par_state->gdbarch ());
- int long_long_bits = gdbarch_long_long_bit (par_state->gdbarch ());
- bool have_signed = !unsigned_p;
- bool have_int = long_p == 0;
- bool have_long = long_p <= 1;
- if (have_int && have_signed && fits_in_type (1, n, int_bits, true))
- putithere->typed_val_int.type = parse_type (par_state)->builtin_int;
- else if (have_int && fits_in_type (1, n, int_bits, false))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_unsigned_int;
- else if (have_long && have_signed && fits_in_type (1, n, long_bits, true))
- putithere->typed_val_int.type = parse_type (par_state)->builtin_long;
- else if (have_long && fits_in_type (1, n, long_bits, false))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_unsigned_long;
- else if (have_signed && fits_in_type (1, n, long_long_bits, true))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_long_long;
- else if (fits_in_type (1, n, long_long_bits, false))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_unsigned_long_long;
- else
- error (_("Numeric constant too large."));
- putithere->typed_val_int.val = n;
-
- return INT;
-}
-
-/* Temporary obstack used for holding strings. */
-static struct obstack tempbuf;
-static int tempbuf_init;
-
-/* Parse a string or character literal from TOKPTR. The string or
- character may be wide or unicode. *OUTPTR is set to just after the
- end of the literal in the input string. The resulting token is
- stored in VALUE. This returns a token value, either STRING or
- CHAR, depending on what was parsed. *HOST_CHARS is set to the
- number of host characters in the literal. */
-
-static int
-parse_string_or_char (const char *tokptr, const char **outptr,
- struct typed_stoken *value, int *host_chars)
-{
- int quote;
-
- /* Build the gdb internal form of the input string in tempbuf. Note
- that the buffer is null byte terminated *only* for the
- convenience of debugging gdb itself and printing the buffer
- contents when the buffer contains no embedded nulls. Gdb does
- not depend upon the buffer being null byte terminated, it uses
- the length string instead. This allows gdb to handle C strings
- (as well as strings in other languages) with embedded null
- bytes */
-
- if (!tempbuf_init)
- tempbuf_init = 1;
- else
- obstack_free (&tempbuf, NULL);
- obstack_init (&tempbuf);
-
- /* Skip the quote. */
- quote = *tokptr;
- ++tokptr;
-
- *host_chars = 0;
-
- while (*tokptr)
- {
- char c = *tokptr;
- if (c == '\\')
- {
- ++tokptr;
- *host_chars += c_parse_escape (&tokptr, &tempbuf);
- }
- else if (c == quote)
- break;
- else
- {
- obstack_1grow (&tempbuf, c);
- ++tokptr;
- /* FIXME: this does the wrong thing with multi-byte host
- characters. We could use mbrlen here, but that would
- make "set host-charset" a bit less useful. */
- ++*host_chars;
- }
- }
-
- if (*tokptr != quote)
- {
- if (quote == '"')
- error (_("Unterminated string in expression."));
- else
- error (_("Unmatched single quote."));
- }
- ++tokptr;
-
- value->type = (int) C_STRING | (quote == '\'' ? C_CHAR : 0); /*FIXME*/
- value->ptr = (char *) obstack_base (&tempbuf);
- value->length = obstack_object_size (&tempbuf);
-
- *outptr = tokptr;
-
- return quote == '\'' ? CHAR : STRING;
-}
-
-struct go_token
-{
- const char *oper;
- int token;
- enum exp_opcode opcode;
-};
-
-static const struct go_token tokentab3[] =
- {
- {">>=", ASSIGN_MODIFY, BINOP_RSH},
- {"<<=", ASSIGN_MODIFY, BINOP_LSH},
- /*{"&^=", ASSIGN_MODIFY, BINOP_BITWISE_ANDNOT}, TODO */
- {"...", DOTDOTDOT, OP_NULL},
- };
-
-static const struct go_token tokentab2[] =
- {
- {"+=", ASSIGN_MODIFY, BINOP_ADD},
- {"-=", ASSIGN_MODIFY, BINOP_SUB},
- {"*=", ASSIGN_MODIFY, BINOP_MUL},
- {"/=", ASSIGN_MODIFY, BINOP_DIV},
- {"%=", ASSIGN_MODIFY, BINOP_REM},
- {"|=", ASSIGN_MODIFY, BINOP_BITWISE_IOR},
- {"&=", ASSIGN_MODIFY, BINOP_BITWISE_AND},
- {"^=", ASSIGN_MODIFY, BINOP_BITWISE_XOR},
- {"++", INCREMENT, OP_NULL},
- {"--", DECREMENT, OP_NULL},
- /*{"->", RIGHT_ARROW, OP_NULL}, Doesn't exist in Go. */
- {"<-", LEFT_ARROW, OP_NULL},
- {"&&", ANDAND, OP_NULL},
- {"||", OROR, OP_NULL},
- {"<<", LSH, OP_NULL},
- {">>", RSH, OP_NULL},
- {"==", EQUAL, OP_NULL},
- {"!=", NOTEQUAL, OP_NULL},
- {"<=", LEQ, OP_NULL},
- {">=", GEQ, OP_NULL},
- /*{"&^", ANDNOT, OP_NULL}, TODO */
- };
-
-/* Identifier-like tokens. */
-static const struct go_token ident_tokens[] =
- {
- {"true", TRUE_KEYWORD, OP_NULL},
- {"false", FALSE_KEYWORD, OP_NULL},
- {"nil", NIL_KEYWORD, OP_NULL},
- {"const", CONST_KEYWORD, OP_NULL},
- {"struct", STRUCT_KEYWORD, OP_NULL},
- {"type", TYPE_KEYWORD, OP_NULL},
- {"interface", INTERFACE_KEYWORD, OP_NULL},
- {"chan", CHAN_KEYWORD, OP_NULL},
- {"byte", BYTE_KEYWORD, OP_NULL}, /* An alias of uint8. */
- {"len", LEN_KEYWORD, OP_NULL},
- {"cap", CAP_KEYWORD, OP_NULL},
- {"new", NEW_KEYWORD, OP_NULL},
- {"iota", IOTA_KEYWORD, OP_NULL},
- };
-
-/* This is set if a NAME token appeared at the very end of the input
- string, with no whitespace separating the name from the EOF. This
- is used only when parsing to do field name completion. */
-static int saw_name_at_eof;
-
-/* This is set if the previously-returned token was a structure
- operator -- either '.' or ARROW. This is used only when parsing to
- do field name completion. */
-static int last_was_structop;
-
-/* Depth of parentheses. */
-static int paren_depth;
-
-/* Read one token, getting characters through lexptr. */
-
-static int
-lex_one_token (struct parser_state *par_state)
-{
- int c;
- int namelen;
- const char *tokstart;
- int saw_structop = last_was_structop;
-
- last_was_structop = 0;
-
- retry:
-
- par_state->prev_lexptr = par_state->lexptr;
-
- tokstart = par_state->lexptr;
- /* See if it is a special token of length 3. */
- for (const auto &token : tokentab3)
- if (strncmp (tokstart, token.oper, 3) == 0)
- {
- par_state->lexptr += 3;
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- /* See if it is a special token of length 2. */
- for (const auto &token : tokentab2)
- if (strncmp (tokstart, token.oper, 2) == 0)
- {
- par_state->lexptr += 2;
- yylval.opcode = token.opcode;
- /* NOTE: -> doesn't exist in Go, so we don't need to watch for
- setting last_was_structop here. */
- return token.token;
- }
-
- switch (c = *tokstart)
- {
- case 0:
- if (saw_name_at_eof)
- {
- saw_name_at_eof = 0;
- return COMPLETE;
- }
- else if (saw_structop)
- return COMPLETE;
- else
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- par_state->lexptr++;
- goto retry;
-
- case '[':
- case '(':
- paren_depth++;
- par_state->lexptr++;
- return c;
-
- case ']':
- case ')':
- if (paren_depth == 0)
- return 0;
- paren_depth--;
- par_state->lexptr++;
- return c;
-
- case ',':
- if (pstate->comma_terminates
- && paren_depth == 0)
- return 0;
- par_state->lexptr++;
- return c;
-
- case '.':
- /* Might be a floating point number. */
- if (par_state->lexptr[1] < '0' || par_state->lexptr[1] > '9')
- {
- if (pstate->parse_completion)
- last_was_structop = 1;
- goto symbol; /* Nope, must be a symbol. */
- }
- [[fallthrough]];
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- /* It's a number. */
- int got_dot = 0, got_e = 0, toktype;
- const char *p = tokstart;
- int hex = input_radix > 10;
-
- if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
- {
- p += 2;
- hex = 1;
- }
-
- for (;; ++p)
- {
- /* This test includes !hex because 'e' is a valid hex digit
- and thus does not indicate a floating point number when
- the radix is hex. */
- if (!hex && !got_e && (*p == 'e' || *p == 'E'))
- got_dot = got_e = 1;
- /* This test does not include !hex, because a '.' always indicates
- a decimal floating point number regardless of the radix. */
- else if (!got_dot && *p == '.')
- got_dot = 1;
- else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
- && (*p == '-' || *p == '+'))
- /* This is the sign of the exponent, not the end of the
- number. */
- continue;
- /* We will take any letters or digits. parse_number will
- complain if past the radix, or if L or U are not final. */
- else if ((*p < '0' || *p > '9')
- && ((*p < 'a' || *p > 'z')
- && (*p < 'A' || *p > 'Z')))
- break;
- }
- toktype = parse_number (par_state, tokstart, p - tokstart,
- got_dot|got_e, &yylval);
- if (toktype == ERROR)
- error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
- tokstart);
- par_state->lexptr = p;
- return toktype;
- }
-
- case '@':
- {
- const char *p = &tokstart[1];
- size_t len = strlen ("entry");
-
- while (c_isspace (*p))
- p++;
- if (strncmp (p, "entry", len) == 0 && !c_isalnum (p[len])
- && p[len] != '_')
- {
- par_state->lexptr = &p[len];
- return ENTRY;
- }
- }
- [[fallthrough]];
- case '+':
- case '-':
- case '*':
- case '/':
- case '%':
- case '|':
- case '&':
- case '^':
- case '~':
- case '!':
- case '<':
- case '>':
- case '?':
- case ':':
- case '=':
- case '{':
- case '}':
- symbol:
- par_state->lexptr++;
- return c;
-
- case '\'':
- case '"':
- case '`':
- {
- int host_len;
- int result = parse_string_or_char (tokstart, &par_state->lexptr,
- &yylval.tsval, &host_len);
- if (result == CHAR)
- {
- if (host_len == 0)
- error (_("Empty character constant."));
- else if (host_len > 2 && c == '\'')
- {
- ++tokstart;
- namelen = par_state->lexptr - tokstart - 1;
- goto tryname;
- }
- else if (host_len > 1)
- error (_("Invalid character constant."));
- }
- return result;
- }
- }
-
- if (!(c == '_' || c == '$'
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
- /* We must have come across a bad character (e.g. ';'). */
- error (_("Invalid character '%c' in expression."), c);
-
- /* It's a name. See how long it is. */
- namelen = 0;
- for (c = tokstart[namelen];
- (c == '_' || c == '$' || (c >= '0' && c <= '9')
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));)
- {
- c = tokstart[++namelen];
- }
-
- /* The token "if" terminates the expression and is NOT removed from
- the input stream. It doesn't count if it appears in the
- expansion of a macro. */
- if (namelen == 2
- && tokstart[0] == 'i'
- && tokstart[1] == 'f')
- {
- return 0;
- }
-
- /* For the same reason (breakpoint conditions), "thread N"
- terminates the expression. "thread" could be an identifier, but
- an identifier is never followed by a number without intervening
- punctuation.
- Handle abbreviations of these, similarly to
- breakpoint.c:find_condition_and_thread.
- TODO: Watch for "goroutine" here? */
- if (namelen >= 1
- && strncmp (tokstart, "thread", namelen) == 0
- && (tokstart[namelen] == ' ' || tokstart[namelen] == '\t'))
- {
- const char *p = skip_spaces (tokstart + namelen + 1);
- if (*p >= '0' && *p <= '9')
- return 0;
- }
-
- par_state->lexptr += namelen;
-
- tryname:
-
- yylval.sval.ptr = tokstart;
- yylval.sval.length = namelen;
-
- /* Catch specific keywords. */
- std::string copy = copy_name (yylval.sval);
- for (const auto &token : ident_tokens)
- if (copy == token.oper)
- {
- /* It is ok to always set this, even though we don't always
- strictly need to. */
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- if (*tokstart == '$')
- return DOLLAR_VARIABLE;
-
- if (pstate->parse_completion && *par_state->lexptr == '\0')
- saw_name_at_eof = 1;
- return NAME;
-}
-
-/* An object of this type is pushed on a FIFO by the "outer" lexer. */
-struct go_token_and_value
-{
- int token;
- YYSTYPE value;
-};
-
-/* A FIFO of tokens that have been read but not yet returned to the
- parser. */
-static std::vector<go_token_and_value> token_fifo;
-
-/* Non-zero if the lexer should return tokens from the FIFO. */
-static int popping;
-
-/* Temporary storage for yylex; this holds symbol names as they are
- built up. */
-static auto_obstack name_obstack;
-
-/* Build "package.name" in name_obstack.
- For convenience of the caller, the name is NUL-terminated,
- but the NUL is not included in the recorded length. */
-
-static struct stoken
-build_packaged_name (const char *package, int package_len,
- const char *name, int name_len)
-{
- struct stoken result;
-
- name_obstack.clear ();
- obstack_grow (&name_obstack, package, package_len);
- obstack_grow_str (&name_obstack, ".");
- obstack_grow (&name_obstack, name, name_len);
- obstack_grow (&name_obstack, "", 1);
- result.ptr = (char *) obstack_base (&name_obstack);
- result.length = obstack_object_size (&name_obstack) - 1;
-
- return result;
-}
-
-/* Return non-zero if NAME is a package name.
- BLOCK is the scope in which to interpret NAME; this can be NULL
- to mean the global scope. */
-
-static int
-package_name_p (const char *name, const struct block *block)
-{
- struct symbol *sym;
- struct field_of_this_result is_a_field_of_this;
-
- sym = lookup_symbol (name, block, SEARCH_TYPE_DOMAIN,
- &is_a_field_of_this).symbol;
-
- if (sym
- && sym->loc_class () == LOC_TYPEDEF
- && sym->type ()->code () == TYPE_CODE_MODULE)
- return 1;
-
- return 0;
-}
-
-/* Classify a (potential) function in the "unsafe" package.
- We fold these into "keywords" to keep things simple, at least until
- something more complex is warranted. */
-
-static int
-classify_unsafe_function (struct stoken function_name)
-{
- std::string copy = copy_name (function_name);
-
- if (copy == "Sizeof")
- {
- yylval.sval = function_name;
- return SIZEOF_KEYWORD;
- }
-
- error (_("Unknown function in `unsafe' package: %s"), copy.c_str ());
-}
-
-/* Classify token(s) "name1.name2" where name1 is known to be a package.
- The contents of the token are in `yylval'.
- Updates yylval and returns the new token type.
-
- The result is one of NAME, NAME_OR_INT, or TYPENAME. */
-
-static int
-classify_packaged_name (const struct block *block)
-{
- struct block_symbol sym;
- struct field_of_this_result is_a_field_of_this;
-
- std::string copy = copy_name (yylval.sval);
-
- sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
-
- if (sym.symbol)
- {
- yylval.ssym.sym = sym;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- }
-
- return NAME;
-}
-
-/* Classify a NAME token.
- The contents of the token are in `yylval'.
- Updates yylval and returns the new token type.
- BLOCK is the block in which lookups start; this can be NULL
- to mean the global scope.
-
- The result is one of NAME, NAME_OR_INT, or TYPENAME. */
-
-static int
-classify_name (struct parser_state *par_state, const struct block *block)
-{
- struct type *type;
- struct block_symbol sym;
- struct field_of_this_result is_a_field_of_this;
-
- std::string copy = copy_name (yylval.sval);
-
- /* Try primitive types first so they win over bad/weird debug info. */
- type = language_lookup_primitive_type (par_state->language (),
- par_state->gdbarch (),
- copy.c_str ());
- if (type != NULL)
- {
- /* NOTE: We take advantage of the fact that yylval coming in was a
- NAME, and that struct ttype is a compatible extension of struct
- stoken, so yylval.tsym.stoken is already filled in. */
- yylval.tsym.type = type;
- return TYPENAME;
- }
-
- /* TODO: What about other types? */
-
- sym = lookup_symbol (copy.c_str (), block, SEARCH_VFT, &is_a_field_of_this);
-
- if (sym.symbol)
- {
- yylval.ssym.sym = sym;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- return NAME;
- }
-
- /* If we didn't find a symbol, look again in the current package.
- This is to, e.g., make "p global_var" work without having to specify
- the package name. We intentionally only looks for objects in the
- current package. */
-
- {
- gdb::unique_xmalloc_ptr<char> current_package_name
- = go_block_package_name (block);
-
- if (current_package_name != NULL)
- {
- struct stoken sval =
- build_packaged_name (current_package_name.get (),
- strlen (current_package_name.get ()),
- copy.c_str (), copy.size ());
-
- sym = lookup_symbol (sval.ptr, block, SEARCH_VFT,
- &is_a_field_of_this);
- if (sym.symbol)
- {
- yylval.ssym.stoken = sval;
- yylval.ssym.sym = sym;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- return NAME;
- }
- }
- }
-
- /* Input names that aren't symbols but ARE valid hex numbers, when
- the input radix permits them, can be names or numbers depending
- on the parse. Note we support radixes > 16 here. */
- if ((copy[0] >= 'a' && copy[0] < 'a' + input_radix - 10)
- || (copy[0] >= 'A' && copy[0] < 'A' + input_radix - 10))
- {
- YYSTYPE newlval; /* Its value is ignored. */
- int hextype = parse_number (par_state, copy.c_str (),
- yylval.sval.length, 0, &newlval);
- if (hextype == INT)
- {
- yylval.ssym.sym.symbol = NULL;
- yylval.ssym.sym.block = NULL;
- yylval.ssym.is_a_field_of_this = 0;
- return NAME_OR_INT;
- }
- }
-
- yylval.ssym.sym.symbol = NULL;
- yylval.ssym.sym.block = NULL;
- yylval.ssym.is_a_field_of_this = 0;
- return NAME;
-}
-
-/* This is taken from c-exp-parser.y mostly to get something working.
- The basic structure has been kept because we may yet need some of it. */
-
-static int
-yylex (void)
-{
- go_token_and_value current, next;
-
- if (popping && !token_fifo.empty ())
- {
- go_token_and_value tv = token_fifo[0];
- token_fifo.erase (token_fifo.begin ());
- yylval = tv.value;
- /* There's no need to fall through to handle package.name
- as that can never happen here. In theory. */
- return tv.token;
- }
- popping = 0;
-
- current.token = lex_one_token (pstate);
-
- /* TODO: Need a way to force specifying name1 as a package.
- .name1.name2 ? */
-
- if (current.token != NAME)
- return current.token;
-
- /* See if we have "name1 . name2". */
-
- current.value = yylval;
- next.token = lex_one_token (pstate);
- next.value = yylval;
-
- if (next.token == '.')
- {
- go_token_and_value name2;
-
- name2.token = lex_one_token (pstate);
- name2.value = yylval;
-
- if (name2.token == NAME)
- {
- /* Ok, we have "name1 . name2". */
- std::string copy = copy_name (current.value.sval);
-
- if (copy == "unsafe")
- {
- popping = 1;
- return classify_unsafe_function (name2.value.sval);
- }
-
- if (package_name_p (copy.c_str (), pstate->expression_context_block))
- {
- popping = 1;
- yylval.sval = build_packaged_name (current.value.sval.ptr,
- current.value.sval.length,
- name2.value.sval.ptr,
- name2.value.sval.length);
- return classify_packaged_name (pstate->expression_context_block);
- }
- }
-
- token_fifo.push_back (next);
- token_fifo.push_back (name2);
- }
- else
- token_fifo.push_back (next);
-
- /* If we arrive here we don't have a package-qualified name. */
-
- popping = 1;
- yylval = current.value;
- return classify_name (pstate, pstate->expression_context_block);
-}
-
-/* See language.h. */
-
-int
-go_language::parser (struct parser_state *par_state) const
-{
- /* Setting up the parser state. */
- scoped_restore pstate_restore = make_scoped_restore (&pstate);
- gdb_assert (par_state != NULL);
- pstate = par_state;
-
- scoped_restore restore_yydebug = make_scoped_restore (&yydebug,
- par_state->debug);
-
- /* Initialize some state used by the lexer. */
- last_was_structop = 0;
- saw_name_at_eof = 0;
- paren_depth = 0;
-
- token_fifo.clear ();
- popping = 0;
- name_obstack.clear ();
-
- int result = yyparse ();
- if (!result)
- pstate->set_operation (pstate->pop ());
- return result;
-}
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 3b388c961fab..618e51cb40dd 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -37,6 +37,7 @@
#include "language.h"
#include "varobj.h"
#include "go-lang.h"
+#include "go-exp-parser.h"
#include "c-lang.h"
#include "parser-defs.h"
#include "gdbarch.h"
@@ -456,6 +457,14 @@ go_block_package_name (const struct block *block)
/* See language.h. */
+int
+go_language::parser (struct parser_state *ps) const
+{
+ return go_parse (ps);
+}
+
+/* See language.h. */
+
void
go_language::language_arch_info (struct gdbarch *gdbarch,
struct language_arch_info *lai) const
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 17/19] gdb: move m2-exp-parser.y's support code to m2-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (15 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 16/19] gdb: move go-exp-parser.y's support code to go-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 18/19] gdb: move p-exp-parser.y's support code to p-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 19/19] gdb: honor "set debug parser" in the Modula-2 and Pascal parsers simon.marchi
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Similar to the previous commits, but for the Modula-2 expression parser.
Like the Fortran parser, the Modula-2 parser is entered through the
m2_language::parser method rather than a free function, so add a free
function m2_parse as the entry point (like the other parsers) and turn
m2_language::parser into a thin wrapper around it, defined in m2-lang.c.
Put the parser support code inside the m2_exp_parser namespace.
Change-Id: I92669a1af7fb81cf59bfb41e2b8c63ce323652d2
---
gdb/Makefile.in | 2 +
gdb/m2-exp-parser.c | 488 ++++++++++++++++++++++++++++++++++++++++++++
gdb/m2-exp-parser.h | 67 ++++++
gdb/m2-exp-parser.y | 465 +----------------------------------------
gdb/m2-lang.c | 9 +
5 files changed, 568 insertions(+), 463 deletions(-)
create mode 100644 gdb/m2-exp-parser.c
create mode 100644 gdb/m2-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4289c5151fd0..1c4ba5a12d57 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1137,6 +1137,7 @@ COMMON_SFILES = \
language.c \
linespec.c \
location.c \
+ m2-exp-parser.c \
m2-lang.c \
m2-typeprint.c \
m2-valprint.c \
@@ -1518,6 +1519,7 @@ HFILES_NO_SRCDIR = \
linux-tdep.h \
location.h \
loongarch-tdep.h \
+ m2-exp-parser.h \
m2-exp.h \
m2-lang.h \
m32r-tdep.h \
diff --git a/gdb/m2-exp-parser.c b/gdb/m2-exp-parser.c
new file mode 100644
index 000000000000..821f1507aef3
--- /dev/null
+++ b/gdb/m2-exp-parser.c
@@ -0,0 +1,488 @@
+/* YACC parser support code for Modula-2 expressions, 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 "m2-exp-parser.h"
+#include "m2-exp-parser-gen.h"
+#include "block.h"
+#include "expression.h"
+#include "language.h"
+#include "m2-exp.h"
+#include "parser-defs.h"
+#include "value.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ m2-exp-parser-gen.c. Bison produces a declaration for m2_yyparse in
+ m2-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int m2_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for m2_yydebug. */
+
+extern int m2_yydebug;
+
+namespace m2_exp_parser
+{
+
+/* See m2-exp-parser.h. */
+
+parser_state *pstate;
+
+/* See m2-exp-parser.h. */
+
+int number_sign = 1;
+
+/* 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 (int olen)
+{
+ const char *p = pstate->lexptr;
+ ULONGEST n = 0;
+ ULONGEST prevn = 0;
+ int c,i,ischar=0;
+ int base = input_radix;
+ int len = olen;
+
+ if(p[len-1] == 'H')
+ {
+ base = 16;
+ len--;
+ }
+ else if(p[len-1] == 'C' || p[len-1] == 'B')
+ {
+ base = 8;
+ ischar = p[len-1] == 'C';
+ len--;
+ }
+
+ /* Scan the number */
+ for (c = 0; c < len; c++)
+ {
+ if (p[c] == '.' && base == 10)
+ {
+ /* It's a float since it contains a point. */
+ if (!parse_float (p, len,
+ parse_m2_type (pstate)->builtin_real,
+ m2_yylval.val))
+ return ERROR;
+
+ pstate->lexptr += len;
+ return FLOAT;
+ }
+ if (p[c] == '.' && base != 10)
+ error (_("Floating point numbers must be base 10."));
+ if (base == 10 && (p[c] < '0' || p[c] > '9'))
+ error (_("Invalid digit \'%c\' in number."),p[c]);
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ n *= base;
+ if( base == 8 && (c == '8' || c == '9'))
+ error (_("Invalid digit \'%c\' in octal number."),c);
+ if (c >= '0' && c <= '9')
+ i = c - '0';
+ else
+ {
+ if (base == 16 && c >= 'A' && c <= 'F')
+ i = c - 'A' + 10;
+ else
+ return ERROR;
+ }
+ n+=i;
+ if(i >= base)
+ return ERROR;
+ if (n == 0 && prevn == 0)
+ ;
+ else if (RANGE_CHECK && prevn >= n)
+ range_error (_("Overflow on numeric constant."));
+
+ prevn=n;
+ }
+
+ pstate->lexptr = p;
+ if(*p == 'B' || *p == 'C' || *p == 'H')
+ pstate->lexptr++; /* Advance past B,C or H */
+
+ if (ischar)
+ {
+ m2_yylval.ulval = n;
+ return CHAR;
+ }
+
+ int int_bits = gdbarch_int_bit (pstate->gdbarch ());
+ bool have_signed = number_sign == -1;
+ bool have_unsigned = number_sign == 1;
+ if (have_signed && fits_in_type (number_sign, n, int_bits, true))
+ {
+ m2_yylval.lval = n;
+ return INT;
+ }
+ else if (have_unsigned && fits_in_type (number_sign, n, int_bits, false))
+ {
+ m2_yylval.ulval = n;
+ return UINT;
+ }
+ else
+ error (_("Overflow on numeric constant."));
+}
+
+/* Some tokens */
+
+static struct
+{
+ char name[2];
+ int token;
+} tokentab2[] =
+{
+ { {'<', '>'}, NOTEQUAL },
+ { {':', '='}, ASSIGN },
+ { {'<', '='}, LEQ },
+ { {'>', '='}, GEQ },
+ { {':', ':'}, COLONCOLON },
+
+};
+
+/* Some specific keywords */
+
+struct keyword {
+ char keyw[10];
+ int token;
+};
+
+static struct keyword keytab[] =
+{
+ {"OR" , OROR },
+ {"IN", IN },/* Note space after IN */
+ {"AND", LOGICAL_AND},
+ {"ABS", ABS },
+ {"ADR", ADR },
+ {"CHR", CHR },
+ {"DEC", DEC },
+ {"NOT", NOT },
+ {"DIV", DIV },
+ {"INC", INC },
+ {"MAX", MAX_FUNC },
+ {"MIN", MIN_FUNC },
+ {"MOD", MOD },
+ {"ODD", ODD },
+ {"CAP", CAP },
+ {"ORD", ORD },
+ {"VAL", VAL },
+ {"EXCL", EXCL },
+ {"HIGH", HIGH },
+ {"INCL", INCL },
+ {"SIZE", SIZE },
+ {"FLOAT", FLOAT_FUNC },
+ {"TRUNC", TRUNC },
+ {"TSIZE", SIZE },
+};
+
+/* Depth of parentheses. */
+static int paren_depth;
+
+/* See m2-exp-parser.h. */
+
+int
+m2_yylex (void)
+{
+ int c;
+ int namelen;
+ int i;
+ const char *tokstart;
+ char quote;
+
+ retry:
+
+ pstate->prev_lexptr = pstate->lexptr;
+
+ tokstart = pstate->lexptr;
+
+
+ /* See if it is a special token of length 2 */
+ for( i = 0 ; i < (int) (sizeof tokentab2 / sizeof tokentab2[0]) ; i++)
+ if (strncmp (tokentab2[i].name, tokstart, 2) == 0)
+ {
+ pstate->lexptr += 2;
+ return tokentab2[i].token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ pstate->lexptr++;
+ goto retry;
+
+ case '(':
+ paren_depth++;
+ pstate->lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ pstate->lexptr++;
+ return c;
+
+ case ',':
+ if (pstate->comma_terminates && paren_depth == 0)
+ return 0;
+ pstate->lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (pstate->lexptr[1] >= '0' && pstate->lexptr[1] <= '9')
+ break; /* Falls into number code. */
+ else
+ {
+ pstate->lexptr++;
+ return DOT;
+ }
+
+/* These are character tokens that appear as-is in the YACC grammar */
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '^':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '=':
+ case '{':
+ case '}':
+ case '#':
+ case '@':
+ case '~':
+ case '&':
+ pstate->lexptr++;
+ return c;
+
+ case '\'' :
+ case '"':
+ quote = c;
+ for (namelen = 1; (c = tokstart[namelen]) != quote && c != '\0'; namelen++)
+ if (c == '\\')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ {
+ c = tokstart[++namelen];
+ if (c >= '0' && c <= '9')
+ c = tokstart[++namelen];
+ }
+ }
+ if(c != quote)
+ error (_("Unterminated string or character constant."));
+ m2_yylval.sval.ptr = tokstart + 1;
+ m2_yylval.sval.length = namelen - 1;
+ pstate->lexptr += namelen + 1;
+
+ if(namelen == 2) /* Single character */
+ {
+ m2_yylval.ulval = tokstart[1];
+ return CHAR;
+ }
+ else
+ return STRING;
+ }
+
+ /* Is it a number? */
+ /* Note: We have already dealt with the case of the token '.'.
+ See case '.' above. */
+ if ((c >= '0' && c <= '9'))
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0;
+ const char *p = tokstart;
+ int toktype;
+
+ for (++p ;; ++p)
+ {
+ if (!got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ else if ((*p < '0' || *p > '9') &&
+ (*p < 'A' || *p > 'F') &&
+ (*p != 'H')) /* Modula-2 hexadecimal number */
+ break;
+ }
+ toktype = parse_number (p - tokstart);
+ if (toktype == ERROR)
+ error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+ tokstart);
+ pstate->lexptr = p;
+ return toktype;
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error (_("Invalid character '%c' in expression."), c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ c = tokstart[++namelen])
+ ;
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
+ {
+ return 0;
+ }
+
+ pstate->lexptr += namelen;
+
+ /* Lookup special keywords */
+ for(i = 0 ; i < (int) (sizeof(keytab) / sizeof(keytab[0])) ; i++)
+ if (namelen == strlen (keytab[i].keyw)
+ && strncmp (tokstart, keytab[i].keyw, namelen) == 0)
+ return keytab[i].token;
+
+ m2_yylval.sval.ptr = tokstart;
+ m2_yylval.sval.length = namelen;
+
+ if (*tokstart == '$')
+ return DOLLAR_VARIABLE;
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+ std::string tmp = copy_name (m2_yylval.sval);
+ struct symbol *sym;
+
+ if (lookup_symtab (current_program_space, tmp.c_str ()) != nullptr)
+ return BLOCKNAME;
+
+ sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
+ SEARCH_VFT, 0).symbol;
+ if (sym && sym->loc_class () == LOC_BLOCK)
+ return BLOCKNAME;
+ if (lookup_typename (pstate->language (),
+ tmp.c_str (), pstate->expression_context_block, 1))
+ return TYPENAME;
+
+ if(sym)
+ {
+ switch(sym->loc_class ())
+ {
+ case LOC_STATIC:
+ case LOC_REGISTER:
+ case LOC_ARG:
+ case LOC_REF_ARG:
+ case LOC_REGPARM_ADDR:
+ case LOC_LOCAL:
+ case LOC_CONST:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ case LOC_COMPUTED:
+ return NAME;
+
+ case LOC_TYPEDEF:
+ return TYPENAME;
+
+ case LOC_BLOCK:
+ return BLOCKNAME;
+
+ case LOC_UNDEF:
+ error (_("internal: Undefined class in m2lex()"));
+
+ case LOC_LABEL:
+ case LOC_UNRESOLVED:
+ error (_("internal: Unforeseen case in m2lex()"));
+
+ default:
+ error (_("unhandled token in m2lex()"));
+ break;
+ }
+ }
+ else
+ {
+ /* Built-in BOOLEAN type. This is sort of a hack. */
+ if (startswith (tokstart, "TRUE"))
+ {
+ m2_yylval.ulval = 1;
+ return M2_TRUE;
+ }
+ else if (startswith (tokstart, "FALSE"))
+ {
+ m2_yylval.ulval = 0;
+ return M2_FALSE;
+ }
+ }
+
+ /* Must be another type of name... */
+ return NAME;
+ }
+}
+
+/* See m2-exp-parser.h. */
+
+void
+m2_yyerror (const char *msg)
+{
+ pstate->parse_error (msg);
+}
+
+} /* namespace m2_exp_parser */
+
+/* See m2-exp-parser.h. */
+
+int
+m2_parse (struct parser_state *par_state)
+{
+ using namespace m2_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate);
+ gdb_assert (par_state != NULL);
+ pstate = par_state;
+ paren_depth = 0;
+
+ int result = m2_yyparse ();
+ if (!result)
+ pstate->set_operation (pstate->pop ());
+ return result;
+}
diff --git a/gdb/m2-exp-parser.h b/gdb/m2-exp-parser.h
new file mode 100644
index 000000000000..bea7155188fb
--- /dev/null
+++ b/gdb/m2-exp-parser.h
@@ -0,0 +1,67 @@
+/* YACC parser support code for Modula-2 expressions, 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_M2_EXP_PARSER_H
+#define GDB_M2_EXP_PARSER_H
+
+#include "parser-defs.h"
+#include "m2-lang.h"
+
+union m2_exp_parser_YYSTYPE;
+
+namespace m2_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+ expression. */
+
+extern parser_state *pstate;
+
+/* The sign of the number being parsed. */
+
+extern int number_sign;
+
+/* Return the Modula-2 type table for the architecture associated to PS. */
+
+static inline const struct builtin_m2_type *
+parse_m2_type (parser_state *ps)
+{
+ return builtin_m2_type (ps->gdbarch ());
+}
+
+/* Read one token, getting characters through lexptr. */
+
+/* This is where we will check to make sure that the language and the
+ operators used are compatible */
+
+int m2_yylex ();
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void m2_yyerror (const char *msg);
+
+} /* namespace m2_exp_parser */
+
+/* Parse a Modula-2 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 m2_parse (struct parser_state *par_state);
+
+#endif /* GDB_M2_EXP_PARSER_H */
diff --git a/gdb/m2-exp-parser.y b/gdb/m2-exp-parser.y
index 0f1c62c7ae52..a10087fa03ab 100644
--- a/gdb/m2-exp-parser.y
+++ b/gdb/m2-exp-parser.y
@@ -42,33 +42,11 @@
#include "value.h"
#include "parser-defs.h"
#include "m2-lang.h"
+#include "m2-exp-parser.h"
#include "block.h"
#include "m2-exp.h"
-/* The state of the parser, used internally when we are parsing the
- expression. */
-
-static struct parser_state *pstate = NULL;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
-
-static int parse_number (int);
-
-/* The sign of the number being parsed. */
-static int number_sign = 1;
-
-/* Return the Modula-2 type table for the architecture associated to PS. */
-
-static inline const struct builtin_m2_type *
-parse_m2_type (parser_state *ps)
-{
- return builtin_m2_type (ps->gdbarch ());
-}
-
+using namespace m2_exp_parser;
using namespace expr;
%}
@@ -569,442 +547,3 @@ 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 (int olen)
-{
- const char *p = pstate->lexptr;
- ULONGEST n = 0;
- ULONGEST prevn = 0;
- int c,i,ischar=0;
- int base = input_radix;
- int len = olen;
-
- if(p[len-1] == 'H')
- {
- base = 16;
- len--;
- }
- else if(p[len-1] == 'C' || p[len-1] == 'B')
- {
- base = 8;
- ischar = p[len-1] == 'C';
- len--;
- }
-
- /* Scan the number */
- for (c = 0; c < len; c++)
- {
- if (p[c] == '.' && base == 10)
- {
- /* It's a float since it contains a point. */
- if (!parse_float (p, len,
- parse_m2_type (pstate)->builtin_real,
- yylval.val))
- return ERROR;
-
- pstate->lexptr += len;
- return FLOAT;
- }
- if (p[c] == '.' && base != 10)
- error (_("Floating point numbers must be base 10."));
- if (base == 10 && (p[c] < '0' || p[c] > '9'))
- error (_("Invalid digit \'%c\' in number."),p[c]);
- }
-
- while (len-- > 0)
- {
- c = *p++;
- n *= base;
- if( base == 8 && (c == '8' || c == '9'))
- error (_("Invalid digit \'%c\' in octal number."),c);
- if (c >= '0' && c <= '9')
- i = c - '0';
- else
- {
- if (base == 16 && c >= 'A' && c <= 'F')
- i = c - 'A' + 10;
- else
- return ERROR;
- }
- n+=i;
- if(i >= base)
- return ERROR;
- if (n == 0 && prevn == 0)
- ;
- else if (RANGE_CHECK && prevn >= n)
- range_error (_("Overflow on numeric constant."));
-
- prevn=n;
- }
-
- pstate->lexptr = p;
- if(*p == 'B' || *p == 'C' || *p == 'H')
- pstate->lexptr++; /* Advance past B,C or H */
-
- if (ischar)
- {
- yylval.ulval = n;
- return CHAR;
- }
-
- int int_bits = gdbarch_int_bit (pstate->gdbarch ());
- bool have_signed = number_sign == -1;
- bool have_unsigned = number_sign == 1;
- if (have_signed && fits_in_type (number_sign, n, int_bits, true))
- {
- yylval.lval = n;
- return INT;
- }
- else if (have_unsigned && fits_in_type (number_sign, n, int_bits, false))
- {
- yylval.ulval = n;
- return UINT;
- }
- else
- error (_("Overflow on numeric constant."));
-}
-
-
-/* Some tokens */
-
-static struct
-{
- char name[2];
- int token;
-} tokentab2[] =
-{
- { {'<', '>'}, NOTEQUAL },
- { {':', '='}, ASSIGN },
- { {'<', '='}, LEQ },
- { {'>', '='}, GEQ },
- { {':', ':'}, COLONCOLON },
-
-};
-
-/* Some specific keywords */
-
-struct keyword {
- char keyw[10];
- int token;
-};
-
-static struct keyword keytab[] =
-{
- {"OR" , OROR },
- {"IN", IN },/* Note space after IN */
- {"AND", LOGICAL_AND},
- {"ABS", ABS },
- {"ADR", ADR },
- {"CHR", CHR },
- {"DEC", DEC },
- {"NOT", NOT },
- {"DIV", DIV },
- {"INC", INC },
- {"MAX", MAX_FUNC },
- {"MIN", MIN_FUNC },
- {"MOD", MOD },
- {"ODD", ODD },
- {"CAP", CAP },
- {"ORD", ORD },
- {"VAL", VAL },
- {"EXCL", EXCL },
- {"HIGH", HIGH },
- {"INCL", INCL },
- {"SIZE", SIZE },
- {"FLOAT", FLOAT_FUNC },
- {"TRUNC", TRUNC },
- {"TSIZE", SIZE },
-};
-
-
-/* Depth of parentheses. */
-static int paren_depth;
-
-/* Read one token, getting characters through lexptr. */
-
-/* This is where we will check to make sure that the language and the
- operators used are compatible */
-
-static int
-yylex (void)
-{
- int c;
- int namelen;
- int i;
- const char *tokstart;
- char quote;
-
- retry:
-
- pstate->prev_lexptr = pstate->lexptr;
-
- tokstart = pstate->lexptr;
-
-
- /* See if it is a special token of length 2 */
- for( i = 0 ; i < (int) (sizeof tokentab2 / sizeof tokentab2[0]) ; i++)
- if (strncmp (tokentab2[i].name, tokstart, 2) == 0)
- {
- pstate->lexptr += 2;
- return tokentab2[i].token;
- }
-
- switch (c = *tokstart)
- {
- case 0:
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- pstate->lexptr++;
- goto retry;
-
- case '(':
- paren_depth++;
- pstate->lexptr++;
- return c;
-
- case ')':
- if (paren_depth == 0)
- return 0;
- paren_depth--;
- pstate->lexptr++;
- return c;
-
- case ',':
- if (pstate->comma_terminates && paren_depth == 0)
- return 0;
- pstate->lexptr++;
- return c;
-
- case '.':
- /* Might be a floating point number. */
- if (pstate->lexptr[1] >= '0' && pstate->lexptr[1] <= '9')
- break; /* Falls into number code. */
- else
- {
- pstate->lexptr++;
- return DOT;
- }
-
-/* These are character tokens that appear as-is in the YACC grammar */
- case '+':
- case '-':
- case '*':
- case '/':
- case '^':
- case '<':
- case '>':
- case '[':
- case ']':
- case '=':
- case '{':
- case '}':
- case '#':
- case '@':
- case '~':
- case '&':
- pstate->lexptr++;
- return c;
-
- case '\'' :
- case '"':
- quote = c;
- for (namelen = 1; (c = tokstart[namelen]) != quote && c != '\0'; namelen++)
- if (c == '\\')
- {
- c = tokstart[++namelen];
- if (c >= '0' && c <= '9')
- {
- c = tokstart[++namelen];
- if (c >= '0' && c <= '9')
- c = tokstart[++namelen];
- }
- }
- if(c != quote)
- error (_("Unterminated string or character constant."));
- yylval.sval.ptr = tokstart + 1;
- yylval.sval.length = namelen - 1;
- pstate->lexptr += namelen + 1;
-
- if(namelen == 2) /* Single character */
- {
- yylval.ulval = tokstart[1];
- return CHAR;
- }
- else
- return STRING;
- }
-
- /* Is it a number? */
- /* Note: We have already dealt with the case of the token '.'.
- See case '.' above. */
- if ((c >= '0' && c <= '9'))
- {
- /* It's a number. */
- int got_dot = 0, got_e = 0;
- const char *p = tokstart;
- int toktype;
-
- for (++p ;; ++p)
- {
- if (!got_e && (*p == 'e' || *p == 'E'))
- got_dot = got_e = 1;
- else if (!got_dot && *p == '.')
- got_dot = 1;
- else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
- && (*p == '-' || *p == '+'))
- /* This is the sign of the exponent, not the end of the
- number. */
- continue;
- else if ((*p < '0' || *p > '9') &&
- (*p < 'A' || *p > 'F') &&
- (*p != 'H')) /* Modula-2 hexadecimal number */
- break;
- }
- toktype = parse_number (p - tokstart);
- if (toktype == ERROR)
- error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
- tokstart);
- pstate->lexptr = p;
- return toktype;
- }
-
- if (!(c == '_' || c == '$'
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
- /* We must have come across a bad character (e.g. ';'). */
- error (_("Invalid character '%c' in expression."), c);
-
- /* It's a name. See how long it is. */
- namelen = 0;
- for (c = tokstart[namelen];
- (c == '_' || c == '$' || (c >= '0' && c <= '9')
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
- c = tokstart[++namelen])
- ;
-
- /* The token "if" terminates the expression and is NOT
- removed from the input stream. */
- if (namelen == 2 && tokstart[0] == 'i' && tokstart[1] == 'f')
- {
- return 0;
- }
-
- pstate->lexptr += namelen;
-
- /* Lookup special keywords */
- for(i = 0 ; i < (int) (sizeof(keytab) / sizeof(keytab[0])) ; i++)
- if (namelen == strlen (keytab[i].keyw)
- && strncmp (tokstart, keytab[i].keyw, namelen) == 0)
- return keytab[i].token;
-
- yylval.sval.ptr = tokstart;
- yylval.sval.length = namelen;
-
- if (*tokstart == '$')
- return DOLLAR_VARIABLE;
-
- /* Use token-type BLOCKNAME for symbols that happen to be defined as
- functions. If this is not so, then ...
- Use token-type TYPENAME for symbols that happen to be defined
- currently as names of types; NAME for other symbols.
- The caller is not constrained to care about the distinction. */
- {
- std::string tmp = copy_name (yylval.sval);
- struct symbol *sym;
-
- if (lookup_symtab (current_program_space, tmp.c_str ()) != nullptr)
- return BLOCKNAME;
-
- sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
- SEARCH_VFT, 0).symbol;
- if (sym && sym->loc_class () == LOC_BLOCK)
- return BLOCKNAME;
- if (lookup_typename (pstate->language (),
- tmp.c_str (), pstate->expression_context_block, 1))
- return TYPENAME;
-
- if(sym)
- {
- switch(sym->loc_class ())
- {
- case LOC_STATIC:
- case LOC_REGISTER:
- case LOC_ARG:
- case LOC_REF_ARG:
- case LOC_REGPARM_ADDR:
- case LOC_LOCAL:
- case LOC_CONST:
- case LOC_CONST_BYTES:
- case LOC_OPTIMIZED_OUT:
- case LOC_COMPUTED:
- return NAME;
-
- case LOC_TYPEDEF:
- return TYPENAME;
-
- case LOC_BLOCK:
- return BLOCKNAME;
-
- case LOC_UNDEF:
- error (_("internal: Undefined class in m2lex()"));
-
- case LOC_LABEL:
- case LOC_UNRESOLVED:
- error (_("internal: Unforeseen case in m2lex()"));
-
- default:
- error (_("unhandled token in m2lex()"));
- break;
- }
- }
- else
- {
- /* Built-in BOOLEAN type. This is sort of a hack. */
- if (startswith (tokstart, "TRUE"))
- {
- yylval.ulval = 1;
- return M2_TRUE;
- }
- else if (startswith (tokstart, "FALSE"))
- {
- yylval.ulval = 0;
- return M2_FALSE;
- }
- }
-
- /* Must be another type of name... */
- return NAME;
- }
-}
-
-int
-m2_language::parser (struct parser_state *par_state) const
-{
- /* Setting up the parser state. */
- scoped_restore pstate_restore = make_scoped_restore (&pstate);
- gdb_assert (par_state != NULL);
- pstate = par_state;
- paren_depth = 0;
-
- int result = yyparse ();
- if (!result)
- pstate->set_operation (pstate->pop ());
- return result;
-}
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 7bee1f36dc83..53bb8e25212e 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -25,6 +25,7 @@
#include "language.h"
#include "varobj.h"
#include "m2-lang.h"
+#include "m2-exp-parser.h"
#include "c-lang.h"
#include "valprint.h"
#include "gdbarch.h"
@@ -118,6 +119,14 @@ static m2_language m2_language_defn;
/* See language.h. */
+int
+m2_language::parser (struct parser_state *ps) const
+{
+ return m2_parse (ps);
+}
+
+/* See language.h. */
+
void
m2_language::language_arch_info (struct gdbarch *gdbarch,
struct language_arch_info *lai) const
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 18/19] gdb: move p-exp-parser.y's support code to p-exp-parser.c
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (16 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 17/19] gdb: move m2-exp-parser.y's support code to m2-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
2026-09-05 4:23 ` [PATCH v2 19/19] gdb: honor "set debug parser" in the Modula-2 and Pascal parsers simon.marchi
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
Similar to the previous commits, but for the Pascal expression parser.
Like the Fortran parser, the Pascal parser is entered through the
pascal_language::parser method rather than a free function, so add a free
function pascal_parse as the entry point (like the other parsers) and turn
pascal_language::parser into a thin wrapper around it, defined in p-lang.c.
Put the parser support code inside the p_exp_parser namespace.
The Pascal support code uses malloc, realloc and free directly. These used
to be rewritten to their x-variants by post-process-parser-output.sh when
the code was part of the generated parser. Replace them with their
x-variants in the moved code.
Change-Id: Ia9c7c36cde15f408ec89ecb14957f303410b70b2
---
gdb/Makefile.in | 2 +
gdb/p-exp-parser.c | 954 +++++++++++++++++++++++++++++++++++++++++++++
gdb/p-exp-parser.h | 84 ++++
gdb/p-exp-parser.y | 924 +------------------------------------------
gdb/p-lang.c | 9 +
5 files changed, 1051 insertions(+), 922 deletions(-)
create mode 100644 gdb/p-exp-parser.c
create mode 100644 gdb/p-exp-parser.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 1c4ba5a12d57..9f1e4a5198d0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1163,6 +1163,7 @@ COMMON_SFILES = \
opencl-lang.c \
osabi.c \
osdata.c \
+ p-exp-parser.c \
p-lang.c \
p-typeprint.c \
p-valprint.c \
@@ -1606,6 +1607,7 @@ HFILES_NO_SRCDIR = \
osdata.h \
pager.h \
parser-defs.h \
+ p-exp-parser.h \
p-lang.h \
ppc64-tdep.h \
ppc-fbsd-tdep.h \
diff --git a/gdb/p-exp-parser.c b/gdb/p-exp-parser.c
new file mode 100644
index 000000000000..108c839d3f51
--- /dev/null
+++ b/gdb/p-exp-parser.c
@@ -0,0 +1,954 @@
+/* YACC parser support code for Pascal expressions, for GDB.
+
+ Copyright (C) 2000-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 "p-exp-parser.h"
+#include "p-exp-parser-gen.h"
+#include "block.h"
+#include "expression.h"
+#include "language.h"
+#include "value.h"
+
+/* The entry point of the bison/yacc-generated parser, defined in
+ p-exp-parser-gen.c. Bison produces a declaration for pascal_yyparse in
+ p-exp-parser-gen.h, but byacc does not, hence this declaration. */
+
+int pascal_yyparse ();
+
+/* Likewise, byacc does not produce a declaration for pascal_yydebug. */
+
+extern int pascal_yydebug;
+
+namespace p_exp_parser
+{
+
+/* See p-exp-parser.h. */
+
+parser_state *pstate;
+
+/* Depth of parentheses. */
+
+static int paren_depth;
+
+/* See p-exp-parser.h. */
+
+struct type *current_type;
+
+/* See p-exp-parser.h. */
+
+int leftdiv_is_integer;
+
+/* See p-exp-parser.h. */
+
+int search_field;
+
+/* See p-exp-parser.h. */
+
+int
+parse_number (struct parser_state *par_state,
+ const char *p, int len, int parsed_float, p_exp_parser_YYSTYPE *putithere)
+{
+ ULONGEST n = 0;
+ ULONGEST prevn = 0;
+
+ int i = 0;
+ int c;
+ int base = input_radix;
+ int unsigned_p = 0;
+
+ /* Number of "L" suffixes encountered. */
+ int long_p = 0;
+
+ /* We have found a "L" or "U" suffix. */
+ int found_suffix = 0;
+
+ if (parsed_float)
+ {
+ /* Handle suffixes: 'f' for float, 'l' for long double.
+ FIXME: This appears to be an extension -- do we want this? */
+ if (len >= 1 && c_tolower (p[len - 1]) == 'f')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_float;
+ len--;
+ }
+ else if (len >= 1 && c_tolower (p[len - 1]) == 'l')
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_long_double;
+ len--;
+ }
+ /* Default type for floating-point literals is double. */
+ else
+ {
+ putithere->typed_val_float.type
+ = parse_type (par_state)->builtin_double;
+ }
+
+ if (!parse_float (p, len,
+ putithere->typed_val_float.type,
+ putithere->typed_val_float.val))
+ return ERROR;
+ return FLOAT;
+ }
+
+ /* Handle base-switching prefixes 0x, 0t, 0d, 0. */
+ if (p[0] == '0' && len > 1)
+ switch (p[1])
+ {
+ case 'x':
+ case 'X':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 16;
+ len -= 2;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ case 'd':
+ case 'D':
+ if (len >= 3)
+ {
+ p += 2;
+ base = 10;
+ len -= 2;
+ }
+ break;
+
+ default:
+ base = 8;
+ break;
+ }
+
+ while (len-- > 0)
+ {
+ c = *p++;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ if (c != 'l' && c != 'u')
+ n *= base;
+ if (c >= '0' && c <= '9')
+ {
+ if (found_suffix)
+ return ERROR;
+ n += i = c - '0';
+ }
+ else
+ {
+ if (base > 10 && c >= 'a' && c <= 'f')
+ {
+ if (found_suffix)
+ return ERROR;
+ n += i = c - 'a' + 10;
+ }
+ else if (c == 'l')
+ {
+ ++long_p;
+ found_suffix = 1;
+ }
+ else if (c == 'u')
+ {
+ unsigned_p = 1;
+ found_suffix = 1;
+ }
+ else
+ return ERROR; /* Char not a digit */
+ }
+ if (i >= base)
+ return ERROR; /* Invalid digit in this base. */
+
+ if (c != 'l' && c != 'u')
+ {
+ /* Test for overflow. */
+ if (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 = !unsigned_p;
+ bool have_int = long_p == 0;
+ bool have_long = long_p <= 1;
+ if (have_int && have_signed && fits_in_type (1, n, int_bits, true))
+ putithere->typed_val_int.type = parse_type (par_state)->builtin_int;
+ else if (have_int && fits_in_type (1, n, int_bits, false))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_unsigned_int;
+ else if (have_long && have_signed && fits_in_type (1, n, long_bits, true))
+ putithere->typed_val_int.type = parse_type (par_state)->builtin_long;
+ else if (have_long && fits_in_type (1, n, long_bits, false))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_unsigned_long;
+ else if (have_signed && fits_in_type (1, n, long_long_bits, true))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_long_long;
+ else if (fits_in_type (1, n, long_long_bits, false))
+ putithere->typed_val_int.type
+ = parse_type (par_state)->builtin_unsigned_long_long;
+ else
+ error (_("Numeric constant too large."));
+ putithere->typed_val_int.val = n;
+
+ return INT;
+}
+
+
+struct type_push
+{
+ struct type *stored;
+ struct type_push *next;
+};
+
+static struct type_push *tp_top = NULL;
+
+/* See p-exp-parser.h. */
+
+void
+push_current_type (void)
+{
+ struct type_push *tpnew;
+ tpnew = (struct type_push *) xmalloc (sizeof (struct type_push));
+ tpnew->next = tp_top;
+ tpnew->stored = current_type;
+ current_type = NULL;
+ tp_top = tpnew;
+}
+
+/* See p-exp-parser.h. */
+
+void
+pop_current_type (void)
+{
+ struct type_push *tp = tp_top;
+ if (tp)
+ {
+ current_type = tp->stored;
+ tp_top = tp->next;
+ xfree (tp);
+ }
+}
+
+struct p_token
+{
+ const char *oper;
+ int token;
+ enum exp_opcode opcode;
+};
+
+static const struct p_token tokentab3[] =
+ {
+ {"shr", RSH, OP_NULL},
+ {"shl", LSH, OP_NULL},
+ {"and", ANDAND, OP_NULL},
+ {"div", DIV, OP_NULL},
+ {"not", NOT, OP_NULL},
+ {"mod", MOD, OP_NULL},
+ {"inc", INCREMENT, OP_NULL},
+ {"dec", DECREMENT, OP_NULL},
+ {"xor", XOR, OP_NULL}
+ };
+
+static const struct p_token tokentab2[] =
+ {
+ {"or", OR, OP_NULL},
+ {"<>", NOTEQUAL, OP_NULL},
+ {"<=", LEQ, OP_NULL},
+ {">=", GEQ, OP_NULL},
+ {":=", ASSIGN, OP_NULL},
+ {"::", COLONCOLON, OP_NULL} };
+
+/* Allocate uppercased var: */
+/* make an uppercased copy of tokstart. */
+static char *
+uptok (const char *tokstart, int namelen)
+{
+ int i;
+ char *uptokstart = (char *)xmalloc(namelen+1);
+ for (i = 0;i <= namelen;i++)
+ {
+ if ((tokstart[i]>='a' && tokstart[i]<='z'))
+ uptokstart[i] = tokstart[i]-('a'-'A');
+ else
+ uptokstart[i] = tokstart[i];
+ }
+ uptokstart[namelen]='\0';
+ return uptokstart;
+}
+
+/* Skip over a Pascal string. STR must point to the opening single quote
+ character. This function returns a pointer to the character after the
+ closing single quote character.
+
+ This function does not support embedded, escaped single quotes, which
+ is done by placing two consecutive single quotes into a string.
+ Support for this would be easy to add, but this function is only used
+ from the Python expression parser, and if we did skip over escaped
+ quotes then the rest of the expression parser wouldn't handle them
+ correctly. */
+static const char *
+pascal_skip_string (const char *str)
+{
+ gdb_assert (*str == '\'');
+
+ do
+ ++str;
+ while (*str != '\0' && *str != '\'');
+
+ return str;
+}
+
+/* See p-exp-parser.h. */
+
+int
+pascal_yylex (void)
+{
+ int c;
+ int namelen;
+ const char *tokstart;
+ char *uptokstart;
+ const char *tokptr;
+ int explen, tempbufindex;
+ static char *tempbuf;
+ static int tempbufsize;
+
+ retry:
+
+ pstate->prev_lexptr = pstate->lexptr;
+
+ tokstart = pstate->lexptr;
+ explen = strlen (pstate->lexptr);
+
+ /* See if it is a special token of length 3. */
+ if (explen > 2)
+ for (const auto &token : tokentab3)
+ if (strncasecmp (tokstart, token.oper, 3) == 0
+ && (!c_isalpha (token.oper[0]) || explen == 3
+ || (!c_isalpha (tokstart[3])
+ && !c_isdigit (tokstart[3]) && tokstart[3] != '_')))
+ {
+ pstate->lexptr += 3;
+ pascal_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ /* See if it is a special token of length 2. */
+ if (explen > 1)
+ for (const auto &token : tokentab2)
+ if (strncasecmp (tokstart, token.oper, 2) == 0
+ && (!c_isalpha (token.oper[0]) || explen == 2
+ || (!c_isalpha (tokstart[2])
+ && !c_isdigit (tokstart[2]) && tokstart[2] != '_')))
+ {
+ pstate->lexptr += 2;
+ pascal_yylval.opcode = token.opcode;
+ return token.token;
+ }
+
+ switch (c = *tokstart)
+ {
+ case 0:
+ if (search_field && pstate->parse_completion)
+ return COMPLETE;
+ else
+ return 0;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ pstate->lexptr++;
+ goto retry;
+
+ case '\'':
+ /* We either have a character constant ('0' or '\177' for example)
+ or we have a quoted symbol reference ('foo(int,int)' in object pascal
+ for example). */
+ pstate->lexptr++;
+ c = *pstate->lexptr++;
+ if (c == '\\')
+ c = parse_escape (pstate->gdbarch (), &pstate->lexptr);
+ else if (c == '\'')
+ error (_("Empty character constant."));
+
+ pascal_yylval.typed_val_int.val = c;
+ pascal_yylval.typed_val_int.type = parse_type (pstate)->builtin_char;
+
+ c = *pstate->lexptr++;
+ if (c != '\'')
+ {
+ namelen = pascal_skip_string (tokstart) - tokstart;
+ if (namelen > 2)
+ {
+ pstate->lexptr = tokstart + namelen;
+ if (pstate->lexptr[-1] != '\'')
+ error (_("Unmatched single quote."));
+ namelen -= 2;
+ tokstart++;
+ uptokstart = uptok(tokstart,namelen);
+ goto tryname;
+ }
+ error (_("Invalid character constant."));
+ }
+ return INT;
+
+ case '(':
+ paren_depth++;
+ pstate->lexptr++;
+ return c;
+
+ case ')':
+ if (paren_depth == 0)
+ return 0;
+ paren_depth--;
+ pstate->lexptr++;
+ return c;
+
+ case ',':
+ if (pstate->comma_terminates && paren_depth == 0)
+ return 0;
+ pstate->lexptr++;
+ return c;
+
+ case '.':
+ /* Might be a floating point number. */
+ if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
+ {
+ goto symbol; /* Nope, must be a symbol. */
+ }
+
+ [[fallthrough]];
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* It's a number. */
+ int got_dot = 0, got_e = 0, toktype;
+ const char *p = tokstart;
+ int hex = input_radix > 10;
+
+ if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
+ {
+ p += 2;
+ hex = 1;
+ }
+ else if (c == '0' && (p[1]=='t' || p[1]=='T'
+ || p[1]=='d' || p[1]=='D'))
+ {
+ p += 2;
+ hex = 0;
+ }
+
+ for (;; ++p)
+ {
+ /* This test includes !hex because 'e' is a valid hex digit
+ and thus does not indicate a floating point number when
+ the radix is hex. */
+ if (!hex && !got_e && (*p == 'e' || *p == 'E'))
+ got_dot = got_e = 1;
+ /* This test does not include !hex, because a '.' always indicates
+ a decimal floating point number regardless of the radix. */
+ else if (!got_dot && *p == '.')
+ got_dot = 1;
+ else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
+ && (*p == '-' || *p == '+'))
+ /* This is the sign of the exponent, not the end of the
+ number. */
+ continue;
+ /* We will take any letters or digits. parse_number will
+ complain if past the radix, or if L or U are not final. */
+ else if ((*p < '0' || *p > '9')
+ && ((*p < 'a' || *p > 'z')
+ && (*p < 'A' || *p > 'Z')))
+ break;
+ }
+ toktype = parse_number (pstate, tokstart,
+ p - tokstart, got_dot | got_e, &pascal_yylval);
+ if (toktype == ERROR)
+ error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
+ tokstart);
+ pstate->lexptr = p;
+ return toktype;
+ }
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case '|':
+ case '&':
+ case '^':
+ case '~':
+ case '!':
+ case '@':
+ case '<':
+ case '>':
+ case '[':
+ case ']':
+ case '?':
+ case ':':
+ case '=':
+ case '{':
+ case '}':
+ symbol:
+ pstate->lexptr++;
+ return c;
+
+ case '"':
+
+ /* Build the gdb internal form of the input string in tempbuf,
+ translating any standard C escape forms seen. 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. */
+
+ tokptr = ++tokstart;
+ tempbufindex = 0;
+
+ do {
+ /* Grow the static temp buffer if necessary, including allocating
+ the first one on demand. */
+ if (tempbufindex + 1 >= tempbufsize)
+ {
+ tempbuf = (char *) xrealloc (tempbuf, tempbufsize += 64);
+ }
+
+ switch (*tokptr)
+ {
+ case '\0':
+ case '"':
+ /* Do nothing, loop will terminate. */
+ break;
+ case '\\':
+ ++tokptr;
+ c = parse_escape (pstate->gdbarch (), &tokptr);
+ if (c == -1)
+ {
+ continue;
+ }
+ tempbuf[tempbufindex++] = c;
+ break;
+ default:
+ tempbuf[tempbufindex++] = *tokptr++;
+ break;
+ }
+ } while ((*tokptr != '"') && (*tokptr != '\0'));
+ if (*tokptr++ != '"')
+ {
+ error (_("Unterminated string in expression."));
+ }
+ tempbuf[tempbufindex] = '\0'; /* See note above. */
+ pascal_yylval.sval.ptr = tempbuf;
+ pascal_yylval.sval.length = tempbufindex;
+ pstate->lexptr = tokptr;
+ return (STRING);
+ }
+
+ if (!(c == '_' || c == '$'
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
+ /* We must have come across a bad character (e.g. ';'). */
+ error (_("Invalid character '%c' in expression."), c);
+
+ /* It's a name. See how long it is. */
+ namelen = 0;
+ for (c = tokstart[namelen];
+ (c == '_' || c == '$' || (c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '<');)
+ {
+ /* Template parameter lists are part of the name.
+ FIXME: This mishandles `print $a<4&&$a>3'. */
+ if (c == '<')
+ {
+ int i = namelen;
+ int nesting_level = 1;
+ while (tokstart[++i])
+ {
+ if (tokstart[i] == '<')
+ nesting_level++;
+ else if (tokstart[i] == '>')
+ {
+ if (--nesting_level == 0)
+ break;
+ }
+ }
+ if (tokstart[i] == '>')
+ namelen = i;
+ else
+ break;
+ }
+
+ /* do NOT uppercase internals because of registers !!! */
+ c = tokstart[++namelen];
+ }
+
+ uptokstart = uptok(tokstart,namelen);
+
+ /* The token "if" terminates the expression and is NOT
+ removed from the input stream. */
+ if (namelen == 2 && uptokstart[0] == 'I' && uptokstart[1] == 'F')
+ {
+ xfree (uptokstart);
+ return 0;
+ }
+
+ pstate->lexptr += namelen;
+
+ tryname:
+
+ /* Catch specific keywords. Should be done with a data structure. */
+ switch (namelen)
+ {
+ case 6:
+ if (streq (uptokstart, "OBJECT"))
+ {
+ xfree (uptokstart);
+ return CLASS;
+ }
+ if (streq (uptokstart, "RECORD"))
+ {
+ xfree (uptokstart);
+ return STRUCT;
+ }
+ if (streq (uptokstart, "SIZEOF"))
+ {
+ xfree (uptokstart);
+ return SIZEOF;
+ }
+ break;
+ case 5:
+ if (streq (uptokstart, "CLASS"))
+ {
+ xfree (uptokstart);
+ return CLASS;
+ }
+ if (streq (uptokstart, "FALSE"))
+ {
+ pascal_yylval.lval = 0;
+ xfree (uptokstart);
+ return FALSEKEYWORD;
+ }
+ break;
+ case 4:
+ if (streq (uptokstart, "TRUE"))
+ {
+ pascal_yylval.lval = 1;
+ xfree (uptokstart);
+ return TRUEKEYWORD;
+ }
+ if (streq (uptokstart, "SELF"))
+ {
+ /* Here we search for 'this' like
+ inserted in FPC stabs debug info. */
+ static const char this_name[] = "this";
+
+ if (lookup_symbol (this_name, pstate->expression_context_block,
+ SEARCH_VFT, NULL).symbol)
+ {
+ xfree (uptokstart);
+ return THIS;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ pascal_yylval.sval.ptr = tokstart;
+ pascal_yylval.sval.length = namelen;
+
+ if (*tokstart == '$')
+ {
+ xfree (uptokstart);
+ return DOLLAR_VARIABLE;
+ }
+
+ /* Use token-type BLOCKNAME for symbols that happen to be defined as
+ functions or symtabs. If this is not so, then ...
+ Use token-type TYPENAME for symbols that happen to be defined
+ currently as names of types; NAME for other symbols.
+ The caller is not constrained to care about the distinction. */
+ {
+ std::string tmp = copy_name (pascal_yylval.sval);
+ struct symbol *sym;
+ struct field_of_this_result is_a_field_of_this;
+ int is_a_field = 0;
+ int hextype;
+
+ is_a_field_of_this.type = NULL;
+ if (search_field && current_type)
+ is_a_field = (lookup_struct_elt_type (current_type,
+ tmp.c_str (), 1) != NULL);
+ if (is_a_field)
+ sym = NULL;
+ else
+ sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
+ SEARCH_VFT, &is_a_field_of_this).symbol;
+ /* second chance uppercased (as Free Pascal does). */
+ if (!sym && is_a_field_of_this.type == NULL && !is_a_field)
+ {
+ for (int i = 0; i <= namelen; i++)
+ {
+ if ((tmp[i] >= 'a' && tmp[i] <= 'z'))
+ tmp[i] -= ('a'-'A');
+ }
+ if (search_field && current_type)
+ is_a_field = (lookup_struct_elt_type (current_type,
+ tmp.c_str (), 1) != NULL);
+ if (is_a_field)
+ sym = NULL;
+ else
+ sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
+ SEARCH_VFT, &is_a_field_of_this).symbol;
+ }
+ /* Third chance Capitalized (as GPC does). */
+ if (!sym && is_a_field_of_this.type == NULL && !is_a_field)
+ {
+ for (int i = 0; i <= namelen; i++)
+ {
+ if (i == 0)
+ {
+ if ((tmp[i] >= 'a' && tmp[i] <= 'z'))
+ tmp[i] -= ('a'-'A');
+ }
+ else
+ if ((tmp[i] >= 'A' && tmp[i] <= 'Z'))
+ tmp[i] -= ('A'-'a');
+ }
+ if (search_field && current_type)
+ is_a_field = (lookup_struct_elt_type (current_type,
+ tmp.c_str (), 1) != NULL);
+ if (is_a_field)
+ sym = NULL;
+ else
+ sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
+ SEARCH_VFT, &is_a_field_of_this).symbol;
+ }
+
+ if (is_a_field || (is_a_field_of_this.type != NULL))
+ {
+ tempbuf = (char *) xrealloc (tempbuf, namelen + 1);
+ strncpy (tempbuf, tmp.c_str (), namelen);
+ tempbuf [namelen] = 0;
+ pascal_yylval.sval.ptr = tempbuf;
+ pascal_yylval.sval.length = namelen;
+ pascal_yylval.ssym.sym.symbol = NULL;
+ pascal_yylval.ssym.sym.block = NULL;
+ xfree (uptokstart);
+ pascal_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ if (is_a_field)
+ return FIELDNAME;
+ else
+ return NAME;
+ }
+ /* Call lookup_symtab, not lookup_partial_symtab, in case there are
+ no psymtabs (coff, xcoff, or some future change to blow away the
+ psymtabs once once symbols are read). */
+ if ((sym && sym->loc_class () == LOC_BLOCK)
+ || lookup_symtab (current_program_space, tmp.c_str ()))
+ {
+ pascal_yylval.ssym.sym.symbol = sym;
+ pascal_yylval.ssym.sym.block = NULL;
+ pascal_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ xfree (uptokstart);
+ return BLOCKNAME;
+ }
+ if (sym && sym->loc_class () == LOC_TYPEDEF)
+ {
+#if 1
+ /* Despite the following flaw, we need to keep this code enabled.
+ Because we can get called from check_stub_method, if we don't
+ handle nested types then it screws many operations in any
+ program which uses nested types. */
+ /* In "A::x", if x is a member function of A and there happens
+ to be a type (nested or not, since the stabs don't make that
+ distinction) named x, then this code incorrectly thinks we
+ are dealing with nested types rather than a member function. */
+
+ const char *p;
+ const char *namestart;
+ struct symbol *best_sym;
+
+ /* Look ahead to detect nested types. This probably should be
+ done in the grammar, but trying seemed to introduce a lot
+ of shift/reduce and reduce/reduce conflicts. It's possible
+ that it could be done, though. Or perhaps a non-grammar, but
+ less ad hoc, approach would work well. */
+
+ /* Since we do not currently have any way of distinguishing
+ a nested type from a non-nested one (the stabs don't tell
+ us whether a type is nested), we just ignore the
+ containing type. */
+
+ p = pstate->lexptr;
+ best_sym = sym;
+ while (1)
+ {
+ /* Skip whitespace. */
+ p = skip_spaces (p);
+ if (*p == ':' && p[1] == ':')
+ {
+ /* Skip the `::'. */
+ p += 2;
+ /* Skip whitespace. */
+ p = skip_spaces (p);
+ namestart = p;
+ while (*p == '_' || *p == '$' || (*p >= '0' && *p <= '9')
+ || (*p >= 'a' && *p <= 'z')
+ || (*p >= 'A' && *p <= 'Z'))
+ ++p;
+ if (p != namestart)
+ {
+ struct symbol *cur_sym;
+ /* As big as the whole rest of the expression, which is
+ at least big enough. */
+ char *ncopy
+ = (char *) alloca (tmp.size () + strlen (namestart)
+ + 3);
+ char *tmp1;
+
+ tmp1 = ncopy;
+ memcpy (tmp1, tmp.c_str (), tmp.size ());
+ tmp1 += tmp.size ();
+ memcpy (tmp1, "::", 2);
+ tmp1 += 2;
+ memcpy (tmp1, namestart, p - namestart);
+ tmp1[p - namestart] = '\0';
+ cur_sym
+ = lookup_symbol (ncopy,
+ pstate->expression_context_block,
+ SEARCH_VFT, NULL).symbol;
+ if (cur_sym)
+ {
+ if (cur_sym->loc_class () == LOC_TYPEDEF)
+ {
+ best_sym = cur_sym;
+ pstate->lexptr = p;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ break;
+ }
+
+ pascal_yylval.tsym.type = best_sym->type ();
+#else /* not 0 */
+ pascal_yylval.tsym.type = sym->type ();
+#endif /* not 0 */
+ xfree (uptokstart);
+ return TYPENAME;
+ }
+ pascal_yylval.tsym.type
+ = language_lookup_primitive_type (pstate->language (),
+ pstate->gdbarch (), tmp.c_str ());
+ if (pascal_yylval.tsym.type != NULL)
+ {
+ xfree (uptokstart);
+ return TYPENAME;
+ }
+
+ /* Input names that aren't symbols but ARE valid hex numbers,
+ when the input radix permits them, can be names or numbers
+ depending on the parse. Note we support radixes > 16 here. */
+ if (!sym
+ && ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
+ || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
+ {
+ p_exp_parser_YYSTYPE newlval; /* Its value is ignored. */
+ hextype = parse_number (pstate, tokstart, namelen, 0, &newlval);
+ if (hextype == INT)
+ {
+ pascal_yylval.ssym.sym.symbol = sym;
+ pascal_yylval.ssym.sym.block = NULL;
+ pascal_yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
+ xfree (uptokstart);
+ return NAME_OR_INT;
+ }
+ }
+
+ xfree(uptokstart);
+ /* Any other kind of symbol. */
+ pascal_yylval.ssym.sym.symbol = sym;
+ pascal_yylval.ssym.sym.block = NULL;
+ return NAME;
+ }
+}
+
+/* See p-exp-parser.h. */
+
+void
+pascal_yyerror (const char *msg)
+{
+ pstate->parse_error (msg);
+}
+
+} /* namespace p_exp_parser */
+
+/* See p-exp-parser.h. */
+
+int
+pascal_parse (struct parser_state *par_state)
+{
+ using namespace p_exp_parser;
+
+ /* Setting up the parser state. */
+ scoped_restore pstate_restore = make_scoped_restore (&pstate);
+ gdb_assert (par_state != NULL);
+ pstate = par_state;
+ paren_depth = 0;
+
+ int result = pascal_yyparse ();
+ if (!result)
+ pstate->set_operation (pstate->pop ());
+ return result;
+}
diff --git a/gdb/p-exp-parser.h b/gdb/p-exp-parser.h
new file mode 100644
index 000000000000..af1d0396ca5c
--- /dev/null
+++ b/gdb/p-exp-parser.h
@@ -0,0 +1,84 @@
+/* YACC parser support code for Pascal expressions, for GDB.
+
+ Copyright (C) 2000-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_P_EXP_PARSER_H
+#define GDB_P_EXP_PARSER_H
+
+#include "parser-defs.h"
+#include "p-lang.h"
+
+union p_exp_parser_YYSTYPE;
+
+namespace p_exp_parser {
+
+/* The state of the parser, used internally when we are parsing the
+ expression. */
+
+extern parser_state *pstate;
+
+/* The type of the sub-expression parsed most recently, or nullptr if it
+ is not known. */
+
+extern struct type *current_type;
+
+/* Non-zero if the left operand of the '/' operator being parsed has an
+ integral type. */
+
+extern int leftdiv_is_integer;
+
+/* Non-zero while the name being lexed should be looked up as a field of
+ CURRENT_TYPE. */
+
+extern int search_field;
+
+/* Save CURRENT_TYPE on an internal stack and reset it. */
+
+void push_current_type ();
+
+/* Restore CURRENT_TYPE from the internal stack. */
+
+void pop_current_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 *p, int len,
+ int parsed_float, p_exp_parser_YYSTYPE *putithere);
+
+/* Read one token, getting characters through lexptr. */
+
+int pascal_yylex ();
+
+/* The error handler invoked by the generated parser. Report MSG as a
+ parse error on the current parser state. */
+
+void pascal_yyerror (const char *msg);
+
+} /* namespace p_exp_parser */
+
+/* Parse a Pascal 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 pascal_parse (struct parser_state *par_state);
+
+#endif /* GDB_P_EXP_PARSER_H */
diff --git a/gdb/p-exp-parser.y b/gdb/p-exp-parser.y
index 0bb2fca36965..2d4dcd9c22c5 100644
--- a/gdb/p-exp-parser.y
+++ b/gdb/p-exp-parser.y
@@ -48,27 +48,11 @@
#include "parser-defs.h"
#include "language.h"
#include "p-lang.h"
+#include "p-exp-parser.h"
#include "block.h"
#include "expop.h"
-/* The state of the parser, used internally when we are parsing the
- expression. */
-
-static struct parser_state *pstate = NULL;
-
-/* Depth of parentheses. */
-static int paren_depth;
-
-int yyparse (void);
-
-static int yylex (void);
-
-static void yyerror (const char *);
-
-static char *uptok (const char *, int);
-
-static const char *pascal_skip_string (const char *str);
-
+using namespace p_exp_parser;
using namespace expr;
%}
@@ -101,18 +85,6 @@ using namespace expr;
int *ivec;
}
-%{
-/* YYSTYPE gets defined by %union */
-static int parse_number (struct parser_state *,
- const char *, int, int, YYSTYPE *);
-
-static struct type *current_type;
-static int leftdiv_is_integer;
-static void push_current_type (void);
-static void pop_current_type (void);
-static int search_field;
-%}
-
%type <voidval> exp exp1 type_exp start normal_start variable qualified_name
%type <tval> type typebase
/* %type <bval> block */
@@ -778,895 +750,3 @@ name_not_typename : NAME
| NAME_OR_INT
*/
;
-
-%%
-
-/* Take care of parsing a number (anything that starts with a digit).
- Set yylval and return the token type; update lexptr.
- LEN is the number of characters in it. */
-
-/*** Needs some error checking for the float case ***/
-
-static int
-parse_number (struct parser_state *par_state,
- const char *p, int len, int parsed_float, YYSTYPE *putithere)
-{
- ULONGEST n = 0;
- ULONGEST prevn = 0;
-
- int i = 0;
- int c;
- int base = input_radix;
- int unsigned_p = 0;
-
- /* Number of "L" suffixes encountered. */
- int long_p = 0;
-
- /* We have found a "L" or "U" suffix. */
- int found_suffix = 0;
-
- if (parsed_float)
- {
- /* Handle suffixes: 'f' for float, 'l' for long double.
- FIXME: This appears to be an extension -- do we want this? */
- if (len >= 1 && c_tolower (p[len - 1]) == 'f')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_float;
- len--;
- }
- else if (len >= 1 && c_tolower (p[len - 1]) == 'l')
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_long_double;
- len--;
- }
- /* Default type for floating-point literals is double. */
- else
- {
- putithere->typed_val_float.type
- = parse_type (par_state)->builtin_double;
- }
-
- if (!parse_float (p, len,
- putithere->typed_val_float.type,
- putithere->typed_val_float.val))
- return ERROR;
- return FLOAT;
- }
-
- /* Handle base-switching prefixes 0x, 0t, 0d, 0. */
- if (p[0] == '0' && len > 1)
- switch (p[1])
- {
- case 'x':
- case 'X':
- if (len >= 3)
- {
- p += 2;
- base = 16;
- len -= 2;
- }
- break;
-
- case 't':
- case 'T':
- case 'd':
- case 'D':
- if (len >= 3)
- {
- p += 2;
- base = 10;
- len -= 2;
- }
- break;
-
- default:
- base = 8;
- break;
- }
-
- while (len-- > 0)
- {
- c = *p++;
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- if (c != 'l' && c != 'u')
- n *= base;
- if (c >= '0' && c <= '9')
- {
- if (found_suffix)
- return ERROR;
- n += i = c - '0';
- }
- else
- {
- if (base > 10 && c >= 'a' && c <= 'f')
- {
- if (found_suffix)
- return ERROR;
- n += i = c - 'a' + 10;
- }
- else if (c == 'l')
- {
- ++long_p;
- found_suffix = 1;
- }
- else if (c == 'u')
- {
- unsigned_p = 1;
- found_suffix = 1;
- }
- else
- return ERROR; /* Char not a digit */
- }
- if (i >= base)
- return ERROR; /* Invalid digit in this base. */
-
- if (c != 'l' && c != 'u')
- {
- /* Test for overflow. */
- if (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 = !unsigned_p;
- bool have_int = long_p == 0;
- bool have_long = long_p <= 1;
- if (have_int && have_signed && fits_in_type (1, n, int_bits, true))
- putithere->typed_val_int.type = parse_type (par_state)->builtin_int;
- else if (have_int && fits_in_type (1, n, int_bits, false))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_unsigned_int;
- else if (have_long && have_signed && fits_in_type (1, n, long_bits, true))
- putithere->typed_val_int.type = parse_type (par_state)->builtin_long;
- else if (have_long && fits_in_type (1, n, long_bits, false))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_unsigned_long;
- else if (have_signed && fits_in_type (1, n, long_long_bits, true))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_long_long;
- else if (fits_in_type (1, n, long_long_bits, false))
- putithere->typed_val_int.type
- = parse_type (par_state)->builtin_unsigned_long_long;
- else
- error (_("Numeric constant too large."));
- putithere->typed_val_int.val = n;
-
- return INT;
-}
-
-
-struct type_push
-{
- struct type *stored;
- struct type_push *next;
-};
-
-static struct type_push *tp_top = NULL;
-
-static void
-push_current_type (void)
-{
- struct type_push *tpnew;
- tpnew = (struct type_push *) malloc (sizeof (struct type_push));
- tpnew->next = tp_top;
- tpnew->stored = current_type;
- current_type = NULL;
- tp_top = tpnew;
-}
-
-static void
-pop_current_type (void)
-{
- struct type_push *tp = tp_top;
- if (tp)
- {
- current_type = tp->stored;
- tp_top = tp->next;
- free (tp);
- }
-}
-
-struct p_token
-{
- const char *oper;
- int token;
- enum exp_opcode opcode;
-};
-
-static const struct p_token tokentab3[] =
- {
- {"shr", RSH, OP_NULL},
- {"shl", LSH, OP_NULL},
- {"and", ANDAND, OP_NULL},
- {"div", DIV, OP_NULL},
- {"not", NOT, OP_NULL},
- {"mod", MOD, OP_NULL},
- {"inc", INCREMENT, OP_NULL},
- {"dec", DECREMENT, OP_NULL},
- {"xor", XOR, OP_NULL}
- };
-
-static const struct p_token tokentab2[] =
- {
- {"or", OR, OP_NULL},
- {"<>", NOTEQUAL, OP_NULL},
- {"<=", LEQ, OP_NULL},
- {">=", GEQ, OP_NULL},
- {":=", ASSIGN, OP_NULL},
- {"::", COLONCOLON, OP_NULL} };
-
-/* Allocate uppercased var: */
-/* make an uppercased copy of tokstart. */
-static char *
-uptok (const char *tokstart, int namelen)
-{
- int i;
- char *uptokstart = (char *)malloc(namelen+1);
- for (i = 0;i <= namelen;i++)
- {
- if ((tokstart[i]>='a' && tokstart[i]<='z'))
- uptokstart[i] = tokstart[i]-('a'-'A');
- else
- uptokstart[i] = tokstart[i];
- }
- uptokstart[namelen]='\0';
- return uptokstart;
-}
-
-/* Skip over a Pascal string. STR must point to the opening single quote
- character. This function returns a pointer to the character after the
- closing single quote character.
-
- This function does not support embedded, escaped single quotes, which
- is done by placing two consecutive single quotes into a string.
- Support for this would be easy to add, but this function is only used
- from the Python expression parser, and if we did skip over escaped
- quotes then the rest of the expression parser wouldn't handle them
- correctly. */
-static const char *
-pascal_skip_string (const char *str)
-{
- gdb_assert (*str == '\'');
-
- do
- ++str;
- while (*str != '\0' && *str != '\'');
-
- return str;
-}
-
-/* Read one token, getting characters through lexptr. */
-
-static int
-yylex (void)
-{
- int c;
- int namelen;
- const char *tokstart;
- char *uptokstart;
- const char *tokptr;
- int explen, tempbufindex;
- static char *tempbuf;
- static int tempbufsize;
-
- retry:
-
- pstate->prev_lexptr = pstate->lexptr;
-
- tokstart = pstate->lexptr;
- explen = strlen (pstate->lexptr);
-
- /* See if it is a special token of length 3. */
- if (explen > 2)
- for (const auto &token : tokentab3)
- if (strncasecmp (tokstart, token.oper, 3) == 0
- && (!c_isalpha (token.oper[0]) || explen == 3
- || (!c_isalpha (tokstart[3])
- && !c_isdigit (tokstart[3]) && tokstart[3] != '_')))
- {
- pstate->lexptr += 3;
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- /* See if it is a special token of length 2. */
- if (explen > 1)
- for (const auto &token : tokentab2)
- if (strncasecmp (tokstart, token.oper, 2) == 0
- && (!c_isalpha (token.oper[0]) || explen == 2
- || (!c_isalpha (tokstart[2])
- && !c_isdigit (tokstart[2]) && tokstart[2] != '_')))
- {
- pstate->lexptr += 2;
- yylval.opcode = token.opcode;
- return token.token;
- }
-
- switch (c = *tokstart)
- {
- case 0:
- if (search_field && pstate->parse_completion)
- return COMPLETE;
- else
- return 0;
-
- case ' ':
- case '\t':
- case '\n':
- pstate->lexptr++;
- goto retry;
-
- case '\'':
- /* We either have a character constant ('0' or '\177' for example)
- or we have a quoted symbol reference ('foo(int,int)' in object pascal
- for example). */
- pstate->lexptr++;
- c = *pstate->lexptr++;
- if (c == '\\')
- c = parse_escape (pstate->gdbarch (), &pstate->lexptr);
- else if (c == '\'')
- error (_("Empty character constant."));
-
- yylval.typed_val_int.val = c;
- yylval.typed_val_int.type = parse_type (pstate)->builtin_char;
-
- c = *pstate->lexptr++;
- if (c != '\'')
- {
- namelen = pascal_skip_string (tokstart) - tokstart;
- if (namelen > 2)
- {
- pstate->lexptr = tokstart + namelen;
- if (pstate->lexptr[-1] != '\'')
- error (_("Unmatched single quote."));
- namelen -= 2;
- tokstart++;
- uptokstart = uptok(tokstart,namelen);
- goto tryname;
- }
- error (_("Invalid character constant."));
- }
- return INT;
-
- case '(':
- paren_depth++;
- pstate->lexptr++;
- return c;
-
- case ')':
- if (paren_depth == 0)
- return 0;
- paren_depth--;
- pstate->lexptr++;
- return c;
-
- case ',':
- if (pstate->comma_terminates && paren_depth == 0)
- return 0;
- pstate->lexptr++;
- return c;
-
- case '.':
- /* Might be a floating point number. */
- if (pstate->lexptr[1] < '0' || pstate->lexptr[1] > '9')
- {
- goto symbol; /* Nope, must be a symbol. */
- }
-
- [[fallthrough]];
-
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- {
- /* It's a number. */
- int got_dot = 0, got_e = 0, toktype;
- const char *p = tokstart;
- int hex = input_radix > 10;
-
- if (c == '0' && (p[1] == 'x' || p[1] == 'X'))
- {
- p += 2;
- hex = 1;
- }
- else if (c == '0' && (p[1]=='t' || p[1]=='T'
- || p[1]=='d' || p[1]=='D'))
- {
- p += 2;
- hex = 0;
- }
-
- for (;; ++p)
- {
- /* This test includes !hex because 'e' is a valid hex digit
- and thus does not indicate a floating point number when
- the radix is hex. */
- if (!hex && !got_e && (*p == 'e' || *p == 'E'))
- got_dot = got_e = 1;
- /* This test does not include !hex, because a '.' always indicates
- a decimal floating point number regardless of the radix. */
- else if (!got_dot && *p == '.')
- got_dot = 1;
- else if (got_e && (p[-1] == 'e' || p[-1] == 'E')
- && (*p == '-' || *p == '+'))
- /* This is the sign of the exponent, not the end of the
- number. */
- continue;
- /* We will take any letters or digits. parse_number will
- complain if past the radix, or if L or U are not final. */
- else if ((*p < '0' || *p > '9')
- && ((*p < 'a' || *p > 'z')
- && (*p < 'A' || *p > 'Z')))
- break;
- }
- toktype = parse_number (pstate, tokstart,
- p - tokstart, got_dot | got_e, &yylval);
- if (toktype == ERROR)
- error (_("Invalid number \"%.*s\"."), (int) (p - tokstart),
- tokstart);
- pstate->lexptr = p;
- return toktype;
- }
-
- case '+':
- case '-':
- case '*':
- case '/':
- case '|':
- case '&':
- case '^':
- case '~':
- case '!':
- case '@':
- case '<':
- case '>':
- case '[':
- case ']':
- case '?':
- case ':':
- case '=':
- case '{':
- case '}':
- symbol:
- pstate->lexptr++;
- return c;
-
- case '"':
-
- /* Build the gdb internal form of the input string in tempbuf,
- translating any standard C escape forms seen. 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. */
-
- tokptr = ++tokstart;
- tempbufindex = 0;
-
- do {
- /* Grow the static temp buffer if necessary, including allocating
- the first one on demand. */
- if (tempbufindex + 1 >= tempbufsize)
- {
- tempbuf = (char *) realloc (tempbuf, tempbufsize += 64);
- }
-
- switch (*tokptr)
- {
- case '\0':
- case '"':
- /* Do nothing, loop will terminate. */
- break;
- case '\\':
- ++tokptr;
- c = parse_escape (pstate->gdbarch (), &tokptr);
- if (c == -1)
- {
- continue;
- }
- tempbuf[tempbufindex++] = c;
- break;
- default:
- tempbuf[tempbufindex++] = *tokptr++;
- break;
- }
- } while ((*tokptr != '"') && (*tokptr != '\0'));
- if (*tokptr++ != '"')
- {
- error (_("Unterminated string in expression."));
- }
- tempbuf[tempbufindex] = '\0'; /* See note above. */
- yylval.sval.ptr = tempbuf;
- yylval.sval.length = tempbufindex;
- pstate->lexptr = tokptr;
- return (STRING);
- }
-
- if (!(c == '_' || c == '$'
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')))
- /* We must have come across a bad character (e.g. ';'). */
- error (_("Invalid character '%c' in expression."), c);
-
- /* It's a name. See how long it is. */
- namelen = 0;
- for (c = tokstart[namelen];
- (c == '_' || c == '$' || (c >= '0' && c <= '9')
- || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '<');)
- {
- /* Template parameter lists are part of the name.
- FIXME: This mishandles `print $a<4&&$a>3'. */
- if (c == '<')
- {
- int i = namelen;
- int nesting_level = 1;
- while (tokstart[++i])
- {
- if (tokstart[i] == '<')
- nesting_level++;
- else if (tokstart[i] == '>')
- {
- if (--nesting_level == 0)
- break;
- }
- }
- if (tokstart[i] == '>')
- namelen = i;
- else
- break;
- }
-
- /* do NOT uppercase internals because of registers !!! */
- c = tokstart[++namelen];
- }
-
- uptokstart = uptok(tokstart,namelen);
-
- /* The token "if" terminates the expression and is NOT
- removed from the input stream. */
- if (namelen == 2 && uptokstart[0] == 'I' && uptokstart[1] == 'F')
- {
- free (uptokstart);
- return 0;
- }
-
- pstate->lexptr += namelen;
-
- tryname:
-
- /* Catch specific keywords. Should be done with a data structure. */
- switch (namelen)
- {
- case 6:
- if (streq (uptokstart, "OBJECT"))
- {
- free (uptokstart);
- return CLASS;
- }
- if (streq (uptokstart, "RECORD"))
- {
- free (uptokstart);
- return STRUCT;
- }
- if (streq (uptokstart, "SIZEOF"))
- {
- free (uptokstart);
- return SIZEOF;
- }
- break;
- case 5:
- if (streq (uptokstart, "CLASS"))
- {
- free (uptokstart);
- return CLASS;
- }
- if (streq (uptokstart, "FALSE"))
- {
- yylval.lval = 0;
- free (uptokstart);
- return FALSEKEYWORD;
- }
- break;
- case 4:
- if (streq (uptokstart, "TRUE"))
- {
- yylval.lval = 1;
- free (uptokstart);
- return TRUEKEYWORD;
- }
- if (streq (uptokstart, "SELF"))
- {
- /* Here we search for 'this' like
- inserted in FPC stabs debug info. */
- static const char this_name[] = "this";
-
- if (lookup_symbol (this_name, pstate->expression_context_block,
- SEARCH_VFT, NULL).symbol)
- {
- free (uptokstart);
- return THIS;
- }
- }
- break;
- default:
- break;
- }
-
- yylval.sval.ptr = tokstart;
- yylval.sval.length = namelen;
-
- if (*tokstart == '$')
- {
- free (uptokstart);
- return DOLLAR_VARIABLE;
- }
-
- /* Use token-type BLOCKNAME for symbols that happen to be defined as
- functions or symtabs. If this is not so, then ...
- Use token-type TYPENAME for symbols that happen to be defined
- currently as names of types; NAME for other symbols.
- The caller is not constrained to care about the distinction. */
- {
- std::string tmp = copy_name (yylval.sval);
- struct symbol *sym;
- struct field_of_this_result is_a_field_of_this;
- int is_a_field = 0;
- int hextype;
-
- is_a_field_of_this.type = NULL;
- if (search_field && current_type)
- is_a_field = (lookup_struct_elt_type (current_type,
- tmp.c_str (), 1) != NULL);
- if (is_a_field)
- sym = NULL;
- else
- sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
- SEARCH_VFT, &is_a_field_of_this).symbol;
- /* second chance uppercased (as Free Pascal does). */
- if (!sym && is_a_field_of_this.type == NULL && !is_a_field)
- {
- for (int i = 0; i <= namelen; i++)
- {
- if ((tmp[i] >= 'a' && tmp[i] <= 'z'))
- tmp[i] -= ('a'-'A');
- }
- if (search_field && current_type)
- is_a_field = (lookup_struct_elt_type (current_type,
- tmp.c_str (), 1) != NULL);
- if (is_a_field)
- sym = NULL;
- else
- sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
- SEARCH_VFT, &is_a_field_of_this).symbol;
- }
- /* Third chance Capitalized (as GPC does). */
- if (!sym && is_a_field_of_this.type == NULL && !is_a_field)
- {
- for (int i = 0; i <= namelen; i++)
- {
- if (i == 0)
- {
- if ((tmp[i] >= 'a' && tmp[i] <= 'z'))
- tmp[i] -= ('a'-'A');
- }
- else
- if ((tmp[i] >= 'A' && tmp[i] <= 'Z'))
- tmp[i] -= ('A'-'a');
- }
- if (search_field && current_type)
- is_a_field = (lookup_struct_elt_type (current_type,
- tmp.c_str (), 1) != NULL);
- if (is_a_field)
- sym = NULL;
- else
- sym = lookup_symbol (tmp.c_str (), pstate->expression_context_block,
- SEARCH_VFT, &is_a_field_of_this).symbol;
- }
-
- if (is_a_field || (is_a_field_of_this.type != NULL))
- {
- tempbuf = (char *) realloc (tempbuf, namelen + 1);
- strncpy (tempbuf, tmp.c_str (), namelen);
- tempbuf [namelen] = 0;
- yylval.sval.ptr = tempbuf;
- yylval.sval.length = namelen;
- yylval.ssym.sym.symbol = NULL;
- yylval.ssym.sym.block = NULL;
- free (uptokstart);
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- if (is_a_field)
- return FIELDNAME;
- else
- return NAME;
- }
- /* Call lookup_symtab, not lookup_partial_symtab, in case there are
- no psymtabs (coff, xcoff, or some future change to blow away the
- psymtabs once once symbols are read). */
- if ((sym && sym->loc_class () == LOC_BLOCK)
- || lookup_symtab (current_program_space, tmp.c_str ()))
- {
- yylval.ssym.sym.symbol = sym;
- yylval.ssym.sym.block = NULL;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- free (uptokstart);
- return BLOCKNAME;
- }
- if (sym && sym->loc_class () == LOC_TYPEDEF)
- {
-#if 1
- /* Despite the following flaw, we need to keep this code enabled.
- Because we can get called from check_stub_method, if we don't
- handle nested types then it screws many operations in any
- program which uses nested types. */
- /* In "A::x", if x is a member function of A and there happens
- to be a type (nested or not, since the stabs don't make that
- distinction) named x, then this code incorrectly thinks we
- are dealing with nested types rather than a member function. */
-
- const char *p;
- const char *namestart;
- struct symbol *best_sym;
-
- /* Look ahead to detect nested types. This probably should be
- done in the grammar, but trying seemed to introduce a lot
- of shift/reduce and reduce/reduce conflicts. It's possible
- that it could be done, though. Or perhaps a non-grammar, but
- less ad hoc, approach would work well. */
-
- /* Since we do not currently have any way of distinguishing
- a nested type from a non-nested one (the stabs don't tell
- us whether a type is nested), we just ignore the
- containing type. */
-
- p = pstate->lexptr;
- best_sym = sym;
- while (1)
- {
- /* Skip whitespace. */
- p = skip_spaces (p);
- if (*p == ':' && p[1] == ':')
- {
- /* Skip the `::'. */
- p += 2;
- /* Skip whitespace. */
- p = skip_spaces (p);
- namestart = p;
- while (*p == '_' || *p == '$' || (*p >= '0' && *p <= '9')
- || (*p >= 'a' && *p <= 'z')
- || (*p >= 'A' && *p <= 'Z'))
- ++p;
- if (p != namestart)
- {
- struct symbol *cur_sym;
- /* As big as the whole rest of the expression, which is
- at least big enough. */
- char *ncopy
- = (char *) alloca (tmp.size () + strlen (namestart)
- + 3);
- char *tmp1;
-
- tmp1 = ncopy;
- memcpy (tmp1, tmp.c_str (), tmp.size ());
- tmp1 += tmp.size ();
- memcpy (tmp1, "::", 2);
- tmp1 += 2;
- memcpy (tmp1, namestart, p - namestart);
- tmp1[p - namestart] = '\0';
- cur_sym
- = lookup_symbol (ncopy,
- pstate->expression_context_block,
- SEARCH_VFT, NULL).symbol;
- if (cur_sym)
- {
- if (cur_sym->loc_class () == LOC_TYPEDEF)
- {
- best_sym = cur_sym;
- pstate->lexptr = p;
- }
- else
- break;
- }
- else
- break;
- }
- else
- break;
- }
- else
- break;
- }
-
- yylval.tsym.type = best_sym->type ();
-#else /* not 0 */
- yylval.tsym.type = sym->type ();
-#endif /* not 0 */
- free (uptokstart);
- return TYPENAME;
- }
- yylval.tsym.type
- = language_lookup_primitive_type (pstate->language (),
- pstate->gdbarch (), tmp.c_str ());
- if (yylval.tsym.type != NULL)
- {
- free (uptokstart);
- return TYPENAME;
- }
-
- /* Input names that aren't symbols but ARE valid hex numbers,
- when the input radix permits them, can be names or numbers
- depending on the parse. Note we support radixes > 16 here. */
- if (!sym
- && ((tokstart[0] >= 'a' && tokstart[0] < 'a' + input_radix - 10)
- || (tokstart[0] >= 'A' && tokstart[0] < 'A' + input_radix - 10)))
- {
- YYSTYPE newlval; /* Its value is ignored. */
- hextype = parse_number (pstate, tokstart, namelen, 0, &newlval);
- if (hextype == INT)
- {
- yylval.ssym.sym.symbol = sym;
- yylval.ssym.sym.block = NULL;
- yylval.ssym.is_a_field_of_this = is_a_field_of_this.type != NULL;
- free (uptokstart);
- return NAME_OR_INT;
- }
- }
-
- free(uptokstart);
- /* Any other kind of symbol. */
- yylval.ssym.sym.symbol = sym;
- yylval.ssym.sym.block = NULL;
- return NAME;
- }
-}
-
-/* See language.h. */
-
-int
-pascal_language::parser (struct parser_state *par_state) const
-{
- /* Setting up the parser state. */
- scoped_restore pstate_restore = make_scoped_restore (&pstate);
- gdb_assert (par_state != NULL);
- pstate = par_state;
- paren_depth = 0;
-
- int result = yyparse ();
- if (!result)
- pstate->set_operation (pstate->pop ());
- return result;
-}
-
-static void
-yyerror (const char *msg)
-{
- pstate->parse_error (msg);
-}
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 457642e00eb5..22e3fe53223b 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -28,6 +28,7 @@
#include "language.h"
#include "varobj.h"
#include "p-lang.h"
+#include "p-exp-parser.h"
#include "valprint.h"
#include "value.h"
#include "c-lang.h"
@@ -162,6 +163,14 @@ class pascal_wchar_printer : public wchar_printer
/* See language.h. */
+int
+pascal_language::parser (struct parser_state *ps) const
+{
+ return pascal_parse (ps);
+}
+
+/* See language.h. */
+
void
pascal_language::printchar (int c, struct type *type,
struct ui_file *stream) const
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 19/19] gdb: honor "set debug parser" in the Modula-2 and Pascal parsers
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
` (17 preceding siblings ...)
2026-09-05 4:23 ` [PATCH v2 18/19] gdb: move p-exp-parser.y's support code to p-exp-parser.c simon.marchi
@ 2026-09-05 4:23 ` simon.marchi
18 siblings, 0 replies; 21+ messages in thread
From: simon.marchi @ 2026-09-05 4:23 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
From: Simon Marchi <simon.marchi@polymtl.ca>
All expression parsers make the generated parser produce its debug output
when parser_state::debug is set, by setting their <prefix>_yydebug
variable, except the Modula-2 and Pascal ones. Add it for Modula-2 and
Pascal. As a side effect, this makes the declarations of these two
variables used, which they were not before.
Change-Id: If5b1abc053d1c20e2ad3cc5b4729ffebe13f5c4e
---
gdb/m2-exp-parser.c | 3 +++
gdb/p-exp-parser.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/gdb/m2-exp-parser.c b/gdb/m2-exp-parser.c
index 821f1507aef3..a1fff9f58c66 100644
--- a/gdb/m2-exp-parser.c
+++ b/gdb/m2-exp-parser.c
@@ -481,6 +481,9 @@ m2_parse (struct parser_state *par_state)
pstate = par_state;
paren_depth = 0;
+ scoped_restore restore_yydebug = make_scoped_restore (&m2_yydebug,
+ par_state->debug);
+
int result = m2_yyparse ();
if (!result)
pstate->set_operation (pstate->pop ());
diff --git a/gdb/p-exp-parser.c b/gdb/p-exp-parser.c
index 108c839d3f51..c013eceecbfc 100644
--- a/gdb/p-exp-parser.c
+++ b/gdb/p-exp-parser.c
@@ -947,6 +947,9 @@ pascal_parse (struct parser_state *par_state)
pstate = par_state;
paren_depth = 0;
+ scoped_restore restore_yydebug = make_scoped_restore (&pascal_yydebug,
+ par_state->debug);
+
int result = pascal_yyparse ();
if (!result)
pstate->set_operation (pstate->pop ());
--
2.55.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c
2026-09-05 4:23 ` [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c simon.marchi
@ 2026-09-08 18:28 ` Kevin Buettner
0 siblings, 0 replies; 21+ messages in thread
From: Kevin Buettner @ 2026-09-08 18:28 UTC (permalink / raw)
To: gdb-patches; +Cc: simon.marchi
On Sat, 5 Sep 2026 00:23:16 -0400
simon.marchi@polymtl.ca wrote:
> diff --git a/gdb/ada-exp-parser.y b/gdb/ada-exp-parser.y
> index 433293d22ad9..0a629f82cc86 100644
> --- a/gdb/ada-exp-parser.y
> +++ b/gdb/ada-exp-parser.y
> @@ -40,412 +40,19 @@
> #include "value.h"
> #include "parser-defs.h"
> #include "language.h"
> +#include "ada-exp-parser.h"
> #include "ada-lang.h"
> #include "frame.h"
> #include "block.h"
> #include "ada-exp.h"
> +#include "ada-exp-parser.h"
> #include "cli/cli-style.h"
Nit: ada-exp-parser.h is included twice here. (Found by AI.)
My AIs (GLM 5.3 Flash and Claude Opus for detailed patch review)
checked the rest of the series, including build and regression
testing. No problems found except for the above nit.
You still have my approval for the entire series:
Approved-By: Kevin Buettner <kevinb@redhat.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-09-08 18:28 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-05 4:23 [PATCH v2 00/19] Move C++ support code out of .y files simon.marchi
2026-09-05 4:23 ` [PATCH v2 01/19] gdb/ada-exp-parser: remove name_info struct simon.marchi
2026-09-05 4:23 ` [PATCH v2 02/19] gdb: replace parse_type macros with functions simon.marchi
2026-09-05 4:23 ` [PATCH v2 03/19] gdb: suffix flex/bison output files with -gen.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 04/19] gdb: remove YY_NULL to YY_NULLPTR substitution simon.marchi
2026-09-05 4:23 ` [PATCH v2 05/19] gdb: move parser output post-processing to a script simon.marchi
2026-09-05 4:23 ` [PATCH v2 06/19] gdb: let the parser and lexer generators prefix their symbols simon.marchi
2026-09-05 4:23 ` [PATCH v2 07/19] gdb: separate cp-name-parser's symbol prefix with an underscore simon.marchi
2026-09-05 4:23 ` [PATCH v2 08/19] gdb: make $(YACC) and $(FLEX) generate headers simon.marchi
2026-09-05 4:23 ` [PATCH v2 09/19] gdb: add check for stale build generated files simon.marchi
2026-09-05 4:23 ` [PATCH v2 10/19] gdb: move cp-name-parser.y's support code to cp-name-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 11/19] gdb: rename LANG-exp.y to LANG-exp-parser.y simon.marchi
2026-09-05 4:23 ` [PATCH v2 12/19] gdb: move c-exp-parser.y's support code to c-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 13/19] gdb: move ada-exp-parser.y's support code to ada-exp-parser.c simon.marchi
2026-09-08 18:28 ` Kevin Buettner
2026-09-05 4:23 ` [PATCH v2 14/19] gdb: move d-exp-parser.y's support code to d-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 15/19] gdb: move f-exp-parser.y's support code to f-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 16/19] gdb: move go-exp-parser.y's support code to go-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 17/19] gdb: move m2-exp-parser.y's support code to m2-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 18/19] gdb: move p-exp-parser.y's support code to p-exp-parser.c simon.marchi
2026-09-05 4:23 ` [PATCH v2 19/19] gdb: honor "set debug parser" in the Modula-2 and Pascal parsers simon.marchi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox