More error handling - locations of refs to undefined symbols.
This commit is contained in:
1 parent
4c7bede5a4
commit
062a6aa6d7
5 files changed
+90
-43
No files matched your search
+32
-8
@@ -3,7 +3,7 @@
|
||||
#include <stdlib.h>
|
||||
#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) {
|
||||
|
||||
+4
-1
@@ -1,6 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#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);
|
||||
|
||||
|
||||
+21
-5
@@ -1,10 +1,15 @@
|
||||
#ifndef _LOCFILE_H
|
||||
#define _LOCFILE_H
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
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, "<unknown location>\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
|
||||
@@ -1,15 +1,36 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
|
||||
+7
-10
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user