From 9c66f418c444a355f6cbbcfadbb259f7d52d44e6 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Wed, 8 Sep 2004 14:36:25 +0000 Subject: [PATCH] * tests/actions.at (_AT_CHECK_PRINTER_AND_DESTRUCTOR): Move the Bison directive from the Bison file to the invocation of this macro, so that these directives are passed to AT_BISON_OPTION_PUSHDEFS to get correct help macros. Use these helping macros (e.g., AT_LOC, AT_VAL and so forth). Move the AT_SETUP/AT_CLEANUP outside, to report as test title the extra Bison directives instead of the whole series. Change the grammar so that there are recoverable errors, and unrecoverable errors. Now we can have the parser give up before consuming the whole input. As a result we now can observe that the lookahead is freed when needed. Change the parser source to parse argv[1] instead of a hard coded string. Simplify yylex, and give a value and location to EOF. Simplify some invocations of AT_CHECK_PRINTER_AND_DESTRUCTOR that passed directives already coded in the file. Add some tests to check the location of "error". For some tests, the C++ parser is correct, and not yacc.c. For other tests, they provide different, but unsatisfying, values, so keep the C++ value so that at least one parser is "correct" according to the test suite. (Actions after errors): Remove, this is subsumed by the AT_CHECK_PRINTER_AND_DESTRUCTOR series. --- ChangeLog | 26 ++++ tests/actions.at | 366 ++++++++++++++++++----------------------------- 2 files changed, 162 insertions(+), 230 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7dfe49cf..4c54cbd0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2004-09-08 Akim Demaille + + * tests/actions.at (_AT_CHECK_PRINTER_AND_DESTRUCTOR): Move the + Bison directive from the Bison file to the invocation of this + macro, so that these directives are passed to + AT_BISON_OPTION_PUSHDEFS to get correct help macros. + Use these helping macros (e.g., AT_LOC, AT_VAL and so forth). + Move the AT_SETUP/AT_CLEANUP outside, to report as test title + the extra Bison directives instead of the whole series. + Change the grammar so that there are recoverable errors, and + unrecoverable errors. Now we can have the parser give up before + consuming the whole input. As a result we now can observe that + the lookahead is freed when needed. + Change the parser source to parse argv[1] instead of a hard coded + string. + Simplify yylex, and give a value and location to EOF. + Simplify some invocations of AT_CHECK_PRINTER_AND_DESTRUCTOR that + passed directives already coded in the file. + Add some tests to check the location of "error". + For some tests, the C++ parser is correct, and not yacc.c. + For other tests, they provide different, but unsatisfying, values, + so keep the C++ value so that at least one parser is "correct" + according to the test suite. + (Actions after errors): Remove, this is subsumed by the + AT_CHECK_PRINTER_AND_DESTRUCTOR series. + 2004-09-06 Akim Demaille * data/lalr1.cc: Adjust the indentation of the labels. diff --git a/tests/actions.at b/tests/actions.at index e8124e9e..933478c6 100644 --- a/tests/actions.at +++ b/tests/actions.at @@ -82,151 +82,6 @@ AT_CLEANUP -## ---------------------- ## -## Actions after errors. ## -## ---------------------- ## - -AT_SETUP([Actions after errors]) - - - -AT_DATA_GRAMMAR([[input.y]], -[[%{ -#include -#include - -static int yylex (void); -static void yyerror (char const *); - -#define YYDEBUG 1 -%} -%union { int ival; } -%type 'x' ';' thing line input - -%% -input: - /* Nothing. */ - { - $$ = 0; - printf ("input (%d): /* Nothing */\n", $$); - } -| line input /* Right recursive to load the stack so that popping at - EOF can be exercised. */ - { - $$ = 2; - printf ("input (%d): line (%d) input (%d)\n", $$, $1, $2); - } -; - -line: - thing thing thing ';' - { - $$ = $1; - printf ("line (%d): thing (%d) thing (%d) thing (%d) ';' (%d)\n", - $$, $1, $2, $3, $4); - } -| thing thing ';' - { - $$ = $1; - printf ("line (%d): thing (%d) thing (%d) ';' (%d)\n", $$, $1, $2, $3); - } -| thing ';' - { - $$ = $1; - printf ("line (%d): thing (%d) ';' (%d)\n", $$, $1, $2); - } -| error ';' - { - $$ = -1; - printf ("line (%d): error ';' (%d)\n", $$, $2); - } -; - -thing: - 'x' - { - $$ = $1; - printf ("thing (%d): 'x' (%d)\n", $$, $1); - } -; -%% -static size_t counter; - -static int -yylex (void) -{ - static char const input[] = - { - /* Exercise the discarding of stack top and input until `error' - can be reduced. */ - 'x', 'x', 'x', 'x', 'x', 'x', ';', - - /* Load the stack and provoke an error that cannot be caught by - the grammar, to check that the stack is cleared. */ - 'x', 'x', ';', - 'x', ';', - 'y' - }; - - if (counter < sizeof input) - { - yylval.ival = counter; - printf ("sending: '%c' (%d)\n", input[counter], yylval.ival); - return input[counter++]; - } - else - { - printf ("sending: EOF\n"); - return EOF; - } -} - -static void -yyerror (char const *msg) -{ - printf ("%lu: %s\n", (unsigned long int) counter, msg); -} - -int -main (void) -{ - yydebug = !!getenv ("YYDEBUG"); - return yyparse (); -} -]]) - -AT_CHECK([bison -o input.c input.y]) -AT_COMPILE([input]) -AT_PARSER_CHECK([./input], 1, -[[sending: 'x' (0) -thing (0): 'x' (0) -sending: 'x' (1) -thing (1): 'x' (1) -sending: 'x' (2) -thing (2): 'x' (2) -sending: 'x' (3) -4: syntax error -sending: 'x' (4) -sending: 'x' (5) -sending: ';' (6) -line (-1): error ';' (6) -sending: 'x' (7) -thing (7): 'x' (7) -sending: 'x' (8) -thing (8): 'x' (8) -sending: ';' (9) -line (7): thing (7) thing (8) ';' (9) -sending: 'x' (10) -thing (10): 'x' (10) -sending: ';' (11) -line (10): thing (10) ';' (11) -sending: 'y' (12) -13: syntax error -sending: EOF -]]) - -AT_CLEANUP - ## ---------------- ## @@ -307,47 +162,44 @@ AT_CLEANUP # _AT_CHECK_PRINTER_AND_DESTRUCTOR($1, $2, $3, $4, BISON-DIRECTIVE, UNION-FLAG) # ----------------------------------------------------------------------------- m4_define([_AT_CHECK_PRINTER_AND_DESTRUCTOR], -[m4_if([$1$2$3], $[1]$[2]$[3], [], +[# Make sure complex $n work. +m4_if([$1$2$3], $[1]$[2]$[3], [], [m4_fatal([$0: Invalid arguments: $@])])dnl -AT_SETUP([Printers and Destructors $6: $5]) - -# Make sure complex $n work. - +# Be sure to pass all the %directives to this macro to have correct +# helping macros. So don't put any directly in the Bison file. AT_BISON_OPTION_PUSHDEFS([$5]) AT_DATA_GRAMMAR([[input.y]], -[[$5 -%{ +[[%{ #include #include +#include ]AT_LALR1_CC_IF( [#define RANGE(Location) (Location).begin.line, (Location).end.line], [#define RANGE(Location) (Location).first_line, (Location).last_line]) [%} -%error-verbose -%debug -%verbose -%locations -]m4_ifval([$6], [%union + +$5] +m4_ifval([$6], [%union { int ival; }]) [ %{ ]AT_LALR1_CC_IF([typedef yy::Location YYLTYPE; -m4_ifval([$6], , [#define YYSTYPE int])]) + m4_ifval([$6], , [#define YYSTYPE int])]) [static int yylex (]AT_LEX_FORMALS[); ]AT_LALR1_CC_IF([], [static void yyerror (const char *msg);]) [%} -]m4_ifval([$6], [%type 'x' ';' thing line input])[ +]m4_ifval([$6], [%type '(' 'x' 'y' ')' ';' thing line input])[ %printer { ]AT_LALR1_CC_IF([cdebug_ << @$ << ": " << $$;], [fprintf (yyoutput, "%d@%d-%d", $$, RANGE (@$))])[; } - input line thing 'x' + input line thing 'x' 'y' %destructor { printf ("Freeing nterm input (%d@%d-%d)\n", $$, RANGE (@$)); } @@ -365,7 +217,18 @@ m4_ifval([$6], , [#define YYSTYPE int])]) { printf ("Freeing token 'x' (%d@%d-%d)\n", $$, RANGE (@$)); } 'x' +%destructor + { printf ("Freeing token 'y' (%d@%d-%d)\n", $$, RANGE (@$)); } + 'y' + %% +/* + This grammar is made to exercise error recovery. + "Lines" starting with `(' support error recovery, with + ')' as synchronizing token. Lines starting with 'x' can never + be recovered from if in error. +*/ + input: /* Nothing. */ { @@ -389,23 +252,24 @@ line: $$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2), $3, RANGE (@3), $4, RANGE (@4)); } -| thing thing ';' +| '(' thing thing ')' { $$ = $1; - printf ("line (%d@%d-%d): thing (%d@%d-%d) thing (%d@%d-%d) ';' (%d@%d-%d)\n", - $$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2), $3, RANGE (@3)); + printf ("line (%d@%d-%d): '(' (%d@%d-%d) thing (%d@%d-%d) thing (%d@%d-%d) ')' (%d@%d-%d)\n", + $$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2), + $3, RANGE (@3), $4, RANGE (@4)); } -| thing ';' +| '(' thing ')' { $$ = $1; - printf ("line (%d@%d-%d): thing (%d@%d-%d) ';' (%d@%d-%d)\n", - $$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2)); + printf ("line (%d@%d-%d): '(' (%d@%d-%d) thing (%d@%d-%d) ')' (%d@%d-%d)\n", + $$, RANGE (@$), $1, RANGE (@1), $2, RANGE (@2), $3, RANGE (@3)); } -| error ';' +| '(' error ')' { $$ = -1; - printf ("line (%d@%d-%d): error (@%d-%d) ';' (%d@%d-%d)\n", - $$, RANGE (@$), RANGE (@1), $2, RANGE (@2)); + printf ("line (%d@%d-%d): '(' (%d@%d-%d) error (@%d-%d) ')' (%d@%d-%d)\n", + $$, RANGE (@$), $1, RANGE (@1), RANGE (@2), $3, RANGE (@3)); } ; @@ -418,48 +282,30 @@ thing: } ; %% +/* Alias to ARGV[1]. */ +const char *yysource = 0; + static int yylex (]AT_LEX_FORMALS[) { - static const char input[] = - { - /* Exercise the discarding of stack top and input until `error' - can be reduced. */ - 'x', 'x', 'x', 'x', 'x', 'x', ';', - - /* Load the stack and provoke an error that cannot be caught by - the grammar, to check that the stack is cleared. */ - 'x', 'x', ';', - 'x', ';', - 'y' - }; static unsigned int counter = 0; - if (counter < (sizeof(input) / sizeof (input[0]))) - { + int c = ]AT_VAL[]m4_ifval([$6], [.ival])[ = counter++; + /* As in BASIC, line numbers go from 10 to 10. */ ]AT_LALR1_CC_IF( -[ int c = m4_ifval([$6], [yylval->ival], [*yylval]) = counter++; - /* As in BASIC, line numbers go from 10 to 10. */ - yylloc->begin.line = yylloc->begin.column = 10 * c; - yylloc->end.line = yylloc->end.column = yylloc->begin.line + 9; - printf ("sending: '%c' (%d@%d-%d)\n", - input[[c]], c, RANGE (*yylloc)); - return input[[c]]; +[ AT_LOC.begin.line = AT_LOC.begin.column = 10 * c; + AT_LOC.end.line = AT_LOC.end.column = AT_LOC.begin.line + 9; ], -[ int c = m4_ifval([$6], [yylval.ival], [yylval]) = counter++; - /* As in BASIC, line numbers go from 10 to 10. */ - yylloc.first_line = yylloc.first_column = 10 * c; - yylloc.last_line = yylloc.last_column = yylloc.first_line + 9; - printf ("sending: '%c' (%d@%d-%d)\n", - input[[c]], c, RANGE (yylloc)); - return input[[c]]; +[ AT_LOC.first_line = AT_LOC.first_column = 10 * c; + AT_LOC.last_line = AT_LOC.last_column = AT_LOC.first_line + 9; ])[ - } + + if (yysource[c]) + printf ("sending: '%c'", yysource[c]); else - { - printf ("sending: EOF\n"); - return EOF; - } + printf ("sending: EOF"); + printf (" (%d@%d-%d)\n", c, RANGE (]AT_LOC[)); + return yysource[c]; } ]AT_LALR1_CC_IF( @@ -492,9 +338,11 @@ yyerror (const char *msg) }])[ int -main (void) +main (int argc, const char *argv[]) { yydebug = !!getenv ("YYDEBUG"); + assert (argc == 2); + yysource = argv[1]; if (yyparse ()) { printf ("Parsing FAILED.\n"); @@ -510,61 +358,119 @@ AT_LALR1_CC_IF( AT_COMPILE_CXX([input])], [AT_CHECK([bison -o input.c input.y]) AT_COMPILE([input])]) -AT_PARSER_CHECK([./input], 1, -[[sending: 'x' (0@0-9) -thing (0@0-9): 'x' (0@0-9) + + +# Check the location of "empty" +# ----------------------------- +# I.e., epsilon-reductions, as in "(x)" which ends by reducing +# an empty "line" nterm. +# FIXME: This location is not satisfying. Depend on the lookahead? +AT_PARSER_CHECK([./input '(x)'], 0, +[[sending: '(' (0@0-9) +sending: 'x' (1@10-19) +thing (1@10-19): 'x' (1@10-19) +sending: ')' (2@20-29) +line (0@0-29): '(' (0@0-9) thing (1@10-19) ')' (2@20-29) +sending: EOF (3@30-39) +input (0@0-29): /* Nothing */ +input (2@0-29): line (0@0-29) input (0@0-29) +Successful parse. +]]) + + +# Check locations in error recovery +# --------------------------------- +# '(y)' is an error, but can be recovered from. But what's the location +# of the error itself ('y'), and of the resulting reduction ('(error)'). +AT_PARSER_CHECK([./input '(y)'], 0, +[[sending: '(' (0@0-9) +sending: 'y' (1@10-19) +10-19: syntax error, unexpected 'y', expecting 'x' +Freeing token 'y' (1@10-19) +sending: ')' (2@20-29) +line (-1@0-29): '(' (0@0-9) error (@10-19) ')' (2@20-29) +sending: EOF (3@30-39) +input (0@0-29): /* Nothing */ +input (2@0-29): line (-1@0-29) input (0@0-29) +Successful parse. +]]) + + +# Syntax errors caught by the parser +# ---------------------------------- +# Exercise the discarding of stack top and input until `error' +# can be reduced. +# +# '(', 'x', 'x', 'x', 'x', 'x', ')', +# +# Load the stack and provoke an error that cannot be caught by the +# grammar, to check that the stack is cleared. And make sure the +# lookahead is freed. +# +# '(', 'x', ')', +# '(', 'x', ')', +# 'y' +AT_PARSER_CHECK([./input '(xxxxx)(x)(x)y'], 1, +[[sending: '(' (0@0-9) sending: 'x' (1@10-19) thing (1@10-19): 'x' (1@10-19) sending: 'x' (2@20-29) thing (2@20-29): 'x' (2@20-29) sending: 'x' (3@30-39) -30-39: syntax error, unexpected 'x', expecting ';' +30-39: syntax error, unexpected 'x', expecting ')' Freeing nterm thing (2@20-29) Freeing nterm thing (1@10-19) -Freeing nterm thing (0@0-9) Freeing token 'x' (3@30-39) sending: 'x' (4@40-49) Freeing token 'x' (4@40-49) sending: 'x' (5@50-59) Freeing token 'x' (5@50-59) -sending: ';' (6@60-69) -line (-1@0-69): error (@0-59) ';' (6@60-69) -sending: 'x' (7@70-79) -thing (7@70-79): 'x' (7@70-79) +sending: ')' (6@60-69) +line (-1@0-69): '(' (0@0-9) error (@10-59) ')' (6@60-69) +sending: '(' (7@70-79) sending: 'x' (8@80-89) thing (8@80-89): 'x' (8@80-89) -sending: ';' (9@90-99) -line (7@70-99): thing (7@70-79) thing (8@80-89) ';' (9@90-99) -sending: 'x' (10@100-109) -thing (10@100-109): 'x' (10@100-109) -sending: ';' (11@110-119) -line (10@100-119): thing (10@100-109) ';' (11@110-119) -sending: 'y' (12@120-129) -120-129: syntax error, unexpected $undefined, expecting $end or 'x' -sending: EOF -Freeing nterm line (10@100-119) -Freeing nterm line (7@70-99) -Freeing nterm line (-1@0-69) +sending: ')' (9@90-99) +line (7@70-99): '(' (7@70-79) thing (8@80-89) ')' (9@90-99) +sending: '(' (10@100-109) +sending: 'x' (11@110-119) +thing (11@110-119): 'x' (11@110-119) +sending: ')' (12@120-129) +line (10@100-129): '(' (10@100-109) thing (11@110-119) ')' (12@120-129) +sending: 'y' (13@130-139) +input (0@100-129): /* Nothing */ +input (2@100-129): line (10@100-129) input (0@100-129) +input (2@70-129): line (7@70-99) input (2@100-129) +input (2@0-129): line (-1@0-69) input (2@70-129) +130-139: syntax error, unexpected 'y', expecting $end +Freeing nterm input (2@0-129) +Freeing token 'y' (13@130-139) Parsing FAILED. ]]) -AT_CLEANUP ]) # AT_CHECK_PRINTER_AND_DESTRUCTOR([BISON-OPTIONS], [UNION-FLAG]) # -------------------------------------------------------------- -# Produce `calc.y'. m4_define([AT_CHECK_PRINTER_AND_DESTRUCTOR], -[_AT_CHECK_PRINTER_AND_DESTRUCTOR($[1], $[2], $[3], $[4], [$1], [$2]) +[AT_SETUP([Printers and Destructors $2: $1]) + +_AT_CHECK_PRINTER_AND_DESTRUCTOR($[1], $[2], $[3], $[4], +[%error-verbose +%debug +%verbose +%locations +$1], [$2]) + +AT_CLEANUP ]) AT_CHECK_PRINTER_AND_DESTRUCTOR([]) AT_CHECK_PRINTER_AND_DESTRUCTOR([], [with union]) -AT_CHECK_PRINTER_AND_DESTRUCTOR([%locations %defines %skeleton "lalr1.cc"]) -AT_CHECK_PRINTER_AND_DESTRUCTOR([%locations %defines %skeleton "lalr1.cc"], - [with union]) +AT_CHECK_PRINTER_AND_DESTRUCTOR([%defines %skeleton "lalr1.cc"]) +AT_CHECK_PRINTER_AND_DESTRUCTOR([%defines %skeleton "lalr1.cc"], [with union]) # FIXME. These test cases fail. #AT_CHECK_PRINTER_AND_DESTRUCTOR([%glr-parser]) -- 2.45.2