]> git.puffer.fish Git - mirror/frr.git/commitdiff
lib: parser: add error location reporting
authorDavid Lamparter <equinox@opensourcerouting.org>
Sat, 17 Dec 2016 06:32:58 +0000 (07:32 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Mon, 23 Jan 2017 20:52:44 +0000 (21:52 +0100)
flex+bison have nice capabilities to track the location that is
currently being processed; let's enable these and get better warnings
for broken CLI specs.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
lib/command_lex.l
lib/command_parse.y

index e245fc49766854ca3fe43358870cac9bb6423d34..703596b43dbf26cc92b4f1f56371afe6699e195d 100644 (file)
 
 %{
 #include "command_parse.h"
+
+#define YY_USER_ACTION yylloc->last_column += yyleng;
+#define LOC_STEP \
+        yylloc->first_column = yylloc->last_column; \
+        yylloc->first_line = yylloc->last_line;
 %}
 
 WORD            (\-|\+)?[a-z0-9\*][-+_a-zA-Z0-9\*]*
@@ -45,9 +50,14 @@ RANGE           \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
 %option prefix="cmd_yy"
 %option reentrant
 %option bison-bridge
+%option bison-locations
 
 %%
-[ \t]           /* ignore whitespace */;
+%{
+                LOC_STEP;
+%}
+
+[ \t]+          LOC_STEP /* ignore whitespace */;
 {WORD}          {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return WORD;}
 {IPV4}          {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return IPV4;}
 {IPV4_PREFIX}   {yylval->string = XSTRDUP(MTYPE_TMP, yytext); return IPV4_PREFIX;}
index db4709172c6a7d8e39d00d4dde0bc219c4dcf064..cbc7ce97541b6aac60525a710df8b32e8f556445 100644 (file)
@@ -27,6 +27,8 @@
 #define YYDEBUG 1
 %}
 
+%locations
+%define parse.error verbose
 %define api.pure full
 /* define api.prefix {cmd_yy} */
 
@@ -49,6 +51,7 @@
   #include "graph.h"
 
   #define YYSTYPE CMD_YYSTYPE
+  #define YYLTYPE CMD_YYLTYPE
   struct parser_ctx;
 
   /* subgraph semantic value */
 
   /* bison declarations */
   void
-  cmd_yyerror (struct parser_ctx *ctx, char const *msg);
+  cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg);
 
   /* helper functions for parser */
   static char *
                   char *doc);
 
   static void
-  terminate_graph (struct parser_ctx *ctx,
+  terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
                    struct graph_node *);
 
   static void
@@ -169,7 +172,7 @@ start:
   cmd_token_seq
 {
   // tack on the command element
-  terminate_graph (ctx, ctx->currnode);
+  terminate_graph (&@1, ctx, ctx->currnode);
 }
 | cmd_token_seq placeholder_token '.' '.' '.'
 {
@@ -183,7 +186,7 @@ start:
   add_edge_dedup (ctx->currnode, ctx->currnode);
 
   // tack on the command element
-  terminate_graph (ctx, ctx->currnode);
+  terminate_graph (&@1, ctx, ctx->currnode);
 }
 ;
 
@@ -255,7 +258,7 @@ placeholder_token:
   token->max = strtoll (yylval.string, &yylval.string, 10);
 
   // validate range
-  if (token->min > token->max) cmd_yyerror (ctx, "Invalid range.");
+  if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range.");
 
   free ($1);
 }
@@ -352,11 +355,39 @@ command_parse_format (struct graph *graph, struct cmd_element *cmd)
 /* parser helper functions */
 
 void
-yyerror (struct parser_ctx *ctx, char const *msg)
+yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg)
 {
+  char *tmpstr = strdup(ctx->el->string);
+  char *line, *eol;
+  char spacing[256];
+  int lineno = 0;
+
   zlog_err ("%s: FATAL parse error: %s", __func__, msg);
-  zlog_err ("while parsing this command definition: \n\t%s\n", ctx->el->string);
-  //exit(EXIT_FAILURE);
+  zlog_err ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column);
+
+  line = tmpstr;
+  do {
+    lineno++;
+    eol = strchr(line, '\n');
+    if (eol)
+      *eol++ = '\0';
+
+    zlog_err ("%s: | %s", __func__, line);
+    if (lineno == loc->first_line && lineno == loc->last_line
+        && loc->first_column < (int)sizeof(spacing) - 1
+        && loc->last_column < (int)sizeof(spacing) - 1) {
+
+      int len = loc->last_column - loc->first_column;
+      if (len == 0)
+        len = 1;
+
+      memset(spacing, ' ', loc->first_column - 1);
+      memset(spacing + loc->first_column - 1, '^', len);
+      spacing[loc->first_column - 1 + len] = '\0';
+      zlog_err ("%s: | %s", __func__, spacing);
+    }
+  } while ((line = eol));
+  free(tmpstr);
 }
 
 static void
@@ -371,7 +402,8 @@ cleanup (struct parser_ctx *ctx)
 }
 
 static void
-terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode)
+terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
+                 struct graph_node *finalnode)
 {
   // end of graph should look like this
   // * -> finalnode -> END_TKN -> cmd_element
@@ -385,7 +417,7 @@ terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode)
     graph_new_node (ctx->graph, element, NULL);
 
   if (node_adjacent (finalnode, end_token_node))
-    cmd_yyerror (ctx, "Duplicate command.");
+    cmd_yyerror (locp, ctx, "Duplicate command.");
 
   graph_add_edge (finalnode, end_token_node);
   graph_add_edge (end_token_node, end_element_node);