[PATCH 4/4] bistromathic: demonstrate caret-diagnostics

Akim Demaille <[email protected]>
Newsgroups gmane.comp.parsers.bison.patches
Message-ID <[email protected]>
* 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 <<EOF
 EOF
 run 0 '> *
 > ''
-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 <<EOF
+123 123456
+EOF
+run 0 '> 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 <<EOF
 1 + 2 * * 3
 EOF
 run 0 '> 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 <<EOF
 1 / 0
@@ -132,8 +146,14 @@ run 0 '> ((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 <<EOF
@@ -142,7 +162,9 @@ EOF
 run 0 '> ()
 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 <<EOF
@@ -189,6 +211,8 @@ err: LAC: checking lookahead function: S5
 err: LAC: checking lookahead variable: S6
 err: LAC: checking lookahead NEG: Err
 err: 1.2: syntax error: expected - or ( or number or function or variable before +
+err:     1 | (+_)
+err:       |  ^
 err: LAC: initial context discarded due to error recovery
 err: Shifting token error (1.2: )
 err: Entering state 10
@@ -286,7 +310,9 @@ run 0 '> (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 <<EOF
@@ -294,7 +320,9 @@ sed -e 's/\\t/	/g' >input <<EOF
 EOF
 run 0 '> (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 <<EOF
@@ -314,6 +342,8 @@ EOF
 run -n 0 '> 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 ();
-- 
2.27.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.