1
0
Fork 0
forked from fun/fun

While loops.

This commit is contained in:
Johannes Findeisen 2025-09-14 15:40:09 +02:00
commit 24305209eb
3 changed files with 94 additions and 2 deletions

1
.gitignore vendored
View file

@ -6,6 +6,7 @@ out/
src/*.o src/*.o
tmp/ tmp/
.aiignore .aiignore
dev.txt
fun fun
fun_test fun_test
test_opcodes test_opcodes

35
examples/while_test.fun Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env fun
// While loops feature test: simple loops, nested with if/else, and functions
print("=== while test start ===")
// Simple counter 0..4
number i = 0
while (i < 5)
print(i) // expect 0,1,2,3,4
i = i + 1
// Nested while with if/else
number a = 3
while (a > 0)
if (a == 2)
print(99) // special case when a == 2
else
print(a)
a = a - 1 // expect: 3, 99, 1
// While with false condition: body should not run
while (0)
print(123)
// Function using while
fun countdown(n)
while (n > 0)
print(n)
n = n - 1
return 0
print(countdown(3)) // expect: 3,2,1 then 0
print("=== while test end ===")

View file

@ -380,6 +380,22 @@ static int emit_multiplicative(Bytecode *bc, const char *src, size_t len, size_t
if (!emit_unary(bc, src, len, pos)) return 0; if (!emit_unary(bc, src, len, pos)) return 0;
for (;;) { for (;;) {
skip_spaces(src, len, pos); skip_spaces(src, len, pos);
/* Stop expression at start of inline comment */
if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '/') {
break;
}
/* Skip block comments inside expressions */
if (*pos + 1 < len && src[*pos] == '/' && src[*pos + 1] == '*') {
size_t p = *pos + 2;
while (p + 1 < len && !(src[p] == '*' && src[p + 1] == '/')) {
p++;
}
if (p + 1 < len) p += 2; /* consume closing marker */
*pos = p;
continue;
}
if (*pos < len && src[*pos] == '*') { if (*pos < len && src[*pos] == '*') {
(*pos)++; (*pos)++;
if (!emit_unary(bc, src, len, pos)) { parser_fail(*pos, "Expected expression after '*'"); return 0; } if (!emit_unary(bc, src, len, pos)) { parser_fail(*pos, "Expected expression after '*'"); return 0; }
@ -684,13 +700,18 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
} }
/* assignment or simple call */ /* assignment or simple call */
int gi = sym_index(name); int lidx = local_find(name);
int gi = (lidx < 0) ? sym_index(name) : -1;
free(name); free(name);
skip_spaces(src, len, &local_pos); skip_spaces(src, len, &local_pos);
if (local_pos < len && src[local_pos] == '=') { if (local_pos < len && src[local_pos] == '=') {
local_pos++; /* '=' */ local_pos++; /* '=' */
if (emit_expression(bc, src, len, &local_pos)) { if (emit_expression(bc, src, len, &local_pos)) {
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi); if (lidx >= 0) {
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
} }
*pos = local_pos; *pos = local_pos;
skip_to_eol(src, len, pos); skip_to_eol(src, len, pos);
@ -937,6 +958,41 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
continue; continue;
} }
/* while loop */
if (starts_with(src, len, *pos, "while")) {
*pos += 5;
skip_spaces(src, len, pos);
int loop_start = bc->instr_count;
/* condition */
if (!emit_expression(bc, src, len, pos)) {
int ci = bytecode_add_constant(bc, make_int(0));
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
}
/* end of condition */
skip_to_eol(src, len, pos);
/* jump over body if false */
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
/* parse body at increased indent (peek indent without advancing pos) */
int body_indent = 0;
size_t look_body = *pos;
if (read_line_start(src, len, &look_body, &body_indent) && body_indent > current_indent) {
parse_block(bc, src, len, pos, body_indent);
} else {
/* empty body allowed */
}
/* back edge to loop start */
bytecode_add_instruction(bc, OP_JUMP, loop_start);
/* patch false jump to here (after body) */
bytecode_set_operand(bc, jmp_false, bc->instr_count);
continue;
}
/* otherwise: simple statement on this line */ /* otherwise: simple statement on this line */
parse_simple_statement(bc, src, len, pos); parse_simple_statement(bc, src, len, pos);
} }