mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-20 17:53:02 +00:00
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.
This commit is contained in:
@@ -37,6 +37,17 @@ recursively. To demonstrate this feature, expressions in parentheses are
|
|||||||
tokenized as strings, and then recursively parsed from the parser. So
|
tokenized as strings, and then recursively parsed from the parser. So
|
||||||
`(((1)+(2))*((3)+(4)))` uses eight parsers, with a depth of four.
|
`(((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.
|
||||||
|
|
||||||
|
|
||||||
<!---
|
<!---
|
||||||
|
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ dist_c_DATA = %D%/README.md
|
|||||||
include %D%/calc/local.mk
|
include %D%/calc/local.mk
|
||||||
include %D%/lexcalc/local.mk
|
include %D%/lexcalc/local.mk
|
||||||
include %D%/mfcalc/local.mk
|
include %D%/mfcalc/local.mk
|
||||||
|
include %D%/pushcalc/local.mk
|
||||||
include %D%/reccalc/local.mk
|
include %D%/reccalc/local.mk
|
||||||
include %D%/rpcalc/local.mk
|
include %D%/rpcalc/local.mk
|
||||||
|
|||||||
28
examples/c/pushcalc/Makefile
Normal file
28
examples/c/pushcalc/Makefile
Normal file
@@ -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)
|
||||||
39
examples/c/pushcalc/README.md
Normal file
39
examples/c/pushcalc/README.md
Normal file
@@ -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/>.
|
||||||
|
--->
|
||||||
42
examples/c/pushcalc/calc.test
Normal file
42
examples/c/pushcalc/calc.test
Normal file
@@ -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 '('"
|
||||||
114
examples/c/pushcalc/calc.y
Normal file
114
examples/c/pushcalc/calc.y
Normal file
@@ -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;
|
||||||
|
}
|
||||||
33
examples/c/pushcalc/local.mk
Normal file
33
examples/c/pushcalc/local.mk
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user