Loops, break and continue.
This commit is contained in:
parent
879801cbfc
commit
0b48aa3e0c
2 changed files with 134 additions and 4 deletions
31
examples/loops_break_continue.fun
Executable file
31
examples/loops_break_continue.fun
Executable file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env fun
|
||||||
|
|
||||||
|
// break and continue examples
|
||||||
|
|
||||||
|
// 1) while loop: print odd numbers, stop after printing 7
|
||||||
|
number i = 0
|
||||||
|
while i < 10
|
||||||
|
if i % 2 == 0
|
||||||
|
i = i + 1
|
||||||
|
continue
|
||||||
|
print(i) // -> 1, 3, 5, 7
|
||||||
|
if i > 5
|
||||||
|
break
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
// 2) for range: skip 3, stop at 6
|
||||||
|
for i in range(0, 8)
|
||||||
|
if i == 3
|
||||||
|
continue
|
||||||
|
print(i) // -> 0, 1, 2, 4, 5, 6
|
||||||
|
if i == 6
|
||||||
|
break
|
||||||
|
|
||||||
|
// 3) for-in over array: skip 3, break at 4
|
||||||
|
arr = [1, 2, 3, 4, 5]
|
||||||
|
for x in arr
|
||||||
|
if x == 3
|
||||||
|
continue
|
||||||
|
print(x) // -> 1, 2, 4
|
||||||
|
if x == 4
|
||||||
|
break
|
||||||
107
src/parser.c
107
src/parser.c
|
|
@ -230,6 +230,17 @@ typedef struct {
|
||||||
|
|
||||||
static LocalEnv *g_locals = NULL;
|
static LocalEnv *g_locals = NULL;
|
||||||
|
|
||||||
|
/* loop context for break/continue patching */
|
||||||
|
typedef struct LoopCtx {
|
||||||
|
int break_jumps[64];
|
||||||
|
int break_count;
|
||||||
|
int continue_jumps[64];
|
||||||
|
int cont_count;
|
||||||
|
struct LoopCtx *prev;
|
||||||
|
} LoopCtx;
|
||||||
|
|
||||||
|
static LoopCtx *g_loop_ctx = NULL;
|
||||||
|
|
||||||
static int local_find(const char *name) {
|
static int local_find(const char *name) {
|
||||||
if (!g_locals) return -1;
|
if (!g_locals) return -1;
|
||||||
for (int i = 0; i < g_locals->count; ++i) {
|
for (int i = 0; i < g_locals->count; ++i) {
|
||||||
|
|
@ -976,6 +987,42 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* break / continue */
|
||||||
|
if (strcmp(name, "break") == 0) {
|
||||||
|
free(name);
|
||||||
|
if (!g_loop_ctx) {
|
||||||
|
parser_fail(local_pos, "break used outside of loop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int j = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||||
|
if (g_loop_ctx->break_count < (int)(sizeof(g_loop_ctx->break_jumps) / sizeof(g_loop_ctx->break_jumps[0]))) {
|
||||||
|
g_loop_ctx->break_jumps[g_loop_ctx->break_count++] = j;
|
||||||
|
} else {
|
||||||
|
parser_fail(local_pos, "Too many 'break' in one loop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*pos = local_pos;
|
||||||
|
skip_to_eol(src, len, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (strcmp(name, "continue") == 0) {
|
||||||
|
free(name);
|
||||||
|
if (!g_loop_ctx) {
|
||||||
|
parser_fail(local_pos, "continue used outside of loop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int j = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||||
|
if (g_loop_ctx->cont_count < (int)(sizeof(g_loop_ctx->continue_jumps) / sizeof(g_loop_ctx->continue_jumps[0]))) {
|
||||||
|
g_loop_ctx->continue_jumps[g_loop_ctx->cont_count++] = j;
|
||||||
|
} else {
|
||||||
|
parser_fail(local_pos, "Too many 'continue' in one loop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*pos = local_pos;
|
||||||
|
skip_to_eol(src, len, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* typed declarations: number|string|boolean <ident> (= expr)? */
|
/* typed declarations: number|string|boolean <ident> (= expr)? */
|
||||||
if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0) {
|
if (strcmp(name, "number") == 0 || strcmp(name, "string") == 0 || strcmp(name, "boolean") == 0) {
|
||||||
int is_number = (strcmp(name, "number") == 0);
|
int is_number = (strcmp(name, "number") == 0);
|
||||||
|
|
@ -1363,6 +1410,10 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
bytecode_add_instruction(bc, OP_LT, 0);
|
bytecode_add_instruction(bc, OP_LT, 0);
|
||||||
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||||
|
|
||||||
|
/* enter loop context for break/continue */
|
||||||
|
LoopCtx ctx = { {0}, 0, {0}, 0, g_loop_ctx };
|
||||||
|
g_loop_ctx = &ctx;
|
||||||
|
|
||||||
/* parse body at increased indent (peek) */
|
/* parse body at increased indent (peek) */
|
||||||
int body_indent = 0;
|
int body_indent = 0;
|
||||||
size_t look_body = *pos;
|
size_t look_body = *pos;
|
||||||
|
|
@ -1372,6 +1423,9 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
/* empty body ok */
|
/* empty body ok */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* continue target: start of increment */
|
||||||
|
int cont_label = bc->instr_count;
|
||||||
|
|
||||||
/* i = i + 1 */
|
/* i = i + 1 */
|
||||||
int c1 = bytecode_add_constant(bc, make_int(1));
|
int c1 = bytecode_add_constant(bc, make_int(1));
|
||||||
if (lidx >= 0) {
|
if (lidx >= 0) {
|
||||||
|
|
@ -1388,7 +1442,19 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
|
|
||||||
/* back edge and patch */
|
/* back edge and patch */
|
||||||
bytecode_add_instruction(bc, OP_JUMP, loop_start);
|
bytecode_add_instruction(bc, OP_JUMP, loop_start);
|
||||||
bytecode_set_operand(bc, jmp_false, bc->instr_count);
|
|
||||||
|
/* end label (after loop) */
|
||||||
|
int end_label = bc->instr_count;
|
||||||
|
bytecode_set_operand(bc, jmp_false, end_label);
|
||||||
|
|
||||||
|
/* patch continue/break jumps */
|
||||||
|
for (int bi = 0; bi < ctx.cont_count; ++bi) {
|
||||||
|
bytecode_set_operand(bc, ctx.continue_jumps[bi], cont_label);
|
||||||
|
}
|
||||||
|
for (int bi = 0; bi < ctx.break_count; ++bi) {
|
||||||
|
bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
|
||||||
|
}
|
||||||
|
g_loop_ctx = ctx.prev;
|
||||||
|
|
||||||
free(ivar);
|
free(ivar);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1489,6 +1555,10 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gdst);
|
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gdst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* enter loop context for break/continue */
|
||||||
|
LoopCtx ctx = { {0}, 0, {0}, 0, g_loop_ctx };
|
||||||
|
g_loop_ctx = &ctx;
|
||||||
|
|
||||||
/* parse body at increased indent */
|
/* parse body at increased indent */
|
||||||
int body_indent = 0;
|
int body_indent = 0;
|
||||||
size_t look_body = *pos;
|
size_t look_body = *pos;
|
||||||
|
|
@ -1498,6 +1568,9 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
/* empty body ok */
|
/* empty body ok */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* continue target: start of increment */
|
||||||
|
int cont_label = bc->instr_count;
|
||||||
|
|
||||||
/* i = i + 1 */
|
/* i = i + 1 */
|
||||||
int c1 = bytecode_add_constant(bc, make_int(1));
|
int c1 = bytecode_add_constant(bc, make_int(1));
|
||||||
if (li >= 0) {
|
if (li >= 0) {
|
||||||
|
|
@ -1514,7 +1587,19 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
|
|
||||||
/* back edge and patch */
|
/* back edge and patch */
|
||||||
bytecode_add_instruction(bc, OP_JUMP, loop_start);
|
bytecode_add_instruction(bc, OP_JUMP, loop_start);
|
||||||
bytecode_set_operand(bc, jmp_false, bc->instr_count);
|
|
||||||
|
/* end label (after loop) */
|
||||||
|
int end_label = bc->instr_count;
|
||||||
|
bytecode_set_operand(bc, jmp_false, end_label);
|
||||||
|
|
||||||
|
/* patch continue/break jumps */
|
||||||
|
for (int bi = 0; bi < ctx.cont_count; ++bi) {
|
||||||
|
bytecode_set_operand(bc, ctx.continue_jumps[bi], cont_label);
|
||||||
|
}
|
||||||
|
for (int bi = 0; bi < ctx.break_count; ++bi) {
|
||||||
|
bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
|
||||||
|
}
|
||||||
|
g_loop_ctx = ctx.prev;
|
||||||
|
|
||||||
free(ivar);
|
free(ivar);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1637,6 +1722,10 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
/* jump over body if false */
|
/* jump over body if false */
|
||||||
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||||
|
|
||||||
|
/* enter loop context (continue -> condition) */
|
||||||
|
LoopCtx ctx = { {0}, 0, {0}, 0, g_loop_ctx };
|
||||||
|
g_loop_ctx = &ctx;
|
||||||
|
|
||||||
/* parse body at increased indent (peek indent without advancing pos) */
|
/* parse body at increased indent (peek indent without advancing pos) */
|
||||||
int body_indent = 0;
|
int body_indent = 0;
|
||||||
size_t look_body = *pos;
|
size_t look_body = *pos;
|
||||||
|
|
@ -1646,11 +1735,21 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
|
||||||
/* empty body allowed */
|
/* empty body allowed */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* patch pending continues to loop_start (re-evaluate condition) */
|
||||||
|
for (int bi = 0; bi < ctx.cont_count; ++bi) {
|
||||||
|
bytecode_set_operand(bc, ctx.continue_jumps[bi], loop_start);
|
||||||
|
}
|
||||||
|
|
||||||
/* back edge to loop start */
|
/* back edge to loop start */
|
||||||
bytecode_add_instruction(bc, OP_JUMP, loop_start);
|
bytecode_add_instruction(bc, OP_JUMP, loop_start);
|
||||||
|
|
||||||
/* patch false jump to here (after body) */
|
/* end label and patches */
|
||||||
bytecode_set_operand(bc, jmp_false, bc->instr_count);
|
int end_label = bc->instr_count;
|
||||||
|
bytecode_set_operand(bc, jmp_false, end_label);
|
||||||
|
for (int bi = 0; bi < ctx.break_count; ++bi) {
|
||||||
|
bytecode_set_operand(bc, ctx.break_jumps[bi], end_label);
|
||||||
|
}
|
||||||
|
g_loop_ctx = ctx.prev;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue