better parser error reporting

This commit is contained in:
2026-02-08 00:23:47 -06:00
parent 2f6700415e
commit a3622bd5bd
2 changed files with 84 additions and 30 deletions

View File

@@ -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);

View File

@@ -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,7 +31507,8 @@ cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) {
return NULL;
}
/* Run semantic pass */
/* Run semantic pass - only if parsing succeeded without errors */
if (!s.has_error) {
cJSON *sem_errors = NULL;
cJSON *scopes = NULL;
cJSON *intrinsics = NULL;
@@ -31513,22 +31518,26 @@ cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) {
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 */
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 = sem_errors->child; err; err = next) {
for (err = s.errors->child; err; err = next) {
next = err->next;
cJSON_DetachItemViaPointer (sem_errors, err);
cJSON_AddItemToArray (s.errors, err);
cJSON_DetachItemViaPointer (s.errors, err);
cJSON_AddItemToArray (existing, err);
}
cJSON_Delete (sem_errors);
cJSON_Delete (s.errors);
} else {
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"));