bison-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

examples: add an example of a push parser


From: Akim Demaille
Subject: examples: add an example of a push parser
Date: Mon, 27 Jan 2020 06:33:24 +0100

commit 7bfff37f01e5e4a625e31ef624b9f120d3d5e199
Author: Akim Demaille <address@hidden>
Date:   Fri Jan 24 08:33:20 2020 +0100

    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.

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]