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");
|
||||
if (cJSON_IsString(fname))
|
||||
filename = fname->valuestring;
|
||||
int prev_line = -1;
|
||||
const char *prev_msg = NULL;
|
||||
cJSON *e;
|
||||
cJSON_ArrayForEach(e, errors) {
|
||||
const char *msg = cJSON_GetStringValue(
|
||||
cJSON_GetObjectItemCaseSensitive(e, "message"));
|
||||
cJSON *line = cJSON_GetObjectItemCaseSensitive(e, "line");
|
||||
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))
|
||||
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
||||
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;
|
||||
cJSON *errors; /* array of error objects */
|
||||
int has_error;
|
||||
int error_count;
|
||||
int in_disruption;
|
||||
char *decoded_str; /* allocated buffer for decoded string escapes */
|
||||
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, ...) {
|
||||
if (s->error_count >= 5) return;
|
||||
s->error_count++;
|
||||
|
||||
va_list ap;
|
||||
char buf[256];
|
||||
int line, col;
|
||||
@@ -29846,11 +29850,11 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
|
||||
default:
|
||||
/* Report syntax error with token info */
|
||||
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) {
|
||||
ast_error (s, start, "unexpected end of input");
|
||||
} else {
|
||||
ast_error (s, start, "unexpected token");
|
||||
ast_error (s, start, "unexpected token (keyword or operator) where expression expected");
|
||||
}
|
||||
ast_next_token (s);
|
||||
return NULL;
|
||||
@@ -30689,7 +30693,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
|
||||
cJSON_AddItemToObject (node, "expression", expr);
|
||||
ast_node_end (s, node, s->buf_ptr);
|
||||
} else {
|
||||
ast_error (s, start, "unexpected token");
|
||||
ast_error (s, start, "unexpected token at start of statement");
|
||||
return NULL; /* caller syncs */
|
||||
}
|
||||
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"));
|
||||
if (pname) ast_sem_add_var (&fn_scope, pname, 1, "input", fn_nr);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
if (pname) ast_sem_add_var (&fn_scope, pname, 1, "input", fn_nr);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@@ -31503,32 +31507,37 @@ cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Run semantic pass */
|
||||
cJSON *sem_errors = NULL;
|
||||
cJSON *scopes = NULL;
|
||||
cJSON *intrinsics = NULL;
|
||||
ast_semantic_check (ast, &sem_errors, &scopes, &intrinsics);
|
||||
/* Run semantic pass - only if parsing succeeded without errors */
|
||||
if (!s.has_error) {
|
||||
cJSON *sem_errors = NULL;
|
||||
cJSON *scopes = NULL;
|
||||
cJSON *intrinsics = NULL;
|
||||
ast_semantic_check (ast, &sem_errors, &scopes, &intrinsics);
|
||||
|
||||
/* Attach scopes and intrinsics to AST */
|
||||
if (scopes) cJSON_AddItemToObject (ast, "scopes", scopes);
|
||||
if (intrinsics) cJSON_AddItemToObject (ast, "intrinsics", intrinsics);
|
||||
/* Attach scopes and intrinsics to AST */
|
||||
if (scopes) cJSON_AddItemToObject (ast, "scopes", scopes);
|
||||
if (intrinsics) cJSON_AddItemToObject (ast, "intrinsics", intrinsics);
|
||||
|
||||
/* Merge parse errors and semantic errors */
|
||||
if (s.errors && sem_errors) {
|
||||
/* Append semantic errors to parse errors */
|
||||
cJSON *err;
|
||||
cJSON *next;
|
||||
for (err = sem_errors->child; err; err = next) {
|
||||
next = err->next;
|
||||
cJSON_DetachItemViaPointer (sem_errors, err);
|
||||
cJSON_AddItemToArray (s.errors, err);
|
||||
if (sem_errors) {
|
||||
cJSON_AddItemToObject (ast, "errors", sem_errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (s.errors) {
|
||||
cJSON *existing = cJSON_GetObjectItemCaseSensitive (ast, "errors");
|
||||
if (existing) {
|
||||
/* 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;
|
||||
@@ -32829,6 +32838,28 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
|
||||
/* Scan scope record for var/def declarations */
|
||||
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 */
|
||||
cJSON *body = cJSON_GetObjectItemCaseSensitive(node, "body");
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (strcmp (kind, "continue") == 0) {
|
||||
if (s->loop_continue) mach_gen_emit_jump (s, s->loop_continue);
|
||||
else mach_gen_error (s, stmt, "'continue' used outside of loop");
|
||||
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;
|
||||
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) */
|
||||
mach_gen_load_intrinsics (&s, cJSON_GetObjectItemCaseSensitive (func_node, "intrinsics"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user