better parser error reporting
This commit is contained in:
@@ -114,12 +114,19 @@ static int print_tree_errors(cJSON *root) {
|
|||||||
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
|
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
|
||||||
if (cJSON_IsString(fname))
|
if (cJSON_IsString(fname))
|
||||||
filename = fname->valuestring;
|
filename = fname->valuestring;
|
||||||
|
int prev_line = -1;
|
||||||
|
const char *prev_msg = NULL;
|
||||||
cJSON *e;
|
cJSON *e;
|
||||||
cJSON_ArrayForEach(e, errors) {
|
cJSON_ArrayForEach(e, errors) {
|
||||||
const char *msg = cJSON_GetStringValue(
|
const char *msg = cJSON_GetStringValue(
|
||||||
cJSON_GetObjectItemCaseSensitive(e, "message"));
|
cJSON_GetObjectItemCaseSensitive(e, "message"));
|
||||||
cJSON *line = cJSON_GetObjectItemCaseSensitive(e, "line");
|
cJSON *line = cJSON_GetObjectItemCaseSensitive(e, "line");
|
||||||
cJSON *col = cJSON_GetObjectItemCaseSensitive(e, "column");
|
cJSON *col = cJSON_GetObjectItemCaseSensitive(e, "column");
|
||||||
|
int cur_line = cJSON_IsNumber(line) ? (int)line->valuedouble : -1;
|
||||||
|
if (prev_msg && msg && cur_line == prev_line && strcmp(msg, prev_msg) == 0)
|
||||||
|
continue;
|
||||||
|
prev_line = cur_line;
|
||||||
|
prev_msg = msg;
|
||||||
if (msg && cJSON_IsNumber(line) && cJSON_IsNumber(col))
|
if (msg && cJSON_IsNumber(line) && cJSON_IsNumber(col))
|
||||||
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
||||||
filename, (int)line->valuedouble, (int)col->valuedouble, msg);
|
filename, (int)line->valuedouble, (int)col->valuedouble, msg);
|
||||||
|
|||||||
107
source/quickjs.c
107
source/quickjs.c
@@ -28290,6 +28290,7 @@ typedef struct ASTParseState {
|
|||||||
int function_nr;
|
int function_nr;
|
||||||
cJSON *errors; /* array of error objects */
|
cJSON *errors; /* array of error objects */
|
||||||
int has_error;
|
int has_error;
|
||||||
|
int error_count;
|
||||||
int in_disruption;
|
int in_disruption;
|
||||||
char *decoded_str; /* allocated buffer for decoded string escapes */
|
char *decoded_str; /* allocated buffer for decoded string escapes */
|
||||||
GetLineColCache lc_cache;
|
GetLineColCache lc_cache;
|
||||||
@@ -28396,6 +28397,9 @@ static void ast_node_end (ASTParseState *s, cJSON *node, const uint8_t *end_ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void ast_error (ASTParseState *s, const uint8_t *ptr, const char *fmt, ...) {
|
static void ast_error (ASTParseState *s, const uint8_t *ptr, const char *fmt, ...) {
|
||||||
|
if (s->error_count >= 5) return;
|
||||||
|
s->error_count++;
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
int line, col;
|
int line, col;
|
||||||
@@ -29846,11 +29850,11 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
|
|||||||
default:
|
default:
|
||||||
/* Report syntax error with token info */
|
/* Report syntax error with token info */
|
||||||
if (s->token_val >= 32 && s->token_val < 127) {
|
if (s->token_val >= 32 && s->token_val < 127) {
|
||||||
ast_error (s, start, "unexpected token '%c'", s->token_val);
|
ast_error (s, start, "unexpected '%c' in expression", s->token_val);
|
||||||
} else if (s->token_val == TOK_EOF) {
|
} else if (s->token_val == TOK_EOF) {
|
||||||
ast_error (s, start, "unexpected end of input");
|
ast_error (s, start, "unexpected end of input");
|
||||||
} else {
|
} else {
|
||||||
ast_error (s, start, "unexpected token");
|
ast_error (s, start, "unexpected token (keyword or operator) where expression expected");
|
||||||
}
|
}
|
||||||
ast_next_token (s);
|
ast_next_token (s);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -30689,7 +30693,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
|
|||||||
cJSON_AddItemToObject (node, "expression", expr);
|
cJSON_AddItemToObject (node, "expression", expr);
|
||||||
ast_node_end (s, node, s->buf_ptr);
|
ast_node_end (s, node, s->buf_ptr);
|
||||||
} else {
|
} else {
|
||||||
ast_error (s, start, "unexpected token");
|
ast_error (s, start, "unexpected token at start of statement");
|
||||||
return NULL; /* caller syncs */
|
return NULL; /* caller syncs */
|
||||||
}
|
}
|
||||||
ast_expect_semi (s);
|
ast_expect_semi (s);
|
||||||
@@ -31105,7 +31109,7 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr
|
|||||||
const char *pname = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (param, "name"));
|
const char *pname = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (param, "name"));
|
||||||
if (pname) ast_sem_add_var (&fn_scope, pname, 1, "input", fn_nr);
|
if (pname) ast_sem_add_var (&fn_scope, pname, 1, "input", fn_nr);
|
||||||
/* Check default value expressions */
|
/* Check default value expressions */
|
||||||
cJSON *def_val = cJSON_GetObjectItemCaseSensitive (param, "default");
|
cJSON *def_val = cJSON_GetObjectItemCaseSensitive (param, "expression");
|
||||||
if (def_val) ast_sem_check_expr (st, &fn_scope, def_val);
|
if (def_val) ast_sem_check_expr (st, &fn_scope, def_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31398,7 +31402,7 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
const char *pname = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (param, "name"));
|
const char *pname = cJSON_GetStringValue (cJSON_GetObjectItemCaseSensitive (param, "name"));
|
||||||
if (pname) ast_sem_add_var (&fn_scope, pname, 1, "input", fn_nr);
|
if (pname) ast_sem_add_var (&fn_scope, pname, 1, "input", fn_nr);
|
||||||
/* Check default value expressions */
|
/* Check default value expressions */
|
||||||
cJSON *def_val = cJSON_GetObjectItemCaseSensitive (param, "default");
|
cJSON *def_val = cJSON_GetObjectItemCaseSensitive (param, "expression");
|
||||||
if (def_val) ast_sem_check_expr (st, &fn_scope, def_val);
|
if (def_val) ast_sem_check_expr (st, &fn_scope, def_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31503,32 +31507,37 @@ cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run semantic pass */
|
/* Run semantic pass - only if parsing succeeded without errors */
|
||||||
cJSON *sem_errors = NULL;
|
if (!s.has_error) {
|
||||||
cJSON *scopes = NULL;
|
cJSON *sem_errors = NULL;
|
||||||
cJSON *intrinsics = NULL;
|
cJSON *scopes = NULL;
|
||||||
ast_semantic_check (ast, &sem_errors, &scopes, &intrinsics);
|
cJSON *intrinsics = NULL;
|
||||||
|
ast_semantic_check (ast, &sem_errors, &scopes, &intrinsics);
|
||||||
|
|
||||||
/* Attach scopes and intrinsics to AST */
|
/* Attach scopes and intrinsics to AST */
|
||||||
if (scopes) cJSON_AddItemToObject (ast, "scopes", scopes);
|
if (scopes) cJSON_AddItemToObject (ast, "scopes", scopes);
|
||||||
if (intrinsics) cJSON_AddItemToObject (ast, "intrinsics", intrinsics);
|
if (intrinsics) cJSON_AddItemToObject (ast, "intrinsics", intrinsics);
|
||||||
|
|
||||||
/* Merge parse errors and semantic errors */
|
if (sem_errors) {
|
||||||
if (s.errors && sem_errors) {
|
cJSON_AddItemToObject (ast, "errors", sem_errors);
|
||||||
/* Append semantic errors to parse errors */
|
}
|
||||||
cJSON *err;
|
}
|
||||||
cJSON *next;
|
|
||||||
for (err = sem_errors->child; err; err = next) {
|
if (s.errors) {
|
||||||
next = err->next;
|
cJSON *existing = cJSON_GetObjectItemCaseSensitive (ast, "errors");
|
||||||
cJSON_DetachItemViaPointer (sem_errors, err);
|
if (existing) {
|
||||||
cJSON_AddItemToArray (s.errors, err);
|
/* Append parse errors to existing semantic errors */
|
||||||
|
cJSON *err;
|
||||||
|
cJSON *next;
|
||||||
|
for (err = s.errors->child; err; err = next) {
|
||||||
|
next = err->next;
|
||||||
|
cJSON_DetachItemViaPointer (s.errors, err);
|
||||||
|
cJSON_AddItemToArray (existing, err);
|
||||||
|
}
|
||||||
|
cJSON_Delete (s.errors);
|
||||||
|
} else {
|
||||||
|
cJSON_AddItemToObject (ast, "errors", s.errors);
|
||||||
}
|
}
|
||||||
cJSON_Delete (sem_errors);
|
|
||||||
cJSON_AddItemToObject (ast, "errors", s.errors);
|
|
||||||
} else if (s.errors) {
|
|
||||||
cJSON_AddItemToObject (ast, "errors", s.errors);
|
|
||||||
} else if (sem_errors) {
|
|
||||||
cJSON_AddItemToObject (ast, "errors", sem_errors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ast;
|
return ast;
|
||||||
@@ -32829,6 +32838,28 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
|
|||||||
/* Scan scope record for var/def declarations */
|
/* Scan scope record for var/def declarations */
|
||||||
mach_scan_scope(&child);
|
mach_scan_scope(&child);
|
||||||
|
|
||||||
|
/* Emit default parameter initialization */
|
||||||
|
for (int i = 0; i < nparams; i++) {
|
||||||
|
cJSON *p = cJSON_GetArrayItem(params, i);
|
||||||
|
cJSON *default_expr = cJSON_GetObjectItemCaseSensitive(p, "expression");
|
||||||
|
if (default_expr) {
|
||||||
|
int slot = 1 + i; /* param slots start at 1 (slot 0 = this) */
|
||||||
|
/* If param is null, skip the JMP and fall into default code */
|
||||||
|
mach_emit(&child, MACH_AsBx(MACH_JMPNULL, slot, 1));
|
||||||
|
/* If param is NOT null, jump past the default code */
|
||||||
|
int jmp_pc = mach_current_pc(&child);
|
||||||
|
mach_emit(&child, MACH_sJ(MACH_JMP, 0)); /* placeholder */
|
||||||
|
|
||||||
|
int save = child.freereg;
|
||||||
|
mach_compile_expr(&child, default_expr, slot);
|
||||||
|
child.freereg = save;
|
||||||
|
|
||||||
|
/* Patch JMP offset */
|
||||||
|
int offset = mach_current_pc(&child) - (jmp_pc + 1);
|
||||||
|
child.code[jmp_pc] = MACH_sJ(MACH_JMP, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Compile body */
|
/* Compile body */
|
||||||
cJSON *body = cJSON_GetObjectItemCaseSensitive(node, "body");
|
cJSON *body = cJSON_GetObjectItemCaseSensitive(node, "body");
|
||||||
if (!body) body = node; /* statements may be directly on the function node */
|
if (!body) body = node; /* statements may be directly on the function node */
|
||||||
@@ -35922,13 +35953,11 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
|
|||||||
|
|
||||||
if (strcmp (kind, "break") == 0) {
|
if (strcmp (kind, "break") == 0) {
|
||||||
if (s->loop_break) mach_gen_emit_jump (s, s->loop_break);
|
if (s->loop_break) mach_gen_emit_jump (s, s->loop_break);
|
||||||
else mach_gen_error (s, stmt, "'break' used outside of loop or switch");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp (kind, "continue") == 0) {
|
if (strcmp (kind, "continue") == 0) {
|
||||||
if (s->loop_continue) mach_gen_emit_jump (s, s->loop_continue);
|
if (s->loop_continue) mach_gen_emit_jump (s, s->loop_continue);
|
||||||
else mach_gen_error (s, stmt, "'continue' used outside of loop");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36070,6 +36099,24 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
|
|||||||
s.next_temp_slot = 1 + s.nr_args + s.nr_local_slots;
|
s.next_temp_slot = 1 + s.nr_args + s.nr_local_slots;
|
||||||
if (s.next_temp_slot > s.max_slot) s.max_slot = s.next_temp_slot;
|
if (s.next_temp_slot > s.max_slot) s.max_slot = s.next_temp_slot;
|
||||||
|
|
||||||
|
/* Emit default parameter initialization */
|
||||||
|
{
|
||||||
|
int ps = 1;
|
||||||
|
cJSON *pp;
|
||||||
|
cJSON_ArrayForEach(pp, params) {
|
||||||
|
cJSON *default_expr = cJSON_GetObjectItemCaseSensitive(pp, "expression");
|
||||||
|
if (default_expr) {
|
||||||
|
char *end_label = mach_gen_label(&s, "default_end");
|
||||||
|
mach_gen_emit_jump_cond(&s, "jump_not_null", ps, end_label);
|
||||||
|
int default_slot = mach_gen_expr(&s, default_expr, -1);
|
||||||
|
mach_gen_emit_2(&s, "move", ps, default_slot);
|
||||||
|
mach_gen_emit_label(&s, end_label);
|
||||||
|
sys_free(end_label);
|
||||||
|
}
|
||||||
|
ps++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Pre-load intrinsics (global names) */
|
/* Pre-load intrinsics (global names) */
|
||||||
mach_gen_load_intrinsics (&s, cJSON_GetObjectItemCaseSensitive (func_node, "intrinsics"));
|
mach_gen_load_intrinsics (&s, cJSON_GetObjectItemCaseSensitive (func_node, "intrinsics"));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user