bison-patches
[Top][All Lists]
Advanced

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

examples: add a complete example with all the bells and whistles


From: Akim Demaille
Subject: examples: add a complete example with all the bells and whistles
Date: Mon, 27 Jan 2020 06:44:02 +0100

commit f374310119891f9b7860c56e8141bc8913683197
Author: Akim Demaille <address@hidden>
Date:   Sat Jan 25 19:38:39 2020 +0100

    examples: add a complete example with all the bells and whistles
    
    * examples/c/bistromathic/Makefile,
    * examples/c/bistromathic/README.md,
    * examples/c/bistromathic/bistromathic.test,
    * examples/c/bistromathic/local.mk,
    * examples/c/bistromathic/parse.y,
    * examples/c/bistromathic/scan.l:
    New.
    
    * Makefile.am (AM_YFLAGS_WITH_LINES): Add -Wdangling-alias.
    * examples/test: Make failure errors easier to read.

diff --git a/Makefile.am b/Makefile.am
index e357db17..677f9b01 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -42,7 +42,7 @@ dependencies = $(BISON_IN) $(src_bison_SOURCES) 
$(dist_skeletons_DATA)
 BISON = $(top_builddir)/tests/bison
 BISON_IN = $(top_srcdir)/tests/bison.in
 YACC = $(BISON) -o y.tab.c
-AM_YFLAGS_WITH_LINES = --defines -Werror -Wall --report=all
+AM_YFLAGS_WITH_LINES = --defines -Werror -Wall,dangling-alias --report=all
 AM_YFLAGS = $(AM_YFLAGS_WITH_LINES) --no-lines
 
 # Initialization before completion by local.mk's.
diff --git a/examples/c/README.md b/examples/c/README.md
index 5225afab..26110cf1 100644
--- a/examples/c/README.md
+++ b/examples/c/README.md
@@ -48,6 +48,14 @@ until the input is drained.
 This example is a straightforward conversion of the 'calc' example to the
 push-parser model.
 
+## bistromathic - all the bells and whistles
+This example demonstrates the best practices when using Bison.
+- Its interface is pure.
+- It uses a custom syntax error with location tracking, lookahead correction
+  and token internationalization.
+- It enables debug trace support with formatting of semantic values.
+
+It also uses Flex to generate the scanner.
 
 <!---
 
@@ -65,6 +73,8 @@ Invariant Sections, with no Front-Cover Texts, and with no 
Back-Cover
 Texts.  A copy of the license is included in the "GNU Free
 Documentation License" file as part of this distribution.
 
-# LocalWords:  mfcalc calc parsers yy rpcalc lexcalc redux reccalc ispell
-# LocalWords:  reentrant tokenized american postfix
---->
+LocalWords:  mfcalc calc parsers yy rpcalc lexcalc redux reccalc ispell
+LocalWords:  reentrant tokenized american postfix pushcalc bistromathic
+LocalWords:  lookahead
+
+-->
diff --git a/examples/c/bistromathic/Makefile b/examples/c/bistromathic/Makefile
new file mode 100644
index 00000000..359d0ac5
--- /dev/null
+++ b/examples/c/bistromathic/Makefile
@@ -0,0 +1,35 @@
+# This Makefile is designed to be simple and readable.  It does not
+# aim at portability.  It requires GNU Make.
+
+BASE = bistromathic
+BISON = bison
+FLEX = flex
+XSLTPROC = xsltproc
+
+all: $(BASE)
+
+%.c %.h %.xml %.gv: %.y
+       $(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
+
+%.c: %.l
+       $(FLEX) $(FLEXFLAGS) -o$*.c $<
+
+scan.o: parse.h
+$(BASE): parse.o scan.o
+       $(CC) $(CFLAGS) -o $@ $^
+
+run: $(BASE)
+       @echo "Type bistromathic expressions.  Quit with ctrl-d."
+       ./$<
+
+html: $(BASE).html
+%.html: %.xml
+       $(XSLTPROC) $(XSLTPROCFLAGS) -o $@ $$($(BISON) 
--print-datadir)/xslt/xml2xhtml.xsl $<
+
+CLEANFILES =                                           \
+  $(BASE) *.o                                          \
+  parse.[ch] parse.output parse.xml parse.html parse.gv        \
+  scan.c
+
+clean:
+       rm -f $(CLEANFILES)
diff --git a/examples/c/bistromathic/README.md 
b/examples/c/bistromathic/README.md
new file mode 100644
index 00000000..764866e3
--- /dev/null
+++ b/examples/c/bistromathic/README.md
@@ -0,0 +1,32 @@
+# bistromathic - all the bells and whistles
+This example demonstrates the best practices when using Bison.
+- Its interface is pure.
+- It uses a custom syntax error with location tracking, lookahead correction
+  and token internationalization.
+- It enables debug trace support with formatting of semantic values.
+
+It also uses Flex to generate the scanner.
+
+<!---
+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/bistromathic/bistromathic.test 
b/examples/c/bistromathic/bistromathic.test
new file mode 100755
index 00000000..4d19c98b
--- /dev/null
+++ b/examples/c/bistromathic/bistromathic.test
@@ -0,0 +1,48 @@
+#! /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 9
+run -noerr 0 9 -p
+
+cat >input <<EOF
+a = 256
+sqrt (a)
+EOF
+run 0 '256
+16'
+
+cat >input <<EOF
+a = .16
+b = 10 ^ 2
+sqrt (a * b)
+EOF
+run 0 '0.16
+100
+4'
+
+cat >input <<EOF
+*
+EOF
+run 0 "err: 1.1: syntax error expected end of file or - or ( or end of line or 
double precision number or function or variable before *"
diff --git a/examples/c/bistromathic/local.mk b/examples/c/bistromathic/local.mk
new file mode 100644
index 00000000..78f2c937
--- /dev/null
+++ b/examples/c/bistromathic/local.mk
@@ -0,0 +1,36 @@
+## 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/>.
+
+bistromathicdir = $(docdir)/%D%
+
+## --------------- ##
+## Bistromathics.  ##
+## --------------- ##
+
+if FLEX_WORKS
+  check_PROGRAMS += %D%/bistromathic
+  TESTS += %D%/bistromathic.test
+  EXTRA_DIST += %D%/bistromathic.test
+  nodist_%C%_bistromathic_SOURCES = %D%/parse.y %D%/parse.h %D%/scan.l
+  %D%/calc.c: $(dependencies)
+
+  # Don't use gnulib's system headers.
+  %C%_bistromathic_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
+  %C%_bistromathic_LDADD = -lm
+endif FLEX_WORKS
+
+dist_bistromathic_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
+CLEANFILES += %D%/parse.[ch] %D%/scan.c %D%/parse.output
+CLEANDIRS += %D%/*.dSYM
diff --git a/examples/c/bistromathic/parse.y b/examples/c/bistromathic/parse.y
new file mode 100644
index 00000000..cac38f32
--- /dev/null
+++ b/examples/c/bistromathic/parse.y
@@ -0,0 +1,225 @@
+%require "3.6"
+
+%code top {
+  #include <ctype.h>  // isdigit
+  #include <math.h>   // cos, sin, etc.
+  #include <stddef.h> // ptrdiff_t
+  #include <stdio.h>  // printf
+  #include <string.h> // strcmp
+}
+
+%code requires {
+  // Function type.
+  typedef double (func_t) (double);
+
+  // Data type for links in the chain of symbols.
+  typedef struct symrec symrec;
+  struct symrec
+  {
+    char *name;  // name of symbol
+    int type;    // type of symbol: either VAR or FUN
+    union
+    {
+      double var;    // value of a VAR
+      func_t *fun;   // value of a FUN
+    } value;
+    symrec *next;  // link field
+  };
+
+  symrec *putsym (char const *name, int sym_type);
+  symrec *getsym (char const *name);
+}
+
+%code provides {
+  #define YY_DECL \
+  int yylex (YYSTYPE *yylval, YYLTYPE *yylloc)
+  YY_DECL;
+
+  void yyerror (YYLTYPE *yylloc, char const *);
+}
+
+%code {
+#define N_
+#define _
+}
+
+// Don't share global variables between the scanner and the parser.
+%define api.pure full
+
+// To avoid name clashes (e.g., with C's EOF) prefix token definitions
+// with TOK_ (e.g., TOK_EOF).
+%define api.token.prefix {TOK_}
+
+// Customized syntax error messages (see yyreport_syntax_error).
+%define parse.error custom
+
+// with locations.
+%locations
+
+// and acurate list of expected tokens.
+%define parse.lac full
+
+// Generate the parser description file (calc.output).
+%verbose
+
+// Generate YYSTYPE from the types assigned to symbols.
+%define api.value.type union
+%token
+    PLUS   "+"
+    MINUS  "-"
+    STAR   "*"
+    SLASH  "/"
+    CARET  "^"
+    LPAREN "("
+    RPAREN ")"
+    EQUAL  "="
+    EOL    _("end of line")
+    EOF 0  _("end of file")
+  <double>
+    NUM _("double precision number")
+  <symrec*>
+    FUN _("function")
+    VAR _("variable")
+
+%nterm <double>  exp
+
+// Enable run-time traces (yydebug).
+%define parse.trace
+
+// Formatting semantic values in debug traces.
+%printer { fprintf (yyo, "%s", $$->name); } VAR;
+%printer { fprintf (yyo, "%s()", $$->name); } FUN;
+%printer { fprintf (yyo, "%g", $$); } <double>;
+
+
+// Precedence (from lowest to highest) and associativity.
+%precedence "="
+%left "+" "-"
+%left "*" "/"
+%precedence NEG // negation--unary minus
+%right "^"      // exponentiation
+
+%% // The grammar follows.
+input:
+  %empty
+| input line
+;
+
+line:
+  EOL
+| exp EOL   { printf ("%.10g\n", $1); }
+| error EOL { yyerrok;                }
+;
+
+exp:
+  NUM
+| VAR              { $$ = $1->value.var;              }
+| VAR "=" exp      { $$ = $3; $1->value.var = $3;     }
+| FUN "(" exp ")"  { $$ = $1->value.fun ($3);         }
+| exp "+" exp      { $$ = $1 + $3; }
+| exp "-" exp      { $$ = $1 - $3; }
+| exp "*" exp      { $$ = $1 * $3; }
+| exp "/" exp
+  {
+    if ($3 == 0)
+      {
+        yyerror (&@$, "division by zero");
+        YYERROR;
+      }
+    else
+      $$ = $1 / $3;
+  }
+| "-" exp  %prec NEG { $$ = -$2; }
+| exp "^" exp        { $$ = pow ($1, $3); }
+| "(" exp ")"        { $$ = $2; }
+;
+
+// End of grammar.
+%%
+
+struct init
+{
+  char const *name;
+  func_t *fun;
+};
+
+static struct init const funs[] =
+{
+  { "atan", atan },
+  { "cos",  cos  },
+  { "exp",  exp  },
+  { "ln",   log  },
+  { "sin",  sin  },
+  { "sqrt", sqrt },
+  { 0, 0 },
+};
+
+// The symbol table: a chain of 'struct symrec'.
+static symrec *sym_table;
+
+// Put functions in table.
+static void
+init_table (void)
+{
+  for (int i = 0; funs[i].name; i++)
+    {
+      symrec *ptr = putsym (funs[i].name, TOK_FUN);
+      ptr->value.fun = funs[i].fun;
+    }
+}
+
+symrec *
+putsym (char const *name, int sym_type)
+{
+  symrec *res = (symrec *) malloc (sizeof (symrec));
+  res->name = strdup (name);
+  res->type = sym_type;
+  res->value.var = 0; // Set value to 0 even if fun.
+  res->next = sym_table;
+  sym_table = res;
+  return res;
+}
+
+symrec *
+getsym (char const *name)
+{
+  for (symrec *p = sym_table; p; p = p->next)
+    if (strcmp (p->name, name) == 0)
+      return p;
+  return NULL;
+}
+
+int
+yyreport_syntax_error (const yyparse_context_t *ctx)
+{
+  enum { ARGMAX = 10 };
+  int arg[ARGMAX];
+  int n = yysyntax_error_arguments (ctx, arg, ARGMAX);
+  if (n == -2)
+    return 2;
+  YY_LOCATION_PRINT (stderr, *yyparse_context_location (ctx));
+  fprintf (stderr, ": syntax error");
+  for (int i = 1; i < n; ++i)
+    fprintf (stderr, " %s %s",
+             i == 1 ? "expected" : "or", yysymbol_name (arg[i]));
+  if (n)
+    fprintf (stderr, " before %s", yysymbol_name (arg[0]));
+  fprintf (stderr, "\n");
+  return 0;
+}
+
+// Called by yyparse on error.
+void yyerror (YYLTYPE *loc, char const *s)
+{
+  YY_LOCATION_PRINT (stderr, *loc);
+  fprintf (stderr, ": %s\n", s);
+}
+
+int main (int argc, char const* argv[])
+{
+  // Enable parse traces on option -p.
+  if (argc == 2 && strcmp(argv[1], "-p") == 0)
+    yydebug = 1;
+  init_table ();
+  return yyparse ();
+}
diff --git a/examples/c/bistromathic/scan.l b/examples/c/bistromathic/scan.l
new file mode 100644
index 00000000..c5f57c1e
--- /dev/null
+++ b/examples/c/bistromathic/scan.l
@@ -0,0 +1,61 @@
+/* Prologue (directives).   -*- C -*- */
+
+/* Disable Flex features we don't need, to avoid warnings. */
+%option nodefault noinput nounput noyywrap
+
+%{
+#include <errno.h> /* errno, ERANGE */
+#include <limits.h> /* INT_MIN */
+#include <stdlib.h> /* strtol */
+
+#include "parse.h"
+
+  // Each time a rule is matched, advance the end cursor/position.
+#define YY_USER_ACTION                          \
+  yylloc->last_column += yyleng;
+%}
+
+%%
+%{
+  // Each time yylex is called, move the head position to the end one.
+  yylloc->first_line = yylloc->last_line;
+  yylloc->first_column = yylloc->last_column;
+%}
+ /* Rules.  */
+
+"+"      return TOK_PLUS;
+"-"      return TOK_MINUS;
+"*"      return TOK_STAR;
+"/"      return TOK_SLASH;
+"^"      return TOK_CARET;
+
+"("      return TOK_LPAREN;
+")"      return TOK_RPAREN;
+
+"="      return TOK_EQUAL;
+
+ /* Scan an identifier.  */
+[a-z]+   {
+  symrec *s = getsym (yytext);
+  if (!s)
+    s = putsym (yytext, TOK_VAR);
+  yylval->TOK_VAR = s;
+  return s->type;
+}
+
+ /* Scan a double precision number.  */
+[0-9]+(\.[0-9]*)?|(\.[0-9]+)   {
+  sscanf (yytext, "%lf", &yylval->TOK_NUM);
+  return TOK_NUM;
+}
+
+"\n"     yylloc->last_line++; yylloc->last_column = 1; return TOK_EOL;
+
+ /* Ignore white spaces. */
+[ \t]+   continue;
+
+<<EOF>>  return TOK_EOF;
+
+.        yyerror (yylloc, "syntax error, invalid character");
+%%
+/* Epilogue (C code). */
diff --git a/examples/c/local.mk b/examples/c/local.mk
index 9ed47a55..860d6994 100644
--- a/examples/c/local.mk
+++ b/examples/c/local.mk
@@ -16,6 +16,7 @@
 cdir = $(docdir)/%D%
 dist_c_DATA = %D%/README.md
 
+include %D%/bistromathic/local.mk
 include %D%/calc/local.mk
 include %D%/lexcalc/local.mk
 include %D%/mfcalc/local.mk
diff --git a/examples/test b/examples/test
index 79b8c994..d726a517 100755
--- a/examples/test
+++ b/examples/test
@@ -89,7 +89,9 @@ run ()
     if test "$out_eff" = "$out_exp"; then
       echo "$me: PASS: $number"
     else
-      echo "$me: FAIL: $number (expected output: $out_exp, effective: 
$out_eff)"
+      echo "$me: FAIL: $number"
+      echo "$me: expected output:  $out_exp"
+      echo "$me: effective output: $out_eff"
       cat err_eff
       exit=false
     fi




reply via email to

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