fix labeled loops, do-while, shorthand property syntax, and added more tests

This commit is contained in:
2026-02-10 10:32:54 -06:00
parent bd577712d9
commit 4e5f1d8faa
5 changed files with 559 additions and 1 deletions

View File

@@ -93,6 +93,16 @@ typedef struct MachCompState {
int loop_break; /* instruction index to patch, or -1 */
int loop_continue; /* instruction index to patch, or -1 */
/* Named label stack for labeled break/continue */
#define MACH_MAX_LABELS 8
struct {
const char *name;
int break_chain;
int continue_chain;
} labels[MACH_MAX_LABELS];
int label_count;
int pending_label; /* label index associated with next loop, or -1 */
/* Parent for nested function compilation */
struct MachCompState *parent;
@@ -1160,6 +1170,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
child.scopes = cs->scopes;
child.filename = cs->filename;
child.freereg = 1; /* slot 0 = this */
child.pending_label = -1;
/* Read function_nr from AST node */
cJSON *fn_nr_node = cJSON_GetObjectItemCaseSensitive(node, "function_nr");
@@ -1513,6 +1524,9 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "expression");
if (!cond) cond = cJSON_GetObjectItemCaseSensitive(stmt, "condition");
int my_label = cs->pending_label;
cs->pending_label = -1;
int old_break = cs->loop_break;
int old_continue = cs->loop_continue;
cs->loop_break = -1;
@@ -1550,6 +1564,17 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
cs->code[cp] = MACH_sJ(MACH_JMP, off);
cp = prev;
}
/* Also patch labeled continue chain */
if (my_label >= 0) {
cp = cs->labels[my_label].continue_chain;
while (cp >= 0) {
int prev = MACH_GET_sJ(cs->code[cp]);
int off = loop_top - (cp + 1);
cs->code[cp] = MACH_sJ(MACH_JMP, off);
cp = prev;
}
cs->labels[my_label].continue_chain = -1;
}
}
/* Jump back to loop top */
@@ -1573,6 +1598,87 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
return;
}
/* Do-while loop */
if (strcmp(kind, "do") == 0) {
cJSON *cond = cJSON_GetObjectItemCaseSensitive(stmt, "expression");
if (!cond) cond = cJSON_GetObjectItemCaseSensitive(stmt, "condition");
int my_label = cs->pending_label;
cs->pending_label = -1;
int old_break = cs->loop_break;
int old_continue = cs->loop_continue;
cs->loop_break = -1;
cs->loop_continue = -1;
int body_top = mach_current_pc(cs);
/* Compile body first (always executes at least once) */
{
cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block");
if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body");
cJSON *stmts = body ? cJSON_GetObjectItemCaseSensitive(body, "statements") : NULL;
if (!stmts) stmts = cJSON_GetObjectItemCaseSensitive(stmt, "statements");
if (stmts && cJSON_IsArray(stmts)) {
int count = cJSON_GetArraySize(stmts);
for (int i = 0; i < count; i++)
mach_compile_stmt(cs, cJSON_GetArrayItem(stmts, i));
} else if (body) {
mach_compile_stmt(cs, body);
}
}
/* Patch continue chain to condition check */
int cond_pc = mach_current_pc(cs);
{
int cp = cs->loop_continue;
while (cp >= 0) {
int prev = MACH_GET_sJ(cs->code[cp]);
int off = cond_pc - (cp + 1);
cs->code[cp] = MACH_sJ(MACH_JMP, off);
cp = prev;
}
/* Also patch labeled continue chain */
if (my_label >= 0) {
cp = cs->labels[my_label].continue_chain;
while (cp >= 0) {
int prev = MACH_GET_sJ(cs->code[cp]);
int off = cond_pc - (cp + 1);
cs->code[cp] = MACH_sJ(MACH_JMP, off);
cp = prev;
}
cs->labels[my_label].continue_chain = -1;
}
}
/* Condition check — jump back to body if true */
int save = cs->freereg;
int cr = mach_compile_expr(cs, cond, -1);
int jmpfalse_pc = mach_current_pc(cs);
mach_emit(cs, MACH_AsBx(MACH_JMPFALSE, cr, 0));
mach_free_reg_to(cs, save);
/* Jump back to body top */
int offset = body_top - (mach_current_pc(cs) + 1);
mach_emit(cs, MACH_sJ(MACH_JMP, offset));
/* Patch jmpfalse to after loop */
offset = mach_current_pc(cs) - (jmpfalse_pc + 1);
cs->code[jmpfalse_pc] = MACH_AsBx(MACH_JMPFALSE, cr, (int16_t)offset);
/* Patch break chain */
int bp = cs->loop_break;
while (bp >= 0) {
int prev = MACH_GET_sJ(cs->code[bp]);
offset = mach_current_pc(cs) - (bp + 1);
cs->code[bp] = MACH_sJ(MACH_JMP, offset);
bp = prev;
}
cs->loop_break = old_break;
cs->loop_continue = old_continue;
return;
}
/* For loop */
if (strcmp(kind, "for") == 0) {
cJSON *init = cJSON_GetObjectItemCaseSensitive(stmt, "init");
@@ -1581,6 +1687,9 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
cJSON *body = cJSON_GetObjectItemCaseSensitive(stmt, "block");
if (!body) body = cJSON_GetObjectItemCaseSensitive(stmt, "body");
int my_label = cs->pending_label;
cs->pending_label = -1;
int old_break = cs->loop_break;
int old_continue = cs->loop_continue;
cs->loop_break = -1;
@@ -1624,6 +1733,17 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
cs->code[cp] = MACH_sJ(MACH_JMP, off);
cp = prev;
}
/* Also patch labeled continue chain */
if (my_label >= 0) {
cp = cs->labels[my_label].continue_chain;
while (cp >= 0) {
int prev = MACH_GET_sJ(cs->code[cp]);
int off = continue_target - (cp + 1);
cs->code[cp] = MACH_sJ(MACH_JMP, off);
cp = prev;
}
cs->labels[my_label].continue_chain = -1;
}
}
/* Update — assignment expressions must be compiled as statements */
@@ -1656,8 +1776,46 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
return;
}
/* Label statement */
if (strcmp(kind, "label") == 0) {
const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name"));
cJSON *inner = cJSON_GetObjectItemCaseSensitive(stmt, "statement");
if (inner && cs->label_count < MACH_MAX_LABELS) {
int li = cs->label_count++;
cs->labels[li].name = name;
cs->labels[li].break_chain = -1;
cs->labels[li].continue_chain = -1;
cs->pending_label = li;
mach_compile_stmt(cs, inner);
/* Patch labeled break chain to after the statement */
int bp = cs->labels[li].break_chain;
while (bp >= 0) {
int prev = MACH_GET_sJ(cs->code[bp]);
int offset = mach_current_pc(cs) - (bp + 1);
cs->code[bp] = MACH_sJ(MACH_JMP, offset);
bp = prev;
}
cs->label_count--;
} else if (inner) {
mach_compile_stmt(cs, inner);
}
return;
}
/* Break */
if (strcmp(kind, "break") == 0) {
const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name"));
if (name) {
/* Labeled break — find the label */
for (int i = cs->label_count - 1; i >= 0; i--) {
if (cs->labels[i].name && strcmp(cs->labels[i].name, name) == 0) {
int pc = mach_current_pc(cs);
mach_emit(cs, MACH_sJ(MACH_JMP, cs->labels[i].break_chain));
cs->labels[i].break_chain = pc;
return;
}
}
}
int pc = mach_current_pc(cs);
mach_emit(cs, MACH_sJ(MACH_JMP, cs->loop_break));
cs->loop_break = pc;
@@ -1666,6 +1824,18 @@ static void mach_compile_stmt(MachCompState *cs, cJSON *stmt) {
/* Continue */
if (strcmp(kind, "continue") == 0) {
const char *name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(stmt, "name"));
if (name) {
/* Labeled continue — find the label */
for (int i = cs->label_count - 1; i >= 0; i--) {
if (cs->labels[i].name && strcmp(cs->labels[i].name, name) == 0) {
int pc = mach_current_pc(cs);
mach_emit(cs, MACH_sJ(MACH_JMP, cs->labels[i].continue_chain));
cs->labels[i].continue_chain = pc;
return;
}
}
}
int pc = mach_current_pc(cs);
mach_emit(cs, MACH_sJ(MACH_JMP, cs->loop_continue));
cs->loop_continue = pc;
@@ -1797,6 +1967,7 @@ MachCode *JS_CompileMachTree(cJSON *ast) {
MachCompState cs = {0};
cs.freereg = 1; /* slot 0 = this */
cs.maxreg = 1;
cs.pending_label = -1;
MachCode *code = mach_compile_program(&cs, ast);