From 062a6aa6d7b9c8ead18cc9cbbe0f458a7e2b7db9 Mon Sep 17 00:00:00 2001 From: Stephen Dolan Date: Tue, 11 Sep 2012 12:24:54 +0100 Subject: [PATCH] More error handling - locations of refs to undefined symbols. --- c/compile.c | 40 ++++++++++++++++++++++++++++++++-------- c/compile.h | 5 ++++- c/locfile.h | 26 +++++++++++++++++++++----- c/main.c | 45 ++++++++++++++++++++++++++------------------- c/parser.y | 17 +++++++---------- 5 files changed, 90 insertions(+), 43 deletions(-) diff --git a/c/compile.c b/c/compile.c index b7f91aa3..c45d45a1 100644 --- a/c/compile.c +++ b/c/compile.c @@ -3,7 +3,7 @@ #include #include "opcode.h" #include "compile.h" - +#include "locfile.h" struct inst { struct inst* next; @@ -18,6 +18,8 @@ struct inst { struct cfunction* cfunc; } imm; + location source; + // Binding // An instruction requiring binding (for parameters/variables) // is in one of three states: @@ -45,6 +47,7 @@ static inst* inst_new(opcode op) { i->bound_by = 0; i->symbol = 0; i->subfn = gen_noop(); + i->source = UNKNOWN_LOCATION; return i; } @@ -82,6 +85,16 @@ static inst* block_take(block* b) { return i; } +block gen_location(location loc, block b) { + for (inst* i = b.first; i; i = i->next) { + if (i->source.start == UNKNOWN_LOCATION.start && + i->source.end == UNKNOWN_LOCATION.end) { + i->source = loc; + } + } + return b; +} + block gen_noop() { block b = {0,0}; return b; @@ -429,7 +442,8 @@ static int count_cfunctions(block b) { return n; } -static void compile(struct bytecode* bc, block b) { +static int compile(struct locfile* locations, struct bytecode* bc, block b) { + int errors = 0; int pos = 0; int var_frame_idx = 0; bc->nsubfunctions = 0; @@ -442,7 +456,10 @@ static void compile(struct bytecode* bc, block b) { int opflags = opcode_describe(curr->op)->flags; if (opflags & OP_HAS_BINDING) { - assert(curr->bound_by && "unbound term"); + if (!curr->bound_by) { + locfile_locate(locations, curr->source, "error: %s is not defined", curr->symbol); + errors++; + } } if ((opflags & OP_HAS_VARIABLE) && curr->bound_by == curr) { @@ -472,7 +489,7 @@ static void compile(struct bytecode* bc, block b) { bc->subfunctions[curr->imm.intval] = subfn; subfn->globals = bc->globals; subfn->parent = bc; - compile(subfn, curr->subfn); + errors += compile(locations, subfn, curr->subfn); } } else { bc->subfunctions = 0; @@ -483,7 +500,7 @@ static void compile(struct bytecode* bc, block b) { pos = 0; jv constant_pool = jv_array(); int maxvar = -1; - for (inst* curr = b.first; curr; curr = curr->next) { + if (!errors) for (inst* curr = b.first; curr; curr = curr->next) { const struct opcode_description* op = opcode_describe(curr->op); if (op->length == 0) continue; @@ -551,18 +568,25 @@ static void compile(struct bytecode* bc, block b) { } bc->constants = constant_pool; bc->nlocals = maxvar + 2; // FIXME: frames of size zero? + return errors; } -struct bytecode* block_compile(block b) { +int block_compile(block b, struct locfile* locations, struct bytecode** out) { struct bytecode* bc = malloc(sizeof(struct bytecode)); bc->parent = 0; bc->globals = malloc(sizeof(struct symbol_table)); int ncfunc = count_cfunctions(b); bc->globals->ncfunctions = 0; bc->globals->cfunctions = malloc(sizeof(struct cfunction) * ncfunc); - compile(bc, b); + int nerrors = compile(locations, bc, b); assert(bc->globals->ncfunctions == ncfunc); - return bc; + if (nerrors > 0) { + bytecode_free(bc); + *out = 0; + } else { + *out = bc; + } + return nerrors; } void block_free(block b) { diff --git a/c/compile.h b/c/compile.h index 8f9526b0..bdaf6a88 100644 --- a/c/compile.h +++ b/c/compile.h @@ -1,6 +1,7 @@ #include #include "bytecode.h" #include "opcode.h" +#include "locfile.h" struct inst; typedef struct inst inst; @@ -11,6 +12,8 @@ typedef struct block { inst* last; } block; +block gen_location(location, block); + block gen_noop(); block gen_op_simple(opcode op); block gen_op_const(opcode op, jv constant); @@ -39,7 +42,7 @@ void block_append(block* b, block b2); block block_join(block a, block b); block block_bind(block binder, block body, int bindflags); -struct bytecode* block_compile(block); +int block_compile(block, struct locfile*, struct bytecode**); void block_free(block); diff --git a/c/locfile.h b/c/locfile.h index 0f605bbb..77b1aef1 100644 --- a/c/locfile.h +++ b/c/locfile.h @@ -1,10 +1,15 @@ +#ifndef _LOCFILE_H +#define _LOCFILE_H #include #include #include +#include typedef struct { int start, end; } location; +static const location UNKNOWN_LOCATION = {-1, -1}; + struct locfile { const char* data; int length; @@ -48,15 +53,26 @@ static int locfile_line_length(struct locfile* l, int line) { return l->linemap[line+1] - l->linemap[line]; } -static void locfile_locate(struct locfile* l, location loc) { +static void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) { + va_list fmtargs; + va_start(fmtargs, fmt); + vfprintf(stderr, fmt, fmtargs); + va_end(fmtargs); + fprintf(stderr, "\n"); + if (loc.start == -1) { + fprintf(stderr, "\n"); + return; + } 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, ""); + fprintf(stderr, "%.*s\n", locfile_line_length(l, startline), l->data + offset); + fprintf(stderr, "%*s", loc.start - offset, ""); for (int i = loc.start; i < loc.end && i < offset + locfile_line_length(l, startline); i++){ - printf("^"); + fprintf(stderr, "^"); } - printf("\n"); + fprintf(stderr, "\n"); } + +#endif diff --git a/c/main.c b/c/main.c index 4faf2a0b..f6d9e67f 100644 --- a/c/main.c +++ b/c/main.c @@ -1,15 +1,36 @@ #include +#include #include "compile.h" #include "parser.tab.h" #include "builtin.h" #include "jv.h" +#include "locfile.h" -int compile(const char* str, block* answer); +int jq_parse(struct locfile* source, block* answer); void jq_init(struct bytecode* bc, jv value); jv jq_next(); void jq_teardown(); +struct bytecode* jq_compile(const char* str) { + struct locfile locations; + locfile_init(&locations, str, strlen(str)); + block program; + struct bytecode* bc = 0; + int nerrors = jq_parse(&locations, &program); + if (nerrors == 0) { + block_append(&program, block_join(gen_op_simple(YIELD), gen_op_simple(BACKTRACK))); + program = gen_cbinding(&builtins, program); + nerrors = block_compile(program, &locations, &bc); + block_free(program); + } + if (nerrors) { + fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error"); + } + locfile_free(&locations); + return bc; +} + void run_program(struct bytecode* bc) { #if JQ_DEBUG @@ -52,14 +73,8 @@ void run_tests() { if (skipline(buf)) continue; printf("Testing %s\n", buf); int pass = 1; - 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); - struct bytecode* bc = block_compile(program); - block_free(program); + struct bytecode* bc = jq_compile(buf); + assert(bc); printf("Disassembly:\n"); dump_disassembly(2, bc); printf("\n"); @@ -111,16 +126,8 @@ void run_tests() { int main(int argc, char* argv[]) { if (argc == 1) { run_tests(); return 0; } - 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); + struct bytecode* bc = jq_compile(argv[1]); + if (!bc) return 1; run_program(bc); bytecode_free(bc); return 0; diff --git a/c/parser.y b/c/parser.y index 111d8ee7..70fb7116 100644 --- a/c/parser.y +++ b/c/parser.y @@ -83,8 +83,7 @@ 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); + locfile_locate(locations, *loc, "error: %s", s); } int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors, @@ -293,11 +292,11 @@ LITERAL { block_append(&$$, gen_op_simple(POP)); } | '$' IDENT { - $$ = gen_op_var_unbound(LOADV, jv_string_value($2)); + $$ = gen_location(@$, gen_op_var_unbound(LOADV, jv_string_value($2))); jv_free($2); } | IDENT { - $$ = gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, jv_string_value($1))); + $$ = gen_location(@$, gen_op_call(CALL_1_1, gen_op_block_unbound(CLOSURE_REF, jv_string_value($1)))); jv_free($1); } | IDENT '(' Exp ')' { @@ -307,6 +306,7 @@ IDENT '(' Exp ')' { "lambda", block_join($3, gen_op_simple(RET))), gen_noop(), OP_IS_CALL_PSEUDO))); + $$ = gen_location(@1, $$); jv_free($1); } | '(' error ')' { $$ = gen_noop(); } | @@ -342,17 +342,14 @@ MkDictPair | '(' error ')' ':' ExpD { $$ = $5; } %% -int compile(const char* str, block* answer) { +int jq_parse(struct locfile* locations, block* answer) { yyscan_t scanner; YY_BUFFER_STATE buf; jq_yylex_init_extra(0, &scanner); - buf = jq_yy_scan_string(str, scanner); + buf = jq_yy_scan_bytes(locations->data, locations->length, scanner); int errors = 0; - struct locfile locations; - locfile_init(&locations, str, strlen(str)); *answer = gen_noop(); - yyparse(answer, &errors, &locations, scanner); - locfile_free(&locations); + yyparse(answer, &errors, locations, scanner); jq_yy_delete_buffer(buf, scanner); jq_yylex_destroy(scanner); if (errors > 0) {