fix labeled loops, do-while, shorthand property syntax, and added more tests
This commit is contained in:
171
source/mach.c
171
source/mach.c
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user