examples: add a simple Flex+Bison example in C

Suggested by Askar Safin.
http://lists.gnu.org/archive/html/bug-bison/2018-12/msg00003.html

* examples/c/lexcalc/Makefile, examples/c/lexcalc/README.md,
* examples/c/lexcalc/lexcalc.test, examples/c/lexcalc/local.mk,
* examples/c/lexcalc/parse.y, examples/c/lexcalc/scan.l:
New.
This commit is contained in:
Akim Demaille
2018-12-09 14:19:00 +01:00
parent 4cbdcaa572
commit d657da9fb4
10 changed files with 270 additions and 6 deletions

View File

@@ -0,0 +1,34 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = lexcalc
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$@ $<
scan.o: parse.h
lexcalc: parse.o scan.o
$(CC) $(CFLAGS) -o $@ $^
run: $(BASE)
@echo "Type arithmetic expressions. Quit with ctrl-d."
./$<
html: parse.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)

View File

@@ -0,0 +1,28 @@
# lexcalc - calculator with Flex and Bison
This directory contains lexcalc, the traditional example of using Flex and
Bison to build a simple calculator.
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018 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/>.
--->

View File

@@ -0,0 +1,27 @@
#! /bin/sh
# Copyright (C) 2018 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

View File

@@ -0,0 +1,32 @@
## Copyright (C) 2018 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/>.
lexcalcdir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
check_PROGRAMS += %D%/lexcalc
TESTS += %D%/lexcalc.test
EXTRA_DIST += %D%/lexcalc.test
%C%_lexcalc_SOURCES = %D%/parse.y %D%/parse.h %D%/scan.l
# Don't use gnulib's system headers.
%C%_lexcalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
dist_lexcalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
CLEANFILES += %D%/lexcalc %D%/*.o %D%/parse.c %D%/scan.c
CLEANDIRS += %D%/*.dSYM

View File

@@ -0,0 +1,95 @@
// Prologue (directives).
%expect 0
// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
// Tell Flex the expected prototype of yylex.
#define YY_DECL \
enum yytokentype yylex (YYSTYPE* yylval, int *nerrs)
YY_DECL;
void yyerror (int *nerrs, const char *msg);
}
// Emitted on top of the implementation file.
%code top
{
#include <stdio.h> /* printf. */
#include <stdlib.h> /* getenv. */
}
%define api.pure full
%define api.token.prefix {TOK_}
%define api.value.type union
%define parse.error verbose
%define parse.trace
// Error count, exchanged between main, yyparse and yylex.
%param {int *nerrs}
%token
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
LPAREN "("
RPAREN ")"
EOL "end-of-line"
EOF 0 "end-of-file"
;
%token <int> NUM "number"
%type <int> exp line
%printer { fprintf (yyo, "%d", $$); } <int>
// Precedence (from lowest to highest) and associativity.
%left "+" "-"
%left "*" "/"
%%
// Rules.
input:
%empty
| input line { printf ("%d\n", $line); }
;
line:
exp EOL { $$ = $1; }
| error EOL { yyerrok; }
;
exp:
exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp
{
if ($3 == 0)
{
yyerror (nerrs, "invalid division by zero");
YYERROR;
}
else
$$ = $1 / $3;
}
| "(" exp ")" { $$ = $2; }
| NUM { $$ = $1; }
;
%%
// Epilogue (C code).
void yyerror(int *nerrs, const char *msg)
{
fprintf (stderr, "%s\n", msg);
++nerrs;
}
int main (void)
{
int nerrs = 0;
// Enable parser runtime debugging.
if (!!getenv ("YYDEBUG"))
yydebug = 1;
yyparse (&nerrs);
// Exit on failure if there were errors.
return !!nerrs;
}

43
examples/c/lexcalc/scan.l Normal file
View File

@@ -0,0 +1,43 @@
/* Prologue (directives). -*- C++ -*- */
/* Disable Flex features we don't need, to avoid warnings. */
%option nodefault noinput nounput noyywrap
%{
#include <limits.h> /* INT_MIN */
#include <stdlib.h> /* strtol */
#include "parse.h"
%}
%%
/* Rules. */
"+" return TOK_PLUS;
"-" return TOK_MINUS;
"*" return TOK_STAR;
"/" return TOK_SLASH;
"(" return TOK_LPAREN;
")" return TOK_RPAREN;
/* Scan an integer. */
[0-9]+ {
errno = 0;
long n = strtol (yytext, NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
yyerror (nerrs, "integer is out of range");
yylval->TOK_NUM = (int) n;
return TOK_NUM;
}
/* Ignore white spaces. */
[ \t]+ continue;
"\n" return TOK_EOL;
. yyerror (nerrs, "syntax error, invalid character");
<<EOF>> return TOK_EOF;
%%
/* Epilogue (C code). */