summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorAkim Demaille <akim.demaille@gmail.com>2020-01-24 08:33:20 +0100
committerAkim Demaille <akim.demaille@gmail.com>2020-01-26 14:02:08 +0100
commit7bfff37f01e5e4a625e31ef624b9f120d3d5e199 (patch)
treecf0450cf7d793d9e79ef8b220fbcbf7ad2938f77 /examples
parent26fba6fc94ff252ce79d478437cb0e60d3544a30 (diff)
downloadbison-7bfff37f01e5e4a625e31ef624b9f120d3d5e199.tar.gz
examples: add an example of a push parser
Add an example to demonstrate the use of push parser. I'm pleasantly surprised: parse.error=detailed works like a charm with push parsers. * examples/c/local.mk, examples/c/pushcalc/Makefile * examples/c/pushcalc/README.md, examples/c/pushcalc/calc.test, * examples/c/pushcalc/calc.y, examples/c/pushcalc/local.mk: New.
Diffstat (limited to 'examples')
-rw-r--r--examples/c/README.md11
-rw-r--r--examples/c/local.mk1
-rw-r--r--examples/c/pushcalc/Makefile28
-rw-r--r--examples/c/pushcalc/README.md39
-rw-r--r--examples/c/pushcalc/calc.test42
-rw-r--r--examples/c/pushcalc/calc.y114
-rw-r--r--examples/c/pushcalc/local.mk33
7 files changed, 268 insertions, 0 deletions
diff --git a/examples/c/README.md b/examples/c/README.md
index 55fd600e..5225afab 100644
--- a/examples/c/README.md
+++ b/examples/c/README.md
@@ -37,6 +37,17 @@ recursively. To demonstrate this feature, expressions in parentheses are
tokenized as strings, and then recursively parsed from the parser. So
`(((1)+(2))*((3)+(4)))` uses eight parsers, with a depth of four.
+## pushcalc - calculator implemented with a push parser
+All the previous examples are so called "pull parsers": the user invokes the
+parser once, which repeatedly calls the scanner until the input is drained.
+
+This example demonstrates the "push parsers": the user calls the scanner to
+fetch the next token, passes it to the parser, and repeats the operation
+until the input is drained.
+
+This example is a straightforward conversion of the 'calc' example to the
+push-parser model.
+
<!---
diff --git a/examples/c/local.mk b/examples/c/local.mk
index 0c8054d0..9ed47a55 100644
--- a/examples/c/local.mk
+++ b/examples/c/local.mk
@@ -19,5 +19,6 @@ dist_c_DATA = %D%/README.md
include %D%/calc/local.mk
include %D%/lexcalc/local.mk
include %D%/mfcalc/local.mk
+include %D%/pushcalc/local.mk
include %D%/reccalc/local.mk
include %D%/rpcalc/local.mk
diff --git a/examples/c/pushcalc/Makefile b/examples/c/pushcalc/Makefile
new file mode 100644
index 00000000..6221ca9e
--- /dev/null
+++ b/examples/c/pushcalc/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 = calc
+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 arithmetic 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/pushcalc/README.md b/examples/c/pushcalc/README.md
new file mode 100644
index 00000000..557d7fa6
--- /dev/null
+++ b/examples/c/pushcalc/README.md
@@ -0,0 +1,39 @@
+# pushcalc - push parser with Bison
+
+This directory contains pushcalc, the traditional calculator, implemented as
+a push parser.
+
+Traditionally Bison is used to create so called "pull parsers": the user
+invokes the parser once, which repeatedly calls (pulls) the scanner until
+the input is drained.
+
+This example demonstrates the "push parsers": the user calls scanner to
+fetch the next token, passes (pushes) it to the parser, and repeats the
+operation until the input is drained.
+
+This example is a straightforward conversion of the 'calc' example to the
+push-parser model.
+
+<!---
+Local Variables:
+fill-column: 76
+ispell-dictionary: "american"
+End:
+
+Copyright (C) 2020 Free Software Foundation, Inc.
+
+This file is part of Bison, the GNU Compiler Compiler.
+
+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/>.
+--->
diff --git a/examples/c/pushcalc/calc.test b/examples/c/pushcalc/calc.test
new file mode 100644
index 00000000..2241e104
--- /dev/null
+++ b/examples/c/pushcalc/calc.test
@@ -0,0 +1,42 @@
+#! /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 <http://www.gnu.org/licenses/>.
+
+cat >input <<EOF
+1+2*3
+EOF
+run 0 7
+
+cat >input <<EOF
+1 - 2 - 3
+EOF
+run 0 -4
+
+cat >input <<EOF
+8 / 2 / 2
+EOF
+run 0 2
+
+cat >input <<EOF
+(1+2) * 3
+EOF
+run 0 9
+run -noerr 0 9 -p
+
+cat >input <<EOF
+1++2
+EOF
+run 0 "err: syntax error, unexpected '+', expecting number or '('"
diff --git a/examples/c/pushcalc/calc.y b/examples/c/pushcalc/calc.y
new file mode 100644
index 00000000..7b0b9996
--- /dev/null
+++ b/examples/c/pushcalc/calc.y
@@ -0,0 +1,114 @@
+%code top {
+ #include <ctype.h> /* isdigit. */
+ #include <stdbool.h>
+ #include <stdio.h> /* For printf, etc. */
+ #include <string.h> /* strcmp. */
+}
+
+%code {
+ int yylex (YYSTYPE *yylval);
+ void yyerror (char const *);
+}
+
+%define api.header.include {"calc.h"}
+
+/* Generate YYSTYPE from the types used in %token and %type. */
+%define api.value.type union
+%token <double> NUM "number"
+%type <double> expr term fact
+
+/* Don't share global variables between the scanner and the parser. */
+%define api.pure full
+/* Generate a push parser. */
+%define api.push-pull push
+
+/* Nice error messages with details. */
+%define parse.error detailed
+
+/* Generate the parser description file (calc.output). */
+%verbose
+
+ /* Enable run-time traces (yydebug). */
+%define parse.trace
+
+/* Formatting semantic values in debug traces. */
+%printer { fprintf (yyo, "%g", $$); } <double>;
+
+%% /* The grammar follows. */
+input:
+ %empty
+| input line
+;
+
+line:
+ '\n'
+| expr '\n' { printf ("%.10g\n", $1); }
+| error '\n' { yyerrok; }
+;
+
+expr:
+ expr '+' term { $$ = $1 + $3; }
+| expr '-' term { $$ = $1 - $3; }
+| term
+;
+
+term:
+ term '*' fact { $$ = $1 * $3; }
+| term '/' fact { $$ = $1 / $3; }
+| fact
+;
+
+fact:
+ "number"
+| '(' expr ')' { $$ = $expr; }
+;
+
+%%
+
+int
+yylex (YYSTYPE *yylval)
+{
+ int c;
+
+ /* Ignore white space, get first nonwhite character. */
+ while ((c = getchar ()) == ' ' || c == '\t')
+ continue;
+
+ if (c == EOF)
+ return 0;
+
+ /* Char starts a number => parse the number. */
+ if (c == '.' || isdigit (c))
+ {
+ ungetc (c, stdin);
+ scanf ("%lf", &yylval->NUM);
+ return NUM;
+ }
+
+ /* Any other character is a token by itself. */
+ return c;
+}
+
+/* Called by yyparse on error. */
+void
+yyerror (char const *s)
+{
+ fprintf (stderr, "%s\n", s);
+}
+
+int
+main (int argc, char const* argv[])
+{
+ /* Enable parse traces on option -p. */
+ for (int i = 1; i < argc; ++i)
+ if (!strcmp (argv[i], "-p"))
+ yydebug = 1;
+ int status;
+ yypstate *ps = yypstate_new ();
+ do {
+ YYSTYPE lval;
+ status = yypush_parse (ps, yylex (&lval), &lval);
+ } while (status == YYPUSH_MORE);
+ yypstate_delete (ps);
+ return status;
+}
diff --git a/examples/c/pushcalc/local.mk b/examples/c/pushcalc/local.mk
new file mode 100644
index 00000000..9b6b19d6
--- /dev/null
+++ b/examples/c/pushcalc/local.mk
@@ -0,0 +1,33 @@
+## 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 <http://www.gnu.org/licenses/>.
+
+pushcalcdir = $(docdir)/%D%
+
+## ------ ##
+## Calc. ##
+## ------ ##
+
+check_PROGRAMS += %D%/calc
+TESTS += %D%/calc.test
+EXTRA_DIST += %D%/calc.test
+nodist_%C%_calc_SOURCES = %D%/calc.y
+%D%/calc.c: $(dependencies)
+
+# Don't use gnulib's system headers.
+%C%_calc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
+
+dist_pushcalc_DATA = %D%/calc.y %D%/Makefile %D%/README.md
+CLEANFILES += %D%/calc.[ch] %D%/calc.output
+CLEANDIRS += %D%/*.dSYM