]> granicus.if.org Git - jq/commitdiff
Much, much better error reporting from the parser.
authorStephen Dolan <mu@netsoc.tcd.ie>
Mon, 10 Sep 2012 23:04:47 +0000 (00:04 +0100)
committerStephen Dolan <mu@netsoc.tcd.ie>
Mon, 10 Sep 2012 23:04:47 +0000 (00:04 +0100)
Getting proper locations through flex/bison was more effort than
was advertised.

c/execute.c
c/jv_parse.c
c/lexer.l
c/locfile.h [new file with mode: 0644]
c/main.c
c/parser.y

index f59e156e08eff6df58c205e438b53ffde844aefd..9f7b63b5704ce467ff330815f69e10ea7e18ff1b 100644 (file)
@@ -470,23 +470,3 @@ void jq_teardown() {
   pathbuf = 0;
   pathsize = 0;
 }
-
-void run_program(struct bytecode* bc) {
-#if JQ_DEBUG
-  dump_disassembly(0, bc);
-  printf("\n");
-#endif
-  char buf[409600];
-  fgets(buf, sizeof(buf), stdin);
-  jq_init(bc, jv_parse(buf));
-  jv result;
-  while (jv_is_valid(result = jq_next())) {
-    jv_dump(result);
-    printf("\n");
-  }
-  jv_free(result);
-  #if JQ_DEBUG
-  printf("end of results\n");
-  #endif
-  jq_teardown();
-}
index 9300f311fea4b4ba09aa25f70ebf5614ebbc77ef..7fd4d2d26bea9491a8842d68394e808bf42daac6 100644 (file)
@@ -331,20 +331,18 @@ jv jv_parse_sized(const char* string, int length) {
 
   const char* p = string;
   char ch;
-  while (p < string + length) {
+  presult msg = 0;
+  while (msg == 0 && p < string + length) {
     ch = *p++;
-    presult msg = scan(&parser, ch);
-    if (msg){
-      printf("ERROR: %s (parsing '%s')\n", msg, string);
-      return jv_invalid();
-    }
+    msg = scan(&parser, ch);
   }
-  presult msg = finish(&parser);
+  if (msg == 0) msg = finish(&parser);
+  jv value;
   if (msg) {
-    printf("ERROR: %s (parsing '%s')\n", msg, string);
-    return jv_invalid();
+    value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", msg, string));
+  } else {
+    value = jv_copy(parser.next);
   }
-  jv value = jv_copy(parser.next);
   jv_parser_free(&parser);
   return value;
 }
index 1794b9f1f9499a983f842046047941c7830b195d..bb7f57d6ee1eb93e4fef524cbfe1154c66812b8f 100644 (file)
--- a/c/lexer.l
+++ b/c/lexer.l
@@ -1,10 +1,19 @@
 %{
 #include "compile.h"
 #include "parser.tab.h"  /* Generated by bison. */
+
+#define YY_USER_ACTION                           \
+  do {                                           \
+    yylloc->start = yyget_extra(yyscanner);      \
+    yylloc->end = yylloc->start + yyleng;        \
+    yyset_extra(yylloc->end, yyscanner);         \
+  } while (0);
+
 %}
 
 %option noyywrap nounput noinput nodefault
 %option reentrant
+%option extra-type="int"
 %option bison-bridge bison-locations
 
 %%
diff --git a/c/locfile.h b/c/locfile.h
new file mode 100644 (file)
index 0000000..0f605bb
--- /dev/null
@@ -0,0 +1,62 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+typedef struct {
+  int start, end;
+} location;
+
+struct locfile {
+  const char* data;
+  int length;
+  int* linemap;
+  int nlines;
+};
+
+static void locfile_init(struct locfile* l, const char* data, int length) {
+  l->data = data;
+  l->length = length;
+  l->nlines = 1;
+  for (int i=0; i<length; i++) {
+    if (data[i] == '\n') l->nlines++;
+  }
+  l->linemap = malloc(sizeof(int) * (l->nlines + 1));
+  l->linemap[0] = 0;
+  int line = 1;
+  for (int i=0; i<length; i++) {
+    if (data[i] == '\n') {
+      l->linemap[line] = i;
+      line++;
+    }
+  }
+  l->linemap[l->nlines] = length;
+}
+
+static void locfile_free(struct locfile* l) {
+  free(l->linemap);
+}
+
+static int locfile_get_line(struct locfile* l, int pos) {
+  assert(pos < l->length);
+  int line = 0;
+  while (l->linemap[line+1] < pos) line++;
+  assert(line < l->nlines);
+  return line;
+}
+
+static int locfile_line_length(struct locfile* l, int line) {
+  assert(line < l->nlines);
+  return l->linemap[line+1] - l->linemap[line];
+}
+
+static void locfile_locate(struct locfile* l, location loc) {
+  int startline = locfile_get_line(l, loc.start);
+  int offset = l->linemap[startline];
+  printf("%.*s\n", locfile_line_length(l, startline), l->data + offset);
+  printf("%*s", loc.start - offset, "");
+  for (int i = loc.start; 
+       i < loc.end && i < offset + locfile_line_length(l, startline);
+       i++){
+    printf("^");
+  }
+  printf("\n");
+}
index 7c341d8d8c45b7edbcba16b355f1aa155e401764..4faf2a0bfa36397e7c199c108069f68c545fc3d4 100644 (file)
--- a/c/main.c
+++ b/c/main.c
@@ -4,13 +4,36 @@
 #include "builtin.h"
 #include "jv.h"
 
-block compile(const char* str);
+int compile(const char* str, block* answer);
 
 void jq_init(struct bytecode* bc, jv value);
 jv jq_next();
 void jq_teardown();
 
-void run_program(struct bytecode* bc);
+
+void run_program(struct bytecode* bc) {
+#if JQ_DEBUG
+  dump_disassembly(0, bc);
+  printf("\n");
+#endif
+  char buf[409600];
+  fgets(buf, sizeof(buf), stdin);
+  jv value = jv_parse(buf);
+  if (!jv_is_valid(value)) {
+    assert(0 && "couldn't parse input"); //FIXME
+  }
+  jq_init(bc, value);
+  jv result;
+  while (jv_is_valid(result = jq_next())) {
+    jv_dump(result);
+    printf("\n");
+  }
+  jv_free(result);
+  #if JQ_DEBUG
+  printf("end of results\n");
+  #endif
+  jq_teardown();
+}
 
 int skipline(const char* buf) {
   int p = 0;
@@ -29,7 +52,9 @@ void run_tests() {
     if (skipline(buf)) continue;
     printf("Testing %s\n", buf);
     int pass = 1;
-    block program = compile(buf);
+    block program;
+    int nerrors = compile(buf, &program);
+    assert(nerrors == 0);
     block_append(&program, gen_op_simple(YIELD));
     block_append(&program, gen_op_simple(BACKTRACK));
     program = gen_cbinding(&builtins, program);
@@ -86,11 +111,17 @@ void run_tests() {
 
 int main(int argc, char* argv[]) {
   if (argc == 1) { run_tests(); return 0; }
-  block blk = compile(argv[1]);
+  block blk;
+  int nerrors = compile(argv[1], &blk);
+  if (nerrors > 0) {
+    printf("%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error");
+    return 1;
+  }
   block_append(&blk, block_join(gen_op_simple(YIELD), gen_op_simple(BACKTRACK)));
   blk = gen_cbinding(&builtins, blk);
   struct bytecode* bc = block_compile(blk);
   block_free(blk);
   run_program(bc);
   bytecode_free(bc);
+  return 0;
 }
index 0f1e42c44525c9739eb148ea084e57e3280f3cbd..11b88a7f60b42aef4f1a43e1c5d68a258ecff00c 100644 (file)
@@ -4,21 +4,40 @@
 #include "compile.h"
 %}
 
+%code requires {
+#include "locfile.h"
+#define YYLTYPE location
+#define YYLLOC_DEFAULT(Loc, Rhs, N)             \
+  do {                                          \
+    if (N) {                                    \
+      (Loc).start = YYRHSLOC(Rhs, 1).start;     \
+      (Loc).end = YYRHSLOC(Rhs, N).end;         \
+    } else {                                    \
+      (Loc).start = YYRHSLOC(Rhs, 0).end;       \
+      (Loc).end = YYRHSLOC(Rhs, 0).end;         \
+    }                                           \
+  } while (0)
+ }
+
 %locations
+%error-verbose
 %define api.pure
 %union {
   jv literal;
   block blk;
 }
 
+%destructor { jv_free($$); } <literal>
+%destructor { block_free($$); } <blk>
+
 %parse-param {block* answer}
+%parse-param {int* errors}
+%parse-param {struct locfile* locations}
 %parse-param {yyscan_t lexer}
 %lex-param {yyscan_t lexer}
 
-
 %token <literal> IDENT
 %token <literal> LITERAL
-
 %token EQ "=="
 %token DEFINEDOR "//"
 %token AS "as"
 %type <blk> Exp Term MkDict MkDictPair ExpD ElseBody
 %{
 #include "lexer.yy.h"
-void yyerror(YYLTYPE* loc, block* answer, yyscan_t lexer, const char *s){
-  printf("ERROR: %s\n", s); 
+#define FAIL(loc, msg)                                   \
+  do {                                                   \
+    location l = loc;                                    \
+    yyerror(&l, answer, errors, locations, lexer, msg);  \
+    /*YYERROR*/;                                         \
+  } while (0)
+
+void yyerror(YYLTYPE* loc, block* answer, int* errors, 
+             struct locfile* locations, yyscan_t lexer, const char *s){
+  (*errors)++;
+  printf("error: %s\n", s);
+  locfile_locate(locations, *loc);
 }
 
 static block gen_dictpair(block k, block v) {
@@ -263,7 +292,9 @@ MkDictPair
   $$ = gen_dictpair(gen_op_const(LOADK, $1), $3);
  }
 | LITERAL ':' ExpD {
-  assert(jv_get_kind($1) == JV_KIND_STRING);
+  if (jv_get_kind($1) != JV_KIND_STRING) {
+    FAIL(@1, "Object keys must be strings");
+  }
   $$ = gen_dictpair(gen_op_const(LOADK, $1), $3);
   }
 | IDENT {
@@ -275,14 +306,22 @@ MkDictPair
   }
 %%
 
-block compile(const char* str) {
+int compile(const char* str, block* answer) {
   yyscan_t scanner;
   YY_BUFFER_STATE buf;
-  block answer = gen_noop();
-  yylex_init(&scanner);
+  yylex_init_extra(0, &scanner);
   buf = yy_scan_string(str, scanner);
-  yyparse(&answer, scanner);
+  int errors = 0;
+  struct locfile locations;
+  locfile_init(&locations, str, strlen(str));
+  *answer = gen_noop();
+  yyparse(answer, &errors, &locations, scanner);
+  locfile_free(&locations);
   yy_delete_buffer(buf, scanner);
   yylex_destroy(scanner);
-  return answer;
+  if (errors > 0) {
+    block_free(*answer);
+    *answer = gen_noop();
+  }
+  return errors;
 }