Much, much better error reporting from the parser.
Getting proper locations through flex/bison was more effort than was advertised.
This commit is contained in:
1 parent
4d47f8f1e1
commit
95dd64b0f0
6 files changed
+164
-45
No files matched your search
-20
@@ -470,23 +470,3 @@ void jq_teardown() {
|
|||||||
pathbuf = 0;
|
pathbuf = 0;
|
||||||
pathsize = 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();
|
|
||||||
}
|
|
||||||
+9
-11
@@ -331,20 +331,18 @@ jv jv_parse_sized(const char* string, int length) {
|
|||||||
|
|
||||||
const char* p = string;
|
const char* p = string;
|
||||||
char ch;
|
char ch;
|
||||||
while (p < string + length) {
|
presult msg = 0;
|
||||||
|
while (msg == 0 && p < string + length) {
|
||||||
ch = *p++;
|
ch = *p++;
|
||||||
presult msg = scan(&parser, ch);
|
msg = scan(&parser, ch);
|
||||||
|
}
|
||||||
|
if (msg == 0) msg = finish(&parser);
|
||||||
|
jv value;
|
||||||
if (msg) {
|
if (msg) {
|
||||||
printf("ERROR: %s (parsing '%s')\n", msg, string);
|
value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", msg, string));
|
||||||
return jv_invalid();
|
} else {
|
||||||
|
value = jv_copy(parser.next);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
presult msg = finish(&parser);
|
|
||||||
if (msg) {
|
|
||||||
printf("ERROR: %s (parsing '%s')\n", msg, string);
|
|
||||||
return jv_invalid();
|
|
||||||
}
|
|
||||||
jv value = jv_copy(parser.next);
|
|
||||||
jv_parser_free(&parser);
|
jv_parser_free(&parser);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
%{
|
%{
|
||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
#include "parser.tab.h" /* Generated by bison. */
|
#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 noyywrap nounput noinput nodefault
|
||||||
%option reentrant
|
%option reentrant
|
||||||
|
%option extra-type="int"
|
||||||
%option bison-bridge bison-locations
|
%option bison-bridge bison-locations
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|||||||
+62
@@ -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");
|
||||||
|
}
|
||||||
@@ -4,13 +4,36 @@
|
|||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
#include "jv.h"
|
#include "jv.h"
|
||||||
|
|
||||||
block compile(const char* str);
|
int compile(const char* str, block* answer);
|
||||||
|
|
||||||
void jq_init(struct bytecode* bc, jv value);
|
void jq_init(struct bytecode* bc, jv value);
|
||||||
jv jq_next();
|
jv jq_next();
|
||||||
void jq_teardown();
|
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 skipline(const char* buf) {
|
||||||
int p = 0;
|
int p = 0;
|
||||||
@@ -29,7 +52,9 @@ void run_tests() {
|
|||||||
if (skipline(buf)) continue;
|
if (skipline(buf)) continue;
|
||||||
printf("Testing %s\n", buf);
|
printf("Testing %s\n", buf);
|
||||||
int pass = 1;
|
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(YIELD));
|
||||||
block_append(&program, gen_op_simple(BACKTRACK));
|
block_append(&program, gen_op_simple(BACKTRACK));
|
||||||
program = gen_cbinding(&builtins, program);
|
program = gen_cbinding(&builtins, program);
|
||||||
@@ -86,11 +111,17 @@ void run_tests() {
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc == 1) { run_tests(); return 0; }
|
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)));
|
block_append(&blk, block_join(gen_op_simple(YIELD), gen_op_simple(BACKTRACK)));
|
||||||
blk = gen_cbinding(&builtins, blk);
|
blk = gen_cbinding(&builtins, blk);
|
||||||
struct bytecode* bc = block_compile(blk);
|
struct bytecode* bc = block_compile(blk);
|
||||||
block_free(blk);
|
block_free(blk);
|
||||||
run_program(bc);
|
run_program(bc);
|
||||||
bytecode_free(bc);
|
bytecode_free(bc);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
+49
-10
@@ -4,21 +4,40 @@
|
|||||||
#include "compile.h"
|
#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
|
%locations
|
||||||
|
%error-verbose
|
||||||
%define api.pure
|
%define api.pure
|
||||||
%union {
|
%union {
|
||||||
jv literal;
|
jv literal;
|
||||||
block blk;
|
block blk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%destructor { jv_free($$); } <literal>
|
||||||
|
%destructor { block_free($$); } <blk>
|
||||||
|
|
||||||
%parse-param {block* answer}
|
%parse-param {block* answer}
|
||||||
|
%parse-param {int* errors}
|
||||||
|
%parse-param {struct locfile* locations}
|
||||||
%parse-param {yyscan_t lexer}
|
%parse-param {yyscan_t lexer}
|
||||||
%lex-param {yyscan_t lexer}
|
%lex-param {yyscan_t lexer}
|
||||||
|
|
||||||
|
|
||||||
%token <literal> IDENT
|
%token <literal> IDENT
|
||||||
%token <literal> LITERAL
|
%token <literal> LITERAL
|
||||||
|
|
||||||
%token EQ "=="
|
%token EQ "=="
|
||||||
%token DEFINEDOR "//"
|
%token DEFINEDOR "//"
|
||||||
%token AS "as"
|
%token AS "as"
|
||||||
@@ -50,8 +69,18 @@
|
|||||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody
|
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody
|
||||||
%{
|
%{
|
||||||
#include "lexer.yy.h"
|
#include "lexer.yy.h"
|
||||||
void yyerror(YYLTYPE* loc, block* answer, yyscan_t lexer, const char *s){
|
#define FAIL(loc, msg) \
|
||||||
printf("ERROR: %s\n", s);
|
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) {
|
static block gen_dictpair(block k, block v) {
|
||||||
@@ -263,7 +292,9 @@ MkDictPair
|
|||||||
$$ = gen_dictpair(gen_op_const(LOADK, $1), $3);
|
$$ = gen_dictpair(gen_op_const(LOADK, $1), $3);
|
||||||
}
|
}
|
||||||
| LITERAL ':' ExpD {
|
| 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);
|
$$ = gen_dictpair(gen_op_const(LOADK, $1), $3);
|
||||||
}
|
}
|
||||||
| IDENT {
|
| IDENT {
|
||||||
@@ -275,14 +306,22 @@ MkDictPair
|
|||||||
}
|
}
|
||||||
%%
|
%%
|
||||||
|
|
||||||
block compile(const char* str) {
|
int compile(const char* str, block* answer) {
|
||||||
yyscan_t scanner;
|
yyscan_t scanner;
|
||||||
YY_BUFFER_STATE buf;
|
YY_BUFFER_STATE buf;
|
||||||
block answer = gen_noop();
|
yylex_init_extra(0, &scanner);
|
||||||
yylex_init(&scanner);
|
|
||||||
buf = yy_scan_string(str, 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);
|
yy_delete_buffer(buf, scanner);
|
||||||
yylex_destroy(scanner);
|
yylex_destroy(scanner);
|
||||||
return answer;
|
if (errors > 0) {
|
||||||
|
block_free(*answer);
|
||||||
|
*answer = gen_noop();
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user