examples: lexcalc: demonstrate location tracking

The bistromathic example should not use Flex, it makes it too complex.
But it was the only example to show location tracking with Flex.

* examples/c/lexcalc/lexcalc.test, examples/c/lexcalc/parse.y,
* examples/c/lexcalc/scan.l: Demonstrate location tracking as is done
in bistromathic.
This commit is contained in:
Akim Demaille
2020-02-29 09:54:34 +01:00
parent b870d5fee4
commit 388e12ac0f
8 changed files with 46 additions and 16 deletions

3
NEWS
View File

@@ -101,6 +101,9 @@ GNU Bison NEWS
There are now two examples in examples/java: a very simple calculator, and There are now two examples in examples/java: a very simple calculator, and
one that tracks locations to provide acurate error messages. one that tracks locations to provide acurate error messages.
The lexcalc example (a simple example in C based on Flex and Bison) now
also demonstrates location tracking.
A new C example, bistromathic, is a fully featured calculator using many A new C example, bistromathic, is a fully featured calculator using many
Bison features: pure interface, location tracking, internationalized Bison features: pure interface, location tracking, internationalized
custom error messages, lookahead-correction, rich debug traces, etc. custom error messages, lookahead-correction, rich debug traces, etc.

View File

@@ -28,7 +28,8 @@ Extracted from the documentation: "Multi-Function Calculator: mfcalc".
https://www.gnu.org/software/bison/manual/html_node/Multi_002dfunction-Calc.html 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 with precedence directives and location tracking. It uses
Flex to generate the scanner.
## reccalc - recursive calculator with Flex and Bison ## reccalc - recursive calculator with Flex and Bison
The example builds on top of the previous one to provide a reentrant parser. The example builds on top of the previous one to provide a reentrant parser.
@@ -53,7 +54,7 @@ This example demonstrates the best practices when using Bison.
- Its interface is pure. - Its interface is pure.
- It uses a custom syntax error with location tracking, lookahead correction - It uses a custom syntax error with location tracking, lookahead correction
and token internationalization. and token internationalization.
- It enables debug trace support with formatting of semantic values. - It supports debug traces with semantic values.
- It uses named references instead of the traditional $1, $2, etc. - It uses named references instead of the traditional $1, $2, etc.
It also uses Flex to generate the scanner. It also uses Flex to generate the scanner.

View File

@@ -3,7 +3,7 @@ This example demonstrates the best practices when using Bison.
- Its interface is pure. - Its interface is pure.
- It uses a custom syntax error with location tracking, lookahead correction - It uses a custom syntax error with location tracking, lookahead correction
and token internationalization. and token internationalization.
- It enables debug trace support with formatting of semantic values. - It supports debug traces with semantic values.
- It uses named references instead of the traditional $1, $2, etc. - It uses named references instead of the traditional $1, $2, etc.
It also uses Flex to generate the scanner. It also uses Flex to generate the scanner.

View File

@@ -209,10 +209,10 @@ yyreport_syntax_error (const yyparse_context_t *ctx)
} }
// Called by yyparse on error. // Called by yyparse on error.
void yyerror (YYLTYPE *loc, char const *s) void yyerror (YYLTYPE *loc, char const *msg)
{ {
YY_LOCATION_PRINT (stderr, *loc); YY_LOCATION_PRINT (stderr, *loc);
fprintf (stderr, ": %s\n", s); fprintf (stderr, ": %s\n", msg);
} }
int main (int argc, char const* argv[]) int main (int argc, char const* argv[])

View File

@@ -3,6 +3,8 @@
This directory contains lexcalc, the traditional example of using Flex and This directory contains lexcalc, the traditional example of using Flex and
Bison to build a simple calculator. Bison to build a simple calculator.
It features detailed syntax errors with locations.
<!--- <!---
Local Variables: Local Variables:
fill-column: 76 fill-column: 76

View File

@@ -29,4 +29,9 @@ run -noerr 0 9 -p
cat >input <<EOF cat >input <<EOF
(1+2) * (1+2) *
EOF EOF
run 1 'err: syntax error, unexpected end-of-line, expecting ( or number' run 1 'err: 1.8-2.0: syntax error, unexpected end-of-line, expecting ( or number'
cat >input <<EOF
1 / (2 - 2)
EOF
run 1 'err: 1.1-11: error: division by zero"

View File

@@ -6,10 +6,10 @@
{ {
// Tell Flex the expected prototype of yylex. // Tell Flex the expected prototype of yylex.
#define YY_DECL \ #define YY_DECL \
enum yytokentype yylex (YYSTYPE* yylval, int *nerrs) enum yytokentype yylex (YYSTYPE* yylval, YYLTYPE *yylloc, int *nerrs)
YY_DECL; YY_DECL;
void yyerror (int *nerrs, const char *msg); void yyerror (YYLTYPE *loc, int *nerrs, const char *msg);
} }
// Emitted on top of the implementation file. // Emitted on top of the implementation file.
@@ -33,6 +33,9 @@
// Generate detailed error messages. // Generate detailed error messages.
%define parse.error detailed %define parse.error detailed
// with locations.
%locations
// Enable debug traces (see yydebug in main). // Enable debug traces (see yydebug in main).
%define parse.trace %define parse.trace
@@ -78,7 +81,7 @@ exp:
{ {
if ($3 == 0) if ($3 == 0)
{ {
yyerror (nerrs, "invalid division by zero"); yyerror (&@$, nerrs, "error: division by zero");
YYERROR; YYERROR;
} }
else else
@@ -90,9 +93,10 @@ exp:
%% %%
// Epilogue (C code). // Epilogue (C code).
void yyerror (int *nerrs, const char *msg) void yyerror (YYLTYPE *loc, int *nerrs, const char *msg)
{ {
fprintf (stderr, "%s\n", msg); YY_LOCATION_PRINT (stderr, *loc);
fprintf (stderr, ": %s\n", msg);
++*nerrs; ++*nerrs;
} }

View File

@@ -9,9 +9,24 @@
#include <stdlib.h> /* strtol */ #include <stdlib.h> /* strtol */
#include "parse.h" #include "parse.h"
// Each time a rule is matched, advance the end cursor/position.
#define YY_USER_ACTION \
yylloc->last_column += yyleng;
// Move the first position onto the last.
#define LOCATION_STEP() \
do { \
yylloc->first_line = yylloc->last_line; \
yylloc->first_column = yylloc->last_column; \
} while (0)
%} %}
%% %%
%{
// Each time yylex is called, move the head position to the end one.
LOCATION_STEP ();
%}
/* Rules. */ /* Rules. */
"+" return TOK_PLUS; "+" return TOK_PLUS;
@@ -27,17 +42,17 @@
errno = 0; errno = 0;
long n = strtol (yytext, NULL, 10); long n = strtol (yytext, NULL, 10);
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE)) if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
yyerror (nerrs, "integer is out of range"); yyerror (yylloc, nerrs, "integer is out of range");
yylval->TOK_NUM = (int) n; yylval->TOK_NUM = (int) n;
return TOK_NUM; return TOK_NUM;
} }
"\n" yylloc->last_line++; yylloc->last_column = 1; return TOK_EOL;
/* Ignore white spaces. */ /* Ignore white spaces. */
[ \t]+ continue; [ \t]+ LOCATION_STEP (); continue;
"\n" return TOK_EOL; . yyerror (yylloc, nerrs, "syntax error, invalid character"); continue;
. yyerror (nerrs, "syntax error, invalid character");
<<EOF>> return TOK_EOF; <<EOF>> return TOK_EOF;
%% %%