1
0
Fork 0
forked from fun/fun

Array iteration.

This commit is contained in:
Johannes Findeisen 2025-09-15 03:03:00 +02:00
commit 879801cbfc
2 changed files with 259 additions and 103 deletions

30
examples/arrays_iter.fun Normal file
View file

@ -0,0 +1,30 @@
// for-in over an array literal
for x in [1, 2, 3]
print(x) // prints: 1, then 2, then 3
// for-in over a named array
arr = [10, 20, 30]
for x in arr
print(x) // prints: 10, then 20, then 30
// nested arrays: iterate rows, then elements
matrix = [[1, 2], [3, 4]]
for row in matrix
print(row) // prints: [1, 2] then [3, 4]
for v in row
print(v) // prints: 1, 2, 3, 4
// iterate over an array expression (concatenation)
for v in arr + [40, 50]
print(v) // prints: 10, 20, 30, 40, 50
// iterate over a slice (end = -1 means till the end)
for v in arr[1:-1]
print(v) // prints: 20, 30
// iterate over a function result
fun make()
return [7, 9, 11]
for v in make()
print(v) // prints: 7, 9, 11

View file

@ -1255,7 +1255,10 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
continue;
}
/* for-sugar: for <ident> in range(a, b) */
/* for-sugar:
* - for <ident> in range(a, b)
* - for <ident> in <array-expr>
*/
if (starts_with(src, len, *pos, "for")) {
*pos += 3;
skip_spaces(src, len, pos);
@ -1276,123 +1279,246 @@ static void parse_block(Bytecode *bc, const char *src, size_t len, size_t *pos,
*pos += 2;
skip_spaces(src, len, pos);
if (!starts_with(src, len, *pos, "range")) {
parser_fail(*pos, "Expected 'range' after 'in'");
if (starts_with(src, len, *pos, "range")) {
/* ===== range(a, b) variant ===== */
*pos += 5;
if (!consume_char(src, len, pos, '(')) {
parser_fail(*pos, "Expected '(' after range");
free(ivar);
return;
}
/* Parse start expression */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "Expected start expression in range");
free(ivar);
return;
}
/* Determine loop variable storage and store start value */
int lidx = local_find(ivar);
int gi = -1;
if (lidx < 0) {
if (g_locals) lidx = local_add(ivar);
else gi = sym_index(ivar);
}
if (lidx >= 0) {
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
/* comma */
skip_spaces(src, len, pos);
if (*pos >= len || src[*pos] != ',') {
parser_fail(*pos, "Expected ',' between range start and end");
free(ivar);
return;
}
(*pos)++; /* consume ',' */
skip_spaces(src, len, pos);
/* Parse end expression and store in a temp (local or global) */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "Expected end expression in range");
free(ivar);
return;
}
char tmpname[64];
snprintf(tmpname, sizeof(tmpname), "__for_end_%d", g_temp_counter++);
int lend = -1, gend = -1;
if (g_locals) {
lend = local_add(tmpname);
bytecode_add_instruction(bc, OP_STORE_LOCAL, lend);
} else {
gend = sym_index(tmpname);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gend);
}
if (!consume_char(src, len, pos, ')')) {
parser_fail(*pos, "Expected ')' after range arguments");
free(ivar);
return;
}
/* end of header line */
skip_to_eol(src, len, pos);
/* emit loop */
int loop_start = bc->instr_count;
/* condition: ivar < end_tmp */
if (lidx >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
}
if (lend >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lend);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gend);
}
bytecode_add_instruction(bc, OP_LT, 0);
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
/* parse body at increased indent (peek) */
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 ok */
}
/* i = i + 1 */
int c1 = bytecode_add_constant(bc, make_int(1));
if (lidx >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lidx);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
/* back edge and patch */
bytecode_add_instruction(bc, OP_JUMP, loop_start);
bytecode_set_operand(bc, jmp_false, bc->instr_count);
free(ivar);
return;
}
*pos += 5;
if (!consume_char(src, len, pos, '(')) {
parser_fail(*pos, "Expected '(' after range");
continue;
} else {
/* ===== array iteration: for ivar in <expr> ===== */
/* Evaluate the iterable once and store in a temp */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "Expected iterable expression after 'in'");
free(ivar);
return;
}
char arrname[64];
snprintf(arrname, sizeof(arrname), "__for_arr_%d", g_temp_counter++);
int larr = -1, garr = -1;
if (g_locals) {
larr = local_add(arrname);
bytecode_add_instruction(bc, OP_STORE_LOCAL, larr);
} else {
garr = sym_index(arrname);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, garr);
}
/* Compute length once: len(arr) -> store temp */
if (larr >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, larr);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, garr);
}
bytecode_add_instruction(bc, OP_LEN, 0);
char lenname[64];
snprintf(lenname, sizeof(lenname), "__for_len_%d", g_temp_counter++);
int llen = -1, glen = -1;
if (g_locals) {
llen = local_add(lenname);
bytecode_add_instruction(bc, OP_STORE_LOCAL, llen);
} else {
glen = sym_index(lenname);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, glen);
}
/* Index temp: i = 0 */
int c0 = bytecode_add_constant(bc, make_int(0));
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
char iname[64];
snprintf(iname, sizeof(iname), "__for_i_%d", g_temp_counter++);
int li = -1, gi = -1;
if (g_locals) {
li = local_add(iname);
bytecode_add_instruction(bc, OP_STORE_LOCAL, li);
} else {
gi = sym_index(iname);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
/* end of header line */
skip_to_eol(src, len, pos);
/* loop start label */
int loop_start = bc->instr_count;
/* condition: i < len */
if (li >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, li);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
}
if (llen >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, llen);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, glen);
}
bytecode_add_instruction(bc, OP_LT, 0);
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
/* element: ivar = arr[i] */
if (larr >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, larr);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, garr);
}
if (li >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, li);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
}
bytecode_add_instruction(bc, OP_INDEX_GET, 0);
/* assign to ivar (local preferred) */
int ldst = local_find(ivar);
int gdst = -1;
if (ldst < 0) {
if (g_locals) ldst = local_add(ivar);
else gdst = sym_index(ivar);
}
if (ldst >= 0) {
bytecode_add_instruction(bc, OP_STORE_LOCAL, ldst);
} else {
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gdst);
}
/* parse body at increased indent */
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 ok */
}
/* i = i + 1 */
int c1 = bytecode_add_constant(bc, make_int(1));
if (li >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, li);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_STORE_LOCAL, li);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
/* back edge and patch */
bytecode_add_instruction(bc, OP_JUMP, loop_start);
bytecode_set_operand(bc, jmp_false, bc->instr_count);
free(ivar);
return;
continue;
}
/* Parse start expression */
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "Expected start expression in range");
free(ivar);
return;
}
/* Determine loop variable storage and store start value */
int lidx = local_find(ivar);
int gi = -1;
if (lidx < 0) {
if (g_locals) lidx = local_add(ivar);
else gi = sym_index(ivar);
}
if (lidx >= 0) {
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
/* comma */
skip_spaces(src, len, pos);
if (*pos >= len || src[*pos] != ',') {
parser_fail(*pos, "Expected ',' between range start and end");
free(ivar);
return;
}
(*pos)++; /* consume ',' */
skip_spaces(src, len, pos);
/* Parse end expression and store in a temp (local or global) */
size_t end_expr_pos_before = *pos;
if (!emit_expression(bc, src, len, pos)) {
parser_fail(*pos, "Expected end expression in range");
free(ivar);
return;
}
char tmpname[64];
snprintf(tmpname, sizeof(tmpname), "__for_end_%d", g_temp_counter++);
int lend = -1, gend = -1;
if (g_locals) {
lend = local_add(tmpname);
bytecode_add_instruction(bc, OP_STORE_LOCAL, lend);
} else {
gend = sym_index(tmpname);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gend);
}
if (!consume_char(src, len, pos, ')')) {
parser_fail(*pos, "Expected ')' after range arguments");
free(ivar);
return;
}
/* end of header line */
skip_to_eol(src, len, pos);
/* emit loop */
int loop_start = bc->instr_count;
/* condition: ivar < end_tmp */
if (lidx >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
}
if (lend >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lend);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gend);
}
bytecode_add_instruction(bc, OP_LT, 0);
int jmp_false = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
/* parse body at increased indent (peek) */
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 ok */
}
/* i = i + 1 */
int c1 = bytecode_add_constant(bc, make_int(1));
if (lidx >= 0) {
bytecode_add_instruction(bc, OP_LOAD_LOCAL, lidx);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_STORE_LOCAL, lidx);
} else {
bytecode_add_instruction(bc, OP_LOAD_GLOBAL, gi);
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
bytecode_add_instruction(bc, OP_ADD, 0);
bytecode_add_instruction(bc, OP_STORE_GLOBAL, gi);
}
/* back edge and patch */
bytecode_add_instruction(bc, OP_JUMP, loop_start);
bytecode_set_operand(bc, jmp_false, bc->instr_count);
free(ivar);
continue;
}
if (starts_with(src, len, *pos, "if")) {