From dc72b3566d78b37775a4b1b27d12eba408d3a5e8 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Sat, 11 Jul 2020 14:30:15 +0200 Subject: [PATCH] bistromathic: demonstrate caret-diagnostics * examples/c/bistromathic/parse.y (user_context): We need the current line. (yyreport_syntax_error): Quote the guilty line, with squiggles. * examples/c/bistromathic/bistromathic.test: Adjust. --- NEWS | 11 ++++++ TODO | 2 - examples/c/README.md | 11 +++++- examples/c/bistromathic/README.md | 11 +++++- examples/c/bistromathic/bistromathic.test | 46 ++++++++++++++++++++--- examples/c/bistromathic/parse.y | 18 +++++++-- 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 04a7c264..38cf60c8 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,17 @@ GNU Bison NEWS * Noteworthy changes in release ?.? (????-??-??) [?] +** Documentation + +*** Examples + + The bistromathic demonstrates %param and how to quote sources in the error + messages: + + > 123 456 + 1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number + 1 | 123 456 + | ^~~ * Noteworthy changes in release 3.6.91 (2020-07-09) [beta] diff --git a/TODO b/TODO index 38853a95..59c08564 100644 --- a/TODO +++ b/TODO @@ -36,8 +36,6 @@ Unless we play it dumb (little structure). - How about not evaluating incomplete lines when the text is not finished (as shells do). -- Caret diagnostics - ** Questions *** Java - Should i18n be part of the Lexer? Currently it's a static method of diff --git a/examples/c/README.md b/examples/c/README.md index 7d2cb2ac..aa7461f8 100644 --- a/examples/c/README.md +++ b/examples/c/README.md @@ -53,7 +53,9 @@ push-parser model. This example demonstrates best practices when using Bison. - Its hand-written scanner tracks locations. - Its interface is pure. -- It uses the `error` token to get error recovery. +- It uses %params to pass user information to the parser and scanner. +- Its scanner uses the `error` token to signal lexical errors and enter + error recovery. - Its interface is "incremental", well suited for interaction: it uses the push-parser API to feed the parser with the incoming tokens. - It features an interactive command line with completion based on the @@ -62,6 +64,13 @@ This example demonstrates best practices when using Bison. messages. - It uses a custom syntax error with location, lookahead correction and token internationalization. +- Error messages quote the source with squiggles that underline the error: +``` +> 123 456 +1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number + 1 | 123 456 + | ^~~ +``` - It supports debug traces with semantic values. - It uses named references instead of the traditional $1, $2, etc. diff --git a/examples/c/bistromathic/README.md b/examples/c/bistromathic/README.md index 6b66299e..5147cea4 100644 --- a/examples/c/bistromathic/README.md +++ b/examples/c/bistromathic/README.md @@ -2,7 +2,9 @@ This example demonstrates best practices when using Bison. - Its hand-written scanner tracks locations. - Its interface is pure. -- It uses the `error` token to get error recovery. +- It uses %params to pass user information to the parser and scanner. +- Its scanner uses the `error` token to signal lexical errors and enter + error recovery. - Its interface is "incremental", well suited for interaction: it uses the push-parser API to feed the parser with the incoming tokens. - It features an interactive command line with completion based on the @@ -11,6 +13,13 @@ This example demonstrates best practices when using Bison. messages. - It uses a custom syntax error with location, lookahead correction and token internationalization. +- Error messages quote the source with squiggles that underline the error: +``` +> 123 456 +1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number + 1 | 123 456 + | ^~~ +``` - It supports debug traces with semantic values. - It uses named references instead of the traditional $1, $2, etc. diff --git a/examples/c/bistromathic/bistromathic.test b/examples/c/bistromathic/bistromathic.test index 74e8db9c..b0a30365 100755 --- a/examples/c/bistromathic/bistromathic.test +++ b/examples/c/bistromathic/bistromathic.test @@ -101,14 +101,28 @@ cat >input < * > '' -err: 1.1: syntax error: expected end of file or - or ( or exit or number or function etc., before *' +err: 1.1: syntax error: expected end of file or - or ( or exit or number or function etc., before * +err: 1 | * +err: | ^' + +# Underline long errors. +cat >input < 123 123456 +> '' +err: 1.5-10: syntax error: expected end of file or + or - or * or / or ^ before number +err: 1 | 123 123456 +err: | ^~~~~~' cat >input < 1 + 2 * * 3 > '' -err: 1.9: syntax error: expected - or ( or number or function or variable before *' +err: 1.9: syntax error: expected - or ( or number or function or variable before * +err: 1 | 1 + 2 * * 3 +err: | ^' cat >input < ((1 ++ 2) ** 3) 1332 > '' err: 1.6: syntax error: expected - or ( or number or function or variable before + +err: 1 | ((1 ++ 2) ** 3) +err: | ^ err: 2.5: syntax error: expected - or ( or number or function or variable before + -err: 2.16: syntax error: expected - or ( or number or function or variable before *' +err: 2 | (1 ++ 2) + (3 ** 4) +err: | ^ +err: 2.16: syntax error: expected - or ( or number or function or variable before * +err: 2 | (1 ++ 2) + (3 ** 4) +err: | ^' # The rule "( error )" should work even if there are no tokens between "(" and ")". cat >input < () 666 > '' -err: 1.2: syntax error: expected - or ( or number or function or variable before )' +err: 1.2: syntax error: expected - or ( or number or function or variable before ) +err: 1 | () +err: | ^' cat >input < (1+ ( - atan cos exp ln number sin sqrt > (1+ > '' -err: 1.4: syntax error: expected - or ( or number or function or variable before end of file' +err: 1.4: syntax error: expected - or ( or number or function or variable before end of file +err: 1 | (1+ +err: | ^' # Check the completion of a word. sed -e 's/\\t/ /g' >input <input < (atan ( '' > '' -err: 1.9: syntax error: expected - or ( or number or function or variable before end of file' +err: 1.9: syntax error: expected - or ( or number or function or variable before end of file +err: 1 | (atan ( '' +err: | ^' # Check the completion at the very beginning. sed -e 's/\\t/ /g' >input < 1++ '' > '' err: 1.3: syntax error: expected - or ( or number or function or variable before + +err: 1 | 1++ '' +err: | ^ ' # And even when the error was recovered from. @@ -323,5 +353,9 @@ EOF run -n 0 '> (1++2) + 3 + '' > '' err: 1.4: syntax error: expected - or ( or number or function or variable before + +err: 1 | (1++2) + 3 + '' +err: | ^ err: 1.15: syntax error: expected - or ( or number or function or variable before end of file +err: 1 | (1++2) + 3 + '' +err: | ^ ' diff --git a/examples/c/bistromathic/parse.y b/examples/c/bistromathic/parse.y index 0dca4ae0..718ec904 100644 --- a/examples/c/bistromathic/parse.y +++ b/examples/c/bistromathic/parse.y @@ -71,6 +71,8 @@ { // Whether to not emit error messages. int silent; + // The current input line. + const char *line; } user_context; } @@ -396,11 +398,12 @@ yyreport_syntax_error (const yypcontext_t *ctx, const user_context *uctx) argsize = ARGS_MAX; const char *format = error_format_string (1 + argsize + too_many_expected_tokens); + const YYLTYPE *loc = yypcontext_location (ctx); while (*format) // %@: location. if (format[0] == '%' && format[1] == '@') { - YY_LOCATION_PRINT (stderr, *yypcontext_location (ctx)); + YY_LOCATION_PRINT (stderr, *loc); format += 2; } // %u: unexpected token. @@ -425,6 +428,15 @@ yyreport_syntax_error (const yypcontext_t *ctx, const user_context *uctx) ++format; } fputc ('\n', stderr); + + // Quote the source line. + { + fprintf (stderr, "%5d | %s\n", loc->first_line, uctx->line); + fprintf (stderr, "%5s | %*s", "", loc->first_column, "^"); + for (int i = loc->last_column - loc->first_column - 1; 0 < i; --i) + putc ('~', stderr); + putc ('\n', stderr); + } return 0; } @@ -470,7 +482,7 @@ xstrndup (const char *string, size_t n) static int process_line (YYLTYPE *lloc, const char *line) { - user_context uctx = {0}; + user_context uctx = {0, line}; yypstate *ps = yypstate_new (); int status = 0; do { @@ -491,7 +503,7 @@ expected_tokens (const char *input, int *tokens, int ntokens) { YYDPRINTF ((stderr, "expected_tokens (\"%s\")", input)); - user_context uctx = {1}; + user_context uctx = {1, input}; // Parse the current state of the line. yypstate *ps = yypstate_new ();