summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--examples/c/README.md16
-rw-r--r--examples/c/bistromathic/Makefile35
-rw-r--r--examples/c/bistromathic/README.md32
-rwxr-xr-xexamples/c/bistromathic/bistromathic.test48
-rw-r--r--examples/c/bistromathic/local.mk36
-rw-r--r--examples/c/bistromathic/parse.y225
-rw-r--r--examples/c/bistromathic/scan.l61
-rw-r--r--examples/c/local.mk1
-rwxr-xr-xexamples/test4
10 files changed, 455 insertions, 5 deletions
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