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