diff --git a/data/skeletons/glr2.cc b/data/skeletons/glr2.cc
index 4f7e89d2d..1bbd623ad 100644
--- a/data/skeletons/glr2.cc
+++ b/data/skeletons/glr2.cc
@@ -2092,7 +2092,7 @@ class glr_stack
{
state_set_index yynewStack = yystateStack.yysplitStack (yyk);
YY_DEBUG_STREAM << "Splitting off stack " << yynewStack.get()
- << " from " << yyk.get();
+ << " from " << yyk.get() << '\n';
YYRESULTTAG yyflag =
yyglrReduce (yynewStack, *yyconflicts,
yyimmediate[*yyconflicts]);
diff --git a/examples/c++/calc++/local.mk b/examples/c++/calc++/local.mk
index 00f47a483..5cc1ed30f 100644
--- a/examples/c++/calc++/local.mk
+++ b/examples/c++/calc++/local.mk
@@ -18,7 +18,6 @@
## Parser generation. ##
## ------------------- ##
-%D%/parser.stamp: $(dependencies)
SUFFIXES += .yy .stamp
.yy.stamp:
$(AM_V_YACC)rm -f $@
@@ -26,6 +25,7 @@ SUFFIXES += .yy .stamp
$(AM_V_at)$(YACCCOMPILE) -o $*.cc $<
$(AM_V_at)mv -f $@.tmp $@
+%D%/parser.stamp: $(dependencies)
$(calcxx_sources_generated): %D%/parser.stamp
@test -f $@ || rm -f %D%/parser.stamp
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) %D%/parser.stamp
diff --git a/examples/c++/glr/Makefile b/examples/c++/glr/Makefile
new file mode 100644
index 000000000..a56edb1f4
--- /dev/null
+++ b/examples/c++/glr/Makefile
@@ -0,0 +1,28 @@
+# This Makefile is designed to be simple and readable. It does not
+# aim at portability. It requires GNU Make.
+
+BASE = c++-types
+BISON = bison
+XSLTPROC = xsltproc
+
+all: $(BASE)
+
+%.c %.h %.xml %.gv: %.y
+ $(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
+
+$(BASE): $(BASE).o
+ $(CC) $(CFLAGS) -o $@ $^
+
+run: $(BASE)
+ @echo "Type C++ declarations or expressions. Quit with ctrl-d."
+ ./$<
+
+html: $(BASE).html
+%.html: %.xml
+ $(XSLTPROC) $(XSLTPROCFLAGS) -o $@ $$($(BISON) --print-datadir)/xslt/xml2xhtml.xsl $<
+
+CLEANFILES = \
+ $(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
+
+clean:
+ rm -f $(CLEANFILES)
diff --git a/examples/c++/glr/README.md b/examples/c++/glr/README.md
new file mode 100644
index 000000000..60de8d654
--- /dev/null
+++ b/examples/c++/glr/README.md
@@ -0,0 +1,24 @@
+# glr
+
+This example demonstrates the use of GLR parsers to handle (local)
+ambiguities in the C++ language. See the node "Merging GLR Parses" in
+Bison's documentation.
+
+
diff --git a/examples/c++/glr/c++-types.test b/examples/c++/glr/c++-types.test
new file mode 100644
index 000000000..6f55c8ab8
--- /dev/null
+++ b/examples/c++/glr/c++-types.test
@@ -0,0 +1,52 @@
+#! /bin/sh
+
+# Copyright (C) 2020 Free Software Foundation, Inc.
+#
+# 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 .
+
+cat >input <(T, x)
+5.0-6: (T, x, y)
+7.0-4: =(x, y)"
+
+exit 77
+
+cat >input <(x, T), y)
+3.0-4: ((T, x), (x, T))
+5.0-12: ((T, y, +(z, q)), =((y, T), +(z, q)))
+7.0-14:
+9.0-4: +(z, q)
+err: 7.5: syntax error, unexpected identifier, expecting '=' or '+' or ')'"
diff --git a/examples/c++/glr/c++-types.yy b/examples/c++/glr/c++-types.yy
new file mode 100644
index 000000000..5c76c83c5
--- /dev/null
+++ b/examples/c++/glr/c++-types.yy
@@ -0,0 +1,255 @@
+/* Simplified -*- C++ -*- Type and Expression Grammar. */
+
+%glr-parser
+%skeleton "glr2.cc"
+%header
+%locations
+%debug
+
+/* Nice error messages with details. */
+%define parse.error detailed
+
+%code requires
+{
+ union Node {
+ struct {
+ int isNterm;
+ int parents;
+ } nodeInfo;
+ struct {
+ int isNterm; /* 1 */
+ int parents;
+ char const *form;
+ union Node *children[3];
+ } nterm;
+ struct {
+ int isNterm; /* 0 */
+ int parents;
+ char *text;
+ } term;
+ };
+ typedef union Node Node;
+}
+
+%define api.value.type {Node *}
+
+%code
+{
+
+#include
+#include
+#include
+#include
+#include
+
+ static Node *new_nterm (char const *form, Node *child0 = nullptr, Node *child1 = nullptr, Node *child2 = nullptr);
+ static Node *new_term (char *);
+ static void free_node (Node *);
+ static std::ostream& operator<< (std::ostream& o, const Node &node);
+ static YYSTYPE stmtMerge (YYSTYPE x0, YYSTYPE x1);
+
+ static int yylex (YYSTYPE *lvalp, YYLTYPE *llocp);
+}
+
+%expect-rr 1
+
+%token
+ TYPENAME "typename"
+ ID "identifier"
+
+%right '='
+%left '+'
+
+%destructor { free_node ($$); } stmt expr decl declarator TYPENAME ID
+
+%%
+
+prog : %empty
+ | prog stmt { std::cout << @2 << ": " << *$2 << '\n'; free_node ($2); }
+ ;
+
+stmt : expr ';' %merge { $$ = $1; }
+ | decl %merge
+ | error ';' { $$ = new_nterm (""); }
+ | '@' { $$ = $1; YYACCEPT; }
+ ;
+
+expr : ID
+ | TYPENAME '(' expr ')'
+ { $$ = new_nterm ("", $3, $1); }
+ | expr '+' expr { $$ = new_nterm ("+", $1, $3); }
+ | expr '=' expr { $$ = new_nterm ("=", $1, $3); }
+ ;
+
+decl : TYPENAME declarator ';'
+ { $$ = new_nterm ("", $1, $2); }
+ | TYPENAME declarator '=' expr ';'
+ { $$ = new_nterm ("", $1,
+ $2, $4); }
+ ;
+
+declarator
+ : ID
+ | '(' declarator ')' { $$ = $2; }
+ ;
+
+%%
+
+int
+main (int argc, char **argv)
+{
+ // Enable parse traces on option -p.
+ if (1 < argc && strcmp (argv[1], "-p") == 0)
+ yydebug = 1;
+ yy::parser parser;
+ return !!parser.parse ();
+}
+
+
+/* A C error reporting function. */
+void yy::parser::error (const location_type& l, const std::string& m)
+{
+ std::cerr << l << ": " << m << '\n';
+}
+
+int yylex (YYSTYPE *lvalp, YYLTYPE *llocp)
+{
+ static int lineNum = 1;
+ static int colNum = 0;
+
+ while (1)
+ {
+ int c;
+ assert (!feof (stdin));
+ c = getchar ();
+ switch (c)
+ {
+ case EOF:
+ return 0;
+ case '\t':
+ colNum = (colNum + 7) & ~7;
+ break;
+ case ' ': case '\f':
+ colNum += 1;
+ break;
+ case '\n':
+ lineNum += 1;
+ colNum = 0;
+ break;
+ default:
+ {
+ int tok;
+ llocp->begin.line = llocp->end.line = lineNum;
+ llocp->begin.column = colNum;
+ if (isalpha (c))
+ {
+ char buffer[256];
+ unsigned i = 0;
+
+ do
+ {
+ buffer[i++] = static_cast (c);
+ colNum += 1;
+ assert (i != sizeof buffer - 1);
+ c = getchar ();
+ }
+ while (isalnum (c) || c == '_');
+
+ ungetc (c, stdin);
+ buffer[i++] = 0;
+ tok
+ = isupper (static_cast (buffer[0]))
+ ? yy::parser::token::TYPENAME
+ : yy::parser::token::ID;
+ *lvalp = new_term (strcpy (static_cast (malloc (i)), buffer));
+ }
+ else
+ {
+ colNum += 1;
+ tok = c;
+ *lvalp = NULL;
+ }
+ llocp->end.column = colNum-1;
+ return tok;
+ }
+ }
+ }
+}
+
+static Node *
+new_nterm (char const *form, Node *child0, Node *child1, Node *child2)
+{
+ Node *res = new Node;
+ res->nterm.isNterm = 1;
+ res->nterm.parents = 0;
+ res->nterm.form = form;
+ res->nterm.children[0] = child0;
+ if (child0)
+ child0->nodeInfo.parents += 1;
+ res->nterm.children[1] = child1;
+ if (child1)
+ child1->nodeInfo.parents += 1;
+ res->nterm.children[2] = child2;
+ if (child2)
+ child2->nodeInfo.parents += 1;
+ return res;
+}
+
+static Node *
+new_term (char *text)
+{
+ Node *res = new Node;
+ res->term.isNterm = 0;
+ res->term.parents = 0;
+ res->term.text = text;
+ return res;
+}
+
+static void
+free_node (Node *node)
+{
+ if (!node)
+ return;
+ node->nodeInfo.parents -= 1;
+ /* Free only if 0 (last parent) or -1 (no parents). */
+ if (node->nodeInfo.parents > 0)
+ return;
+ if (node->nodeInfo.isNterm == 1)
+ {
+ free_node (node->nterm.children[0]);
+ free_node (node->nterm.children[1]);
+ free_node (node->nterm.children[2]);
+ }
+ else
+ free (node->term.text);
+ delete node;
+}
+
+static std::ostream&
+operator<< (std::ostream& o, const Node &node)
+{
+ if (node.nodeInfo.isNterm == 1)
+ {
+ o << node.nterm.form;
+ if (node.nterm.children[0])
+ {
+ o << '(' << *node.nterm.children[0];
+ if (node.nterm.children[1])
+ o << ", " << *node.nterm.children[1];
+ if (node.nterm.children[2])
+ o << ", " << *node.nterm.children[2];
+ o << ")";
+ }
+ }
+ else
+ o << node.term.text;
+ return o;
+}
+
+
+static YYSTYPE
+stmtMerge (YYSTYPE x0, YYSTYPE x1)
+{
+ return new_nterm ("", x0, x1);
+}
+
diff --git a/examples/c++/glr/local.mk b/examples/c++/glr/local.mk
new file mode 100644
index 000000000..8558651ab
--- /dev/null
+++ b/examples/c++/glr/local.mk
@@ -0,0 +1,46 @@
+## Copyright (C) 2020 Free Software Foundation, Inc.
+##
+## 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 .
+
+glrxxdir = $(docdir)/%D%
+
+## ----------- ##
+## c++-types. ##
+## ----------- ##
+
+%D%/c++-types.stamp: $(dependencies)
+$(nodist_%C%_c___types_SOURCES): %D%/c++-types.stamp
+ @test -f $@ || rm -f %D%/c++-types.stamp
+ @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) %D%/c++-types.stamp
+CLEANFILES += \
+ $(nodist_%C%_c___types_SOURCES) %D%/c++-types.stamp
+CLEANDIRS += %D%/*.dSYM
+
+## -------------------- ##
+## Building & testing. ##
+## -------------------- ##
+
+# Avoid using BUILT_SOURCES which is too global.
+$(%C%_c___types_OBJECTS): $(cxx_types_sources_generated)
+
+if ENABLE_CXX
+ check_PROGRAMS += %D%/c++-types
+ nodist_%C%_c___types_SOURCES = \
+ %D%/c++-types.cc \
+ %D%/c++-types.hh
+ # Don't use gnulib's system headers.
+ %C%_c___types_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
+ TESTS += %D%/c++-types.test
+endif ENABLE_CXX
+EXTRA_DIST += %D%/c++-types.yy %D%/c++-types.test
diff --git a/examples/c++/local.mk b/examples/c++/local.mk
index a41613fda..6af9ac941 100644
--- a/examples/c++/local.mk
+++ b/examples/c++/local.mk
@@ -15,6 +15,7 @@
cxxdir = $(docdir)/%D%
include %D%/calc++/local.mk
+include %D%/glr/local.mk
## -------- ##
## Simple. ##
diff --git a/examples/c/glr/Makefile b/examples/c/glr/Makefile
index 6221ca9ea..a56edb1f4 100644
--- a/examples/c/glr/Makefile
+++ b/examples/c/glr/Makefile
@@ -1,7 +1,7 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
-BASE = calc
+BASE = c++-types
BISON = bison
XSLTPROC = xsltproc
@@ -14,7 +14,7 @@ $(BASE): $(BASE).o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
- @echo "Type arithmetic expressions. Quit with ctrl-d."
+ @echo "Type C++ declarations or expressions. Quit with ctrl-d."
./$<
html: $(BASE).html