examples: add an example with a reentrant parser in Flex+Bison

Suggested by Eric S. Raymond.
https://lists.gnu.org/archive/html/bison-patches/2019-02/msg00066.html

* examples/c/reentrant-calc/Makefile, examples/c/reentrant-calc/README.md,
* examples/c/reentrant-calc/parse.y, examples/c/reentrant-calc/scan.l
* examples/c/reentrant-calc/lexcalc.test,
* examples/c/reentrant-calc/local.mk:
New.
This commit is contained in:
Akim Demaille
2019-02-16 13:13:30 +01:00
parent 0adda755a2
commit 6289d673a0
15 changed files with 845 additions and 3 deletions

1
THANKS
View File

@@ -56,6 +56,7 @@ Didier Godefroy dg@ulysium.net
Efi Fogel efifogel@gmail.com Efi Fogel efifogel@gmail.com
Enrico Scholz enrico.scholz@informatik.tu-chemnitz.de Enrico Scholz enrico.scholz@informatik.tu-chemnitz.de
Eric Blake ebb9@byu.net Eric Blake ebb9@byu.net
Eric S. Raymond esr@thyrsus.com
Étienne Renault renault@lrde.epita.fr Étienne Renault renault@lrde.epita.fr
Evgeny Stambulchik fnevgeny@plasma-gate.weizmann.ac.il Evgeny Stambulchik fnevgeny@plasma-gate.weizmann.ac.il
Fabrice Bauzac noon@cote-dazur.com Fabrice Bauzac noon@cote-dazur.com

View File

@@ -2,7 +2,7 @@
This directory contains simple examples of Bison grammar files in C. This directory contains simple examples of Bison grammar files in C.
Most of them come from the documentation, which should be installed together Some of them come from the documentation, which should be installed together
with Bison. The URLs are provided for convenience. with Bison. The URLs are provided for convenience.
## rpcalc - Reverse Polish Notation Calculator ## rpcalc - Reverse Polish Notation Calculator
@@ -16,7 +16,7 @@ https://www.gnu.org/software/bison/manual/html_node/RPN-Calc.html
## calc - Simple Calculator ## calc - Simple Calculator
This example is slightly more complex than rpcalc: it features infix This example is slightly more complex than rpcalc: it features infix
operators (`1 + 2`, instead of `1 2 +` in rpcalc), but it does so using a operators (`1 + 2`, instead of `1 2 +` in rpcalc), but it does so using a
unambiguous grammar of the arithmetics instead of using precedence unambiguous grammar of the arithmetic instead of using precedence
directives (%left, etc.). directives (%left, etc.).
## mfcalc - Multi-Function Calculator ## mfcalc - Multi-Function Calculator
@@ -30,6 +30,13 @@ https://www.gnu.org/software/bison/manual/html_node/Multi_002dfunction-Calc.html
## lexcalc - calculator with Flex and Bison ## lexcalc - calculator with Flex and Bison
The calculator, redux. This time using a scanner generated by Flex. The calculator, redux. This time using a scanner generated by Flex.
## reccalc - recursive calculator with Flex and Bison
The example builds on top of the previous one to provide a reentrant parser.
Such parsers can be called concurrently in different threads, or even
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.
<!--- <!---
@@ -47,5 +54,6 @@ Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
Texts. A copy of the license is included in the "GNU Free Texts. A copy of the license is included in the "GNU Free
Documentation License" file as part of this distribution. Documentation License" file as part of this distribution.
# LocalWords: mfcalc calc parsers yy # LocalWords: mfcalc calc parsers yy rpcalc lexcalc redux reccalc ispell
# LocalWords: reentrant tokenized american postfix
---> --->

View File

@@ -19,4 +19,5 @@ dist_c_DATA = %D%/README.md
include %D%/calc/local.mk include %D%/calc/local.mk
include %D%/lexcalc/local.mk include %D%/lexcalc/local.mk
include %D%/mfcalc/local.mk include %D%/mfcalc/local.mk
include %D%/reccalc/local.mk
include %D%/rpcalc/local.mk include %D%/rpcalc/local.mk

View File

@@ -0,0 +1,35 @@
# This Makefile is designed to be simple and readable. It does not
# aim at portability. It requires GNU Make.
BASE = reccalc
BISON = bison
FLEX = flex
XSLTPROC = xsltproc
all: $(BASE)
%.c %.h %.xml %.gv: %.y
$(BISON) $(BISONFLAGS) --defines --xml --graph=$*.gv -o $*.c $<
%.c %.h: %.l
$(FLEX) $(FLEXFLAGS) -o$*.c --header-file=$*.h $<
scan.o: parse.h
parse.o: scan.h
$(BASE): 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.[ch]
clean:
rm -f $(CLEANFILES)

View File

@@ -0,0 +1,45 @@
# reccalc - recursive calculator with Flex and Bison
In this example the generated parser is pure and reentrant: it can be used
concurrently in different threads, or recursively. As a proof of this
reentrancy, expressions in parenthesis are tokenized as strings, and then
recursively parsed from the parser:
```
exp: STR
{
result r = parse_string ($1);
free ($1);
if (r.nerrs)
{
res->nerrs += r.nerrs;
YYERROR;
}
else
$$ = r.value;
}
```
<!---
Local Variables:
fill-column: 76
ispell-dictionary: "american"
End:
Copyright (C) 2018-2019 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,53 @@
## Copyright (C) 2019 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/>.
reccalcdir = $(docdir)/%D%
## ------ ##
## Calc. ##
## ------ ##
check_PROGRAMS += %D%/reccalc
TESTS += %D%/reccalc.test
EXTRA_DIST += %D%/reccalc.test %D%/scan.l
%C%_reccalc_SOURCES = %D%/parse.y %D%/parse.h
nodist_%C%_reccalc_SOURCES = %D%/scan.h %D%/scan.c
BUILT_SOURCES += $(nodist_%C%_reccalc_SOURCES)
%D%/parse.c: $(dependencies)
# Tell Make that parse.o depends on scan.h, so that scan.h is built
# before parse.o. Obfuscate the name of the target, otherwise
# Automake removes its recipe for parse.o and leaves only our
# additional dependency.
DASH = -
%D%/reccalc$(DASH)parse.o: %D%/scan.h
%D%/scan.c %D%/scan.h: %D%/scan.stamp
@test -f $@ || rm -f %D%/scan.stamp
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) %D%/scan.stamp
%D%/scan.stamp: %D%/scan.l
$(AM_V_LEX)rm -f $@ $@.tmp
$(AM_V_at)$(MKDIR_P) %D%
$(AM_V_at)touch $@.tmp
$(AM_V_at) $(LEX) -o %D%/scan.c --header-file=%D%/scan.h $<
$(AM_V_at)mv $@.tmp $@
# Don't use gnulib's system headers.
%C%_reccalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
dist_reccalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
CLEANFILES += %D%/reccalc %D%/*.o %D%/parse.[ch] %D%/scan.[ch] %D%/*.stamp
CLEANDIRS += %D%/*.dSYM

183
examples/c/reccalc/parse.y Normal file
View File

@@ -0,0 +1,183 @@
// Prologue (directives).
%expect 0
// Emitted in the header file, before the definition of YYSTYPE.
%code requires
{
typedef void* yyscan_t;
typedef struct
{
// Whether to print the intermediate results.
int verbose;
// Value of the last computation.
int value;
// Number of errors.
int nerrs;
} result;
}
// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
// Tell Flex the expected prototype of yylex.
// The scanner argument must be named yyscanner.
#define YY_DECL \
enum yytokentype yylex (YYSTYPE* yylval, yyscan_t yyscanner, result *res)
YY_DECL;
void yyerror (yyscan_t scanner, result *res, const char *msg, ...);
}
// Emitted on top of the implementation file.
%code top
{
#include <stdarg.h> // va_list.
#include <stdio.h> // printf.
#include <stdlib.h> // getenv.
}
%code
{
result parse_string (const char* cp);
result parse (void);
}
%define api.pure full
%define api.token.prefix {TOK_}
%define api.value.type union
%define parse.error verbose
%define parse.trace
%verbose
// Scanner and error count are exchanged between main, yyparse and yylex.
%param {yyscan_t scanner}{result *res}
%token
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
EOL "end-of-line"
EOF 0 "end-of-file"
;
%token <int> NUM "number"
%type <int> exp
%printer { fprintf (yyo, "%d", $$); } <int>
%token <char*> STR "string"
%printer { fprintf (yyo, "\"%s\"", $$); } <char*>
%destructor { free ($$); } <char*>
// Precedence (from lowest to highest) and associativity.
%left "+" "-"
%left "*" "/"
%precedence UNARY
%%
// Rules.
input:
line
| input line
;
line:
exp eol
{
res->value = $exp;
if (res->verbose)
printf ("%d\n", $exp);
}
| error eol
{
yyerrok;
}
;
eol:
EOF
| EOL
;
exp:
NUM { $$ = $1; }
| exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp
{
if ($3 == 0)
{
yyerror (scanner, res, "invalid division by zero");
YYERROR;
}
else
$$ = $1 / $3;
}
| "+" exp %prec UNARY { $$ = + $2; }
| "-" exp %prec UNARY { $$ = - $2; }
| STR
{
result r = parse_string ($1);
free ($1);
if (r.nerrs)
{
res->nerrs += r.nerrs;
YYERROR;
}
else
$$ = r.value;
}
;
%%
// Epilogue (C code).
#include "scan.h"
result
parse (void)
{
yyscan_t scanner;
yylex_init (&scanner);
result res = {1, 0, 0};
yyparse (scanner, &res);
yylex_destroy (scanner);
return res;
}
result
parse_string (const char *str)
{
yyscan_t scanner;
yylex_init (&scanner);
YY_BUFFER_STATE buf = yy_scan_string (str ? str : "", scanner);
result res = {0, 0, 0};
yyparse (scanner, &res);
yy_delete_buffer (buf, scanner);
yylex_destroy (scanner);
return res;
}
void
yyerror (yyscan_t scanner, result *res,
const char *msg, ...)
{
(void) scanner;
va_list args;
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
fputc ('\n', stderr);
res->nerrs += 1;
}
int
main (void)
{
// Possibly enable parser runtime debugging.
yydebug = !!getenv ("YYDEBUG");
result res = parse ();
// Exit on failure if there were errors.
return !!res.nerrs;
}

View File

@@ -0,0 +1,57 @@
#! /bin/sh
# Copyright (C) 2018-2019 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
(((1)+(2))*((3)+(4)))
EOF
run 0 '21'
# Some really deep computation.
# for 4, gives 4 + (3 + (2 + (1 + (- (4 * (4 + 1)) / 2)))).
n=100
for i in $(seq 0 $n)
do
if [ "$i" -eq 0 ]; then
input="- ($n * ($n + 1)) / 2"
else
input="$i + ($input)"
fi
done
echo "$input" > input
run 0 '0'
cat >input <<EOF
() + ()
EOF
run 1 'err: syntax error, unexpected end-of-file, expecting + or - or number or string'
cat >input <<EOF
1 + $
EOF
run 1 'err: syntax error, invalid character: $
err: syntax error, unexpected end-of-line, expecting + or - or number or string'

85
examples/c/reccalc/scan.l Normal file
View File

@@ -0,0 +1,85 @@
/* Prologue (directives). -*- C++ -*- */
/* Disable Flex features we don't need, to avoid warnings. */
%option nodefault noinput nounput noyywrap
%option reentrant
%{
#include <assert.h>
#include <limits.h> /* INT_MIN */
#include <stdlib.h> /* strtol */
#include "parse.h"
%}
%x SC_STRING
%%
%{
int nesting = 0;
char *str = NULL;
int size = 0;
int capacity = 0;
#define STR_APPEND() \
do { \
if (capacity < size + yyleng + 1) \
{ \
do \
capacity = capacity ? 2 * capacity : 128; \
while (capacity < size + yyleng + 1); \
str = realloc (str, capacity); \
} \
strncpy (str + size, yytext, yyleng); \
size += yyleng; \
assert (size < capacity); \
} while (0)
%}
// Rules.
"+" return TOK_PLUS;
"-" return TOK_MINUS;
"*" return TOK_STAR;
"/" return TOK_SLASH;
"(" nesting += 1; BEGIN SC_STRING;
/* Scan an integer. */
[0-9]+ {
errno = 0;
long n = strtol (yytext, NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
yyerror (yyscanner, res, "integer is out of range");
yylval->TOK_NUM = (int) n;
return TOK_NUM;
}
/* Ignore white spaces. */
[ \t]+ continue;
"\n" return TOK_EOL;
. yyerror (yyscanner, res, "syntax error, invalid character: %c", yytext[0]);
<SC_STRING>
{
"("+ nesting += yyleng; STR_APPEND ();
")" {
if (!--nesting)
{
BEGIN INITIAL;
if (str)
str[size] = 0;
yylval->TOK_STR = str;
return TOK_STR;
}
else
STR_APPEND ();
}
[^()]+ STR_APPEND ();
}
<<EOF>> return TOK_EOF;
%%
/* Epilogue (C code). */

View File

@@ -0,0 +1,35 @@
# 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 %.h: %.l
$(FLEX) $(FLEXFLAGS) -o$*.c --header-file=$*.h $<
scan.o: parse.h
parse.o: scan.h
$(BASE): 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-2019 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,33 @@
#! /bin/sh
# Copyright (C) 2018-2019 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
(((1)+(2))*((3)+(4)))
EOF
run 0 21

View File

@@ -0,0 +1,37 @@
## Copyright (C) 2018-2019 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 %D%/scan.l
%C%_lexcalc_SOURCES = %D%/parse.y %D%/parse.h
nodist_%C%_lexcalc_SOURCES = %D%/scan.c %D%/scan.h
%D%/lexcalc.c: $(dependencies)
%D%/scan.c: %D%/scan.l
$(AM_V_LEX) $(LEX) -o %D%/scan.c --header-file=%D%/scan.h $<
# 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,160 @@
// Prologue (directives).
%expect 0
// Emitted in the header file, before the definition of YYSTYPE.
%code requires
{
typedef void* yyscan_t;
typedef struct
{
// Whether to print the intermediate results.
int verbose;
// Value of the last computation.
int value;
// Number of errors.
int nerrs;
} result;
}
// Emitted in the header file, after the definition of YYSTYPE.
%code provides
{
// Tell Flex the expected prototype of yylex.
// The scanner argument must be named yyscanner.
#define YY_DECL \
enum yytokentype yylex (YYSTYPE* yylval, yyscan_t yyscanner, result *res)
YY_DECL;
void yyerror (yyscan_t scanner, result *res, const char *msg);
}
// Emitted on top of the implementation file.
%code top
{
#include <stdio.h> /* printf. */
#include <stdlib.h> /* getenv. */
}
%code
{
result parse_string (const char* cp);
result parse (void);
}
%define api.pure full
%define api.token.prefix {TOK_}
%define api.value.type union
%define parse.error verbose
%define parse.trace
// Scanner and error count are exchanged between main, yyparse and yylex.
%param {yyscan_t scanner}{result *res}
%token
PLUS "+"
MINUS "-"
STAR "*"
SLASH "/"
EOL "end-of-line"
EOF 0 "end-of-file"
;
%token <int> NUM "number"
%type <int> exp line
%printer { fprintf (yyo, "%d", $$); } <int>
%token <char*> STR "string"
%printer { fprintf (yyo, "\"%s\"", $$); } <char*>
// Precedence (from lowest to highest) and associativity.
%left "+" "-"
%left "*" "/"
%%
// Rules.
input:
%empty
| input line
{
res->value = $line;
if (res->verbose)
printf ("%d\n", $line);
}
;
line:
exp EOL { $$ = $1; }
| exp { $$ = $1; }
| error EOL { $$ = 0; yyerrok; }
;
exp:
exp "+" exp { $$ = $1 + $3; }
| exp "-" exp { $$ = $1 - $3; }
| exp "*" exp { $$ = $1 * $3; }
| exp "/" exp
{
if ($3 == 0)
{
yyerror (scanner, res, "invalid division by zero");
YYERROR;
}
else
$$ = $1 / $3;
}
| STR
{
result r = parse_string ($1);
free ($1);
if (r.nerrs)
{
res->nerrs += r.nerrs;
YYERROR;
}
else
$$ = r.value;
}
| NUM { $$ = $1; }
;
%%
#include "scan.h"
result
parse (void)
{
result res = {1, 0, 0};
yyscan_t scanner;
yylex_init (&scanner);
yyparse (scanner, &res);
yylex_destroy (scanner);
return res;
}
result
parse_string (const char *str)
{
result res = {0, 0, 0};
yyscan_t scanner;
yylex_init (&scanner);
YY_BUFFER_STATE buf = yy_scan_string (str, scanner);
yyparse (scanner, &res);
yy_delete_buffer(buf, scanner);
yylex_destroy (scanner);
return res;
}
// Epilogue (C code).
void yyerror (yyscan_t scanner, result *res, const char *msg)
{
(void) scanner;
fprintf (stderr, "%s\n", msg);
res->nerrs += 1;
}
int main (void)
{
// Possibly enable parser runtime debugging.
yydebug = !!getenv ("YYDEBUG");
result res = parse ();
// Exit on failure if there were errors.
return !!res.nerrs;
}

View File

@@ -0,0 +1,81 @@
/* Prologue (directives). -*- C++ -*- */
/* Disable Flex features we don't need, to avoid warnings. */
%option nodefault noinput nounput noyywrap
%option reentrant
%{
#include <limits.h> /* INT_MIN */
#include <stdlib.h> /* strtol */
#include "parse.h"
%}
%x SC_STRING
%%
%{
int nesting = 0;
char *str = NULL;
int size = 0;
int capacity = 0;
#define STR_APPEND() \
do { \
if (capacity < size + 1) \
{ \
do \
capacity = capacity ? 2 * capacity : 128; \
while (capacity < size + 1); \
str = realloc (str, capacity); \
} \
strncpy (str + size, yytext, yyleng); \
size += yyleng; \
} while (0)
%}
// Rules.
"+" return TOK_PLUS;
"-" return TOK_MINUS;
"*" return TOK_STAR;
"/" return TOK_SLASH;
"(" nesting += 1; BEGIN SC_STRING;
/* Scan an integer. */
[0-9]+ {
errno = 0;
long n = strtol (yytext, NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
yyerror (yyscanner, res, "integer is out of range");
yylval->TOK_NUM = (int) n;
return TOK_NUM;
}
/* Ignore white spaces. */
[ \t]+ continue;
"\n" return TOK_EOL;
. yyerror (yyscanner, res, "syntax error, invalid character");
<SC_STRING>
{
"("* nesting += yyleng; STR_APPEND ();
")" {
if (!--nesting)
{
BEGIN INITIAL;
yylval->TOK_STR = str;
return TOK_STR;
}
else
STR_APPEND ();
}
[^()]+ STR_APPEND ();
}
<<EOF>> return TOK_EOF;
%%
/* Epilogue (C code). */