From 68ef3a046663faf750085650c004bea89b9e2b30 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Sat, 11 Jan 2020 17:24:59 +0100 Subject: [PATCH] yacc.c: check custom error messages * tests/local.at (AT_ERROR_CUSTOM_IF, AT_ERROR_VERBOSE_IF) (AT_ERROR_SIMPLE_IF): New. (AT_YYERROR_DEFINE(c)): Generate yyreport_syntax_error. * tests/calc.at (_AT_CHECK_CALC_ERROR): Accept custom error messages as additional test case. Use it. Add a new test case for %define parse.error custom. --- tests/calc.at | 68 ++++++++++++++++++++++++++++++++-------------- tests/local.at | 50 +++++++++++++++++++++++++++++----- tests/testsuite.at | 2 +- 3 files changed, 92 insertions(+), 28 deletions(-) diff --git a/tests/calc.at b/tests/calc.at index 4af06afc..06a74bc3 100644 --- a/tests/calc.at +++ b/tests/calc.at @@ -545,10 +545,11 @@ AT_PARSER_CHECK([calc input], 0, [], [stderr]) ]) -# _AT_CHECK_CALC_ERROR(BISON-OPTIONS, EXIT-STATUS, INPUT, -# [NUM-STDERR-LINES], -# [VERBOSE-AND-LOCATED-ERROR-MESSAGE]) -# --------------------------------------------------------- +# _AT_CHECK_CALC_ERROR($1 = BISON-OPTIONS, $2 = EXIT-STATUS, $3 = INPUT, +# $4 = [NUM-STDERR-LINES], +# $5 = [VERBOSE-AND-LOCATED-ERROR-MESSAGE] +# $6 = [CUSTOM-ERROR-MESSAGE]) +# ---------------------------------------------------------------------- # Run 'calc' on INPUT, and expect a 'syntax error' message. # # If INPUT starts with a slash, it is used as absolute input file name, @@ -595,9 +596,15 @@ sed '/^Starting/d mv at-stderr stderr # 2. Create the reference error message. -AT_DATA([[expout]], +AT_ERROR_CUSTOM_IF([ + AT_DATA([[expout]], +[$6 +]) +], + [AT_DATA([[expout]], [$5 ]) +]) # 3. If locations are not used, remove them. AT_YYERROR_SEES_LOC_IF([], @@ -605,7 +612,7 @@ AT_YYERROR_SEES_LOC_IF([], mv at-expout expout]]) # 4. If error-verbose is not used, strip the', unexpected....' part. -m4_bmatch([$1], [%define parse.error verbose], [], +AT_ERROR_SIMPLE_IF( [[sed 's/syntax error, .*$/syntax error/' expout >at-expout mv at-expout expout]]) @@ -668,21 +675,27 @@ _AT_CHECK_CALC([$1], # Some syntax errors. _AT_CHECK_CALC_ERROR([$1], [1], [1 2], [15], - [1.3: syntax error, unexpected number]) + [[1.3: syntax error, unexpected number]], + [[1.3: syntax error on token ["number"] (expected: ['='] ['-'] ['+'] ['*'] ['/'] ['^'] ['\n'])]]) _AT_CHECK_CALC_ERROR([$1], [1], [1//2], [20], - [1.3: syntax error, unexpected '/', expecting number or '-' or '(' or '!']) + [[1.3: syntax error, unexpected '/', expecting number or '-' or '(' or '!']], + [[1.3: syntax error on token ['/'] (expected: ["number"] ['-'] ['('] ['!'])]]) _AT_CHECK_CALC_ERROR([$1], [1], [error], [5], - [1.1: syntax error, unexpected $undefined]) + [[1.1: syntax error, unexpected $undefined]], + [[1.1: syntax error on token [$undefined] (expected: ["number"] ['-'] ['\n'] ['('] ['!'])]]) _AT_CHECK_CALC_ERROR([$1], [1], [1 = 2 = 3], [30], - [1.7: syntax error, unexpected '=']) + [[1.7: syntax error, unexpected '=']], + [[1.7: syntax error on token ['='] (expected: ['-'] ['+'] ['*'] ['/'] ['^'])]]) _AT_CHECK_CALC_ERROR([$1], [1], [ +1], [20], - [2.1: syntax error, unexpected '+']) + [[2.1: syntax error, unexpected '+']], + [[2.1: syntax error on token ['+'] (expected: ["end of input"] ["number"] ['-'] ['\n'] ['('] ['!'])]]) # Exercise error messages with EOF: work on an empty file. _AT_CHECK_CALC_ERROR([$1], [1], [/dev/null], [4], - [1.1: syntax error, unexpected end of input]) + [[1.1: syntax error, unexpected end of input]], + [[1.1: syntax error on token ["end of input"] (expected: ["number"] ['-'] ['\n'] ['('] ['!'])]]) # Exercise the error token: without it, we die at the first error, # hence be sure to @@ -703,28 +716,41 @@ _AT_CHECK_CALC_ERROR([$1], [1], [/dev/null], [4], _AT_CHECK_CALC_ERROR([$1], [0], [() + (1 + 1 + 1 +) + (* * *) + (1 * 2 * *) = 1], [250], -[1.2: syntax error, unexpected ')', expecting number or '-' or '(' or '!' +[[1.2: syntax error, unexpected ')', expecting number or '-' or '(' or '!' 1.18: syntax error, unexpected ')', expecting number or '-' or '(' or '!' 1.23: syntax error, unexpected '*', expecting number or '-' or '(' or '!' 1.41: syntax error, unexpected '*', expecting number or '-' or '(' or '!' -calc: error: 4444 != 1]) +calc: error: 4444 != 1]], +[[1.2: syntax error on token [')'] (expected: ["number"] ['-'] ['('] ['!']) +1.18: syntax error on token [')'] (expected: ["number"] ['-'] ['('] ['!']) +1.23: syntax error on token ['*'] (expected: ["number"] ['-'] ['('] ['!']) +1.41: syntax error on token ['*'] (expected: ["number"] ['-'] ['('] ['!']) +calc: error: 4444 != 1]]) # The same, but this time exercising explicitly triggered syntax errors. # POSIX says the lookahead causing the error should not be discarded. _AT_CHECK_CALC_ERROR([$1], [0], [(!) + (1 2) = 1], [102], -[1.10: syntax error, unexpected number -calc: error: 2222 != 1]) +[[1.10: syntax error, unexpected number +calc: error: 2222 != 1]], +[[1.10: syntax error on token ["number"] (expected: ['='] ['-'] ['+'] ['*'] ['/'] ['^'] [')']) +calc: error: 2222 != 1]]) _AT_CHECK_CALC_ERROR([$1], [0], [(- *) + (1 2) = 1], [113], -[1.4: syntax error, unexpected '*', expecting number or '-' or '(' or '!' +[[1.4: syntax error, unexpected '*', expecting number or '-' or '(' or '!' 1.12: syntax error, unexpected number -calc: error: 2222 != 1]) +calc: error: 2222 != 1]], +[[1.4: syntax error on token ['*'] (expected: ["number"] ['-'] ['('] ['!']) +1.12: syntax error on token ["number"] (expected: ['='] ['-'] ['+'] ['*'] ['/'] ['^'] [')']) +calc: error: 2222 != 1]]) # Check that yyerrok works properly: second error is not reported, # third and fourth are. Parse status is succesful. _AT_CHECK_CALC_ERROR([$1], [0], [(* *) + (*) + (*)], [113], -[1.2: syntax error, unexpected '*', expecting number or '-' or '(' or '!' +[[1.2: syntax error, unexpected '*', expecting number or '-' or '(' or '!' 1.10: syntax error, unexpected '*', expecting number or '-' or '(' or '!' -1.16: syntax error, unexpected '*', expecting number or '-' or '(' or '!']) +1.16: syntax error, unexpected '*', expecting number or '-' or '(' or '!']], +[[1.2: syntax error on token ['*'] (expected: ["number"] ['-'] ['('] ['!']) +1.10: syntax error on token ['*'] (expected: ["number"] ['-'] ['('] ['!']) +1.16: syntax error on token ['*'] (expected: ["number"] ['-'] ['('] ['!'])]]) AT_BISON_OPTION_POPDEFS @@ -777,6 +803,8 @@ AT_CHECK_CALC_LALR([%define api.pure %define parse.error verbose %debug %locatio AT_CHECK_CALC_LALR([%no-lines %define api.pure %define parse.error verbose %debug %locations %defines %define api.prefix {calc} %verbose %yacc %parse-param {semantic_value *result}{int *count}]) +AT_CHECK_CALC_LALR([%define parse.error custom]) + # ----------------------- # # Simple GLR Calculator. # # ----------------------- # diff --git a/tests/local.at b/tests/local.at index 2e2dd5bf..38f2fa19 100644 --- a/tests/local.at +++ b/tests/local.at @@ -203,7 +203,13 @@ m4_pushdef([AT_AUTOMOVE_IF], m4_pushdef([AT_DEFINES_IF], [m4_bmatch([$3], [%defines], [$1], [$2])]) m4_pushdef([AT_DEBUG_IF], -[m4_bmatch([$3], [%debug\|%define parse.trace], [$1], [$2])]) + [m4_bmatch([$3], [%debug\|%define parse.trace], [$1], [$2])]) +m4_pushdef([AT_ERROR_CUSTOM_IF], + [m4_bmatch([$3], [%define parse\.error custom], [$1], [$2])]) +m4_pushdef([AT_ERROR_VERBOSE_IF], + [m4_bmatch([$3], [%define parse\.error verbose], [$1], [$2])]) +m4_pushdef([AT_ERROR_SIMPLE_IF], + [AT_ERROR_CUSTOM_IF([$2], [AT_ERROR_VERBOSE_IF([$2], [$1])], [$1])]) m4_pushdef([AT_CXX_IF], [m4_bmatch([$3], [%language "[Cc]\+\+"\|%skeleton "[a-z0-9]+\.cc"], [$1], [$2])]) m4_pushdef([AT_D_IF], @@ -409,8 +415,12 @@ m4_popdef([AT_LANG]) m4_popdef([AT_JAVA_IF]) m4_popdef([AT_GLR_CC_IF]) m4_popdef([AT_LALR1_CC_IF]) -m4_popdef([AT_DEFINES_IF]) +m4_popdef([AT_ERROR_SIMPLE_IF]) +m4_popdef([AT_ERROR_VERBOSE_IF]) +m4_popdef([AT_ERROR_CUSTOM_IF]) m4_popdef([AT_DEBUG_IF]) +m4_popdef([AT_DEFINES_IF]) +m4_popdef([AT_AUTOMOVE_IF]) AT_LOC_POPDEF])dnl ])# AT_BISON_OPTION_POPDEFS @@ -552,7 +562,7 @@ m4_define([AT_YYERROR_DECLARE_EXTERN(c)], [AT_YYERROR_PROTOTYPE;]) m4_define([AT_YYERROR_DECLARE(c)], -[#include +[[#include ]AT_LOCATION_IF([[ #if defined ]AT_YYLTYPE[_IS_TRIVIAL && ]AT_YYLTYPE[_IS_TRIVIAL static int location_print (FILE *yyo, ]AT_YYLTYPE[ const * const yylocp); @@ -561,12 +571,11 @@ static int location_print (FILE *yyo, ]AT_YYLTYPE[ const * const yylocp); # endif #endif ]])[ -static AT_YYERROR_DECLARE_EXTERN]) +static ]AT_YYERROR_DECLARE_EXTERN]) m4_define([AT_YYERROR_DEFINE(c)], -[[ -]AT_LOCATION_IF([[ +[AT_LOCATION_IF([[ # if defined ]AT_YYLTYPE[_IS_TRIVIAL && ]AT_YYLTYPE[_IS_TRIVIAL /* Print *YYLOCP on YYO. */ __attribute__((__unused__)) @@ -596,6 +605,33 @@ location_print (FILE *yyo, ]AT_YYLTYPE[ const * const yylocp) } #endif ]])[ + +]AT_ERROR_CUSTOM_IF([[ +int +yyreport_syntax_error (const yyparse_context_t *ctx) +{ + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + int arg[YYNTOKENS]; + int n = yysyntax_error_arguments (ctx, arg, sizeof arg / sizeof *arg); + if (n == -2) + return 2; + if (n) + { + fprintf (stderr, "syntax error on token [%s]", yysymbol_name (arg[0])); + if (1 < n) + { + fprintf (stderr, " (expected:"); + for (int i = 1; i < n; ++i) + fprintf (stderr, " [%s]", yysymbol_name (arg[i])); + fprintf (stderr, ")"); + } + fprintf (stderr, "\n"); + } + return 0; +} +]])[ + /* A C error reporting function. */ static ]AT_YYERROR_PROTOTYPE[ @@ -606,7 +642,7 @@ AT_YYERROR_SEES_LOC_IF([[ LOCATION_PRINT (stderr, ]AT_LOC[); fprintf (stderr, ": ");]])[ fprintf (stderr, "%s\n", msg); -}]]) +}]])]) m4_define([AT_YYLEX_DEFINE(c)], diff --git a/tests/testsuite.at b/tests/testsuite.at index 2cfbefc5..e2b72b84 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -26,7 +26,7 @@ m4_include([named-refs.at]) # Output file names. m4_include([output.at]) -# Diagnostics. +# Bison diagnostics. m4_include([diagnostics.at]) # Skeleton support.