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/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 . + +cat >input <input <input <input <input < /* isdigit. */ + #include + #include /* For printf, etc. */ + #include /* 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 NUM "number" +%type 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", $$); } ; + +%% /* 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 . + +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