diff options
author | Akim Demaille <akim.demaille@gmail.com> | 2020-01-24 08:33:20 +0100 |
---|---|---|
committer | Akim Demaille <akim.demaille@gmail.com> | 2020-01-26 14:02:08 +0100 |
commit | 7bfff37f01e5e4a625e31ef624b9f120d3d5e199 (patch) | |
tree | cf0450cf7d793d9e79ef8b220fbcbf7ad2938f77 /examples | |
parent | 26fba6fc94ff252ce79d478437cb0e60d3544a30 (diff) | |
download | bison-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.md | 11 | ||||
-rw-r--r-- | examples/c/local.mk | 1 | ||||
-rw-r--r-- | examples/c/pushcalc/Makefile | 28 | ||||
-rw-r--r-- | examples/c/pushcalc/README.md | 39 | ||||
-rw-r--r-- | examples/c/pushcalc/calc.test | 42 | ||||
-rw-r--r-- | examples/c/pushcalc/calc.y | 114 | ||||
-rw-r--r-- | examples/c/pushcalc/local.mk | 33 |
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 |