for arr: (it) {}

This commit is contained in:
agra
2026-02-16 00:55:03 +02:00
parent 3e1e764753
commit fb60818424
7 changed files with 88 additions and 56 deletions

View File

@@ -2,14 +2,14 @@
sum :: (args: ..s32) -> s32 { sum :: (args: ..s32) -> s32 {
result := 0; result := 0;
for args { for args: (it) {
result = result + it; result = result + it;
} }
result; result;
} }
print_all :: (args: ..s32) { print_all :: (args: ..s32) {
for args { for args: (it) {
write(int_to_string(it)); write(int_to_string(it));
write(" "); write(" ");
} }
@@ -26,7 +26,7 @@ main :: () -> s32 {
write(int_to_string(sum(..arr))); write(int_to_string(sum(..arr)));
write("\n"); write("\n");
for arr { for arr: (it) {
write(int_to_string(it)); write(int_to_string(it));
write(" "); write(" ");
} }

View File

@@ -7,7 +7,7 @@ Point :: struct {
// Print all arguments — accepts any type, dispatches via type-switch // Print all arguments — accepts any type, dispatches via type-switch
print_any :: (args: ..Any) { print_any :: (args: ..Any) {
for args { for args: (it) {
type := type_of(it); type := type_of(it);
if type == { if type == {
case int: write(int_to_string(cast(s32) it)); case int: write(int_to_string(cast(s32) it));

View File

@@ -53,7 +53,7 @@ pair_add :: (a: $T, b: $U) -> s64 {
typed_sum :: (args: ..s32) -> s32 { typed_sum :: (args: ..s32) -> s32 {
result := 0; result := 0;
for args { result = result + it; } for args: (it) { result = result + it; }
result; result;
} }
@@ -635,7 +635,7 @@ END;
// For loop basic // For loop basic
farr : [4]s32 = .[10, 20, 30, 40]; farr : [4]s32 = .[10, 20, 30, 40];
write("for:"); write("for:");
for farr { for farr: (it) {
write(" "); write(" ");
write(int_to_string(it)); write(int_to_string(it));
} }
@@ -643,29 +643,29 @@ END;
// For with print // For with print
write("for-print:"); write("for-print:");
for farr { for farr: (it) {
print(" {}", it); print(" {}", it);
} }
write("\n"); write("\n");
// For with it_index // For with index
write("for-idx:"); write("for-idx:");
for farr { for farr: (_, ix) {
write(" "); write(" ");
write(int_to_string(it_index)); write(int_to_string(ix));
} }
write("\n"); write("\n");
// For with print two args // For with print two args
write("for-2arg:"); write("for-2arg:");
for farr { for farr: (it, ix) {
print(" {}@{}", it, it_index); print(" {}@{}", it, ix);
} }
write("\n"); write("\n");
// For with break // For with break
write("for-break:"); write("for-break:");
for farr { for farr: (it) {
if it == 30 { break; } if it == 30 { break; }
print(" {}", it); print(" {}", it);
} }
@@ -673,7 +673,7 @@ END;
// For with continue // For with continue
write("for-continue:"); write("for-continue:");
for farr { for farr: (it) {
if it == 20 { continue; } if it == 20 { continue; }
print(" {}", it); print(" {}", it);
} }
@@ -682,15 +682,15 @@ END;
// For on slice // For on slice
fsl : []s32 = .[10, 20, 30]; fsl : []s32 = .[10, 20, 30];
write("for-slice:"); write("for-slice:");
for fsl { for fsl: (it) {
print(" {}", it); print(" {}", it);
} }
write("\n"); write("\n");
// For on slice with it_index // For on slice with index
write("for-slice-idx:"); write("for-slice-idx:");
for fsl { for fsl: (it, ix) {
print(" {}:{}", it_index, it); print(" {}:{}", ix, it);
} }
write("\n"); write("\n");
@@ -698,19 +698,18 @@ END;
nf_a : [2]s32 = .[0, 1]; nf_a : [2]s32 = .[0, 1];
nf_b : [2]s32 = .[0, 1]; nf_b : [2]s32 = .[0, 1];
write("for-nested:"); write("for-nested:");
for nf_a { for nf_a: (oa) {
oa := it; for nf_b: (ob) {
for nf_b { print(" ({},{})", oa, ob);
print(" ({},{})", oa, it);
} }
} }
write("\n"); write("\n");
// For with break preserving it_index // For with break preserving index
fbi : [5]s32 = .[10, 20, 30, 40, 50]; fbi : [5]s32 = .[10, 20, 30, 40, 50];
fbi_idx := 0; fbi_idx := 0;
for fbi { for fbi: (it, ix) {
if it == 30 { fbi_idx = it_index; break; } if it == 30 { fbi_idx = ix; break; }
} }
print("for-break-idx: {}\n", fbi_idx); print("for-break-idx: {}\n", fbi_idx);

View File

@@ -789,22 +789,23 @@ while i < 10 {
### For Loop ### For Loop
```sx ```sx
for iterable { for iterable: (elem) { } // element alias (no copy)
// `it` is the current element for iterable: (elem, ix) { } // element + index
// `it_index` is the current index (s64) for iterable: (_, ix) { } // index only
print("{it}\n");
}
``` ```
Iterates over arrays and slices. The loop body has two implicit variables: Iterates over arrays and slices. The capture clause after `:` binds loop variables:
- `it` — the current element value - The first name is the element capture (non-reassignable alias into the array/slice)
- `it_index` — the current index (s64, starting at 0) - The optional second name is the index (s64, starting at 0, also non-reassignable)
- Use `_` to discard a capture
The element capture is a direct alias — reads and field writes go to the original array element. Direct reassignment of the capture (`elem = x`) is a compile error.
`break;` exits the loop. `continue;` skips to the next iteration. `break;` exits the loop. `continue;` skips to the next iteration.
```sx ```sx
arr : [5]s32 = .[1, 2, 3, 4, 5]; arr : [5]s32 = .[1, 2, 3, 4, 5];
for arr { for arr: (val, ix) {
if it_index == 2 { continue; } if ix == 2 { continue; }
print("{it}\n"); print("{}\n", val);
} }
``` ```
@@ -1069,7 +1070,7 @@ assignment = lvalue ('=' | '+=' | '-=' | '*=' | '/=') expr
lvalue = IDENT | postfix '.' IDENT lvalue = IDENT | postfix '.' IDENT
expr = if_expr | match_expr | while_expr | for_expr | lambda | binary expr = if_expr | match_expr | while_expr | for_expr | lambda | binary
while_expr = 'while' expr block while_expr = 'while' expr block
for_expr = 'for' expr block for_expr = 'for' expr ':' '(' IDENT [',' IDENT] ')' block
binary = unary (binop unary)* binary = unary (binop unary)*
unary = ('-' | '!' | 'xx' | 'cast' '(' type ')') postfix unary = ('-' | '!' | 'xx' | 'cast' '(' type ')') postfix
| postfix | postfix

View File

@@ -349,6 +349,8 @@ pub const WhileExpr = struct {
pub const ForExpr = struct { pub const ForExpr = struct {
iterable: *Node, iterable: *Node,
body: *Node, body: *Node,
capture_name: []const u8,
index_name: ?[]const u8 = null,
}; };
pub const SpreadExpr = struct { pub const SpreadExpr = struct {

View File

@@ -296,6 +296,7 @@ pub const CodeGen = struct {
const NamedValue = struct { const NamedValue = struct {
ptr: c.LLVMValueRef, // alloca pointer ptr: c.LLVMValueRef, // alloca pointer
ty: Type, // sx type ty: Type, // sx type
is_const: bool = false,
}; };
/// Unified value lookup result — avoids sequential hash lookups at hot paths. /// Unified value lookup result — avoids sequential hash lookups at hot paths.
@@ -2881,6 +2882,8 @@ pub const CodeGen = struct {
return self.emitErrorFmt("undefined variable '{s}'", .{name}); return self.emitErrorFmt("undefined variable '{s}'", .{name});
const entry = lookup.asNamedValue() orelse const entry = lookup.asNamedValue() orelse
return self.emitErrorFmt("cannot assign to constant '{s}'", .{name}); return self.emitErrorFmt("cannot assign to constant '{s}'", .{name});
if (entry.is_const)
return self.emitErrorFmt("cannot assign to '{s}'", .{name});
// Meta type reassignment: x = Vec4, x = f64, x = test // Meta type reassignment: x = Vec4, x = f64, x = test
if (entry.ty == .meta_type and asgn.op == .assign) { if (entry.ty == .meta_type and asgn.op == .assign) {
@@ -6345,15 +6348,17 @@ pub const CodeGen = struct {
const elem_llvm_ty = self.typeToLLVM(elem_ty); const elem_llvm_ty = self.typeToLLVM(elem_ty);
// Allocate it_index (s64) and it (element type) // Allocate index variable
const idx_alloca = self.buildEntryBlockAlloca(i64_type, "it_index"); const idx_alloca = self.buildEntryBlockAlloca(i64_type, "for_idx");
_ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i64_type, 0, 0), idx_alloca); _ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(i64_type, 0, 0), idx_alloca);
const it_alloca = self.buildEntryBlockAlloca(elem_llvm_ty, "it");
// Push scope and bind it, it_index // Push scope and bind index if requested
try self.pushScope(); try self.pushScope();
try self.named_values.put("it", .{ .ptr = it_alloca, .ty = elem_ty }); if (for_expr.index_name) |idx_name| {
try self.named_values.put("it_index", .{ .ptr = idx_alloca, .ty = Type.s(64) }); if (!std.mem.eql(u8, idx_name, "_")) {
try self.named_values.put(idx_name, .{ .ptr = idx_alloca, .ty = Type.s(64), .is_const = true });
}
}
// Create basic blocks // Create basic blocks
const cond_bb = self.appendBB("for.cond"); const cond_bb = self.appendBB("for.cond");
@@ -6363,29 +6368,32 @@ pub const CodeGen = struct {
self.br(cond_bb); self.br(cond_bb);
// Condition: it_index < len // Condition: index < len
self.positionAt(cond_bb); self.positionAt(cond_bb);
const cur_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "cur_idx"); const cur_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "cur_idx");
const cond_val = self.icmp(c.LLVMIntSLT, cur_idx, len_val, "for_cond"); const cond_val = self.icmp(c.LLVMIntSLT, cur_idx, len_val, "for_cond");
self.condBr(cond_val, body_bb, after_bb); self.condBr(cond_val, body_bb, after_bb);
// Body: load it = iterable[it_index], then execute body // Body: compute element GEP, bind capture, execute body
self.positionAt(body_bb); self.positionAt(body_bb);
const body_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "body_idx"); const body_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "body_idx");
if (is_slice) { const elem_gep = if (is_slice)
// Slice: GEP through data pointer self.gepPointerElement(elem_llvm_ty, iter_ptr, body_idx, "for_elem")
const gep = self.gepPointerElement(elem_llvm_ty, iter_ptr, body_idx, "for_elem"); else blk: {
const elem_val = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "it_val");
_ = c.LLVMBuildStore(self.builder, elem_val, it_alloca);
} else {
// Array: GEP with [0, idx]
const arr_llvm_ty = self.typeToLLVM(iter_ty); const arr_llvm_ty = self.typeToLLVM(iter_ty);
const zero = c.LLVMConstInt(i64_type, 0, 0); const zero = c.LLVMConstInt(i64_type, 0, 0);
var indices = [_]c.LLVMValueRef{ zero, body_idx }; var indices = [_]c.LLVMValueRef{ zero, body_idx };
const gep = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, iter_ptr, &indices, 2, "for_elem"); break :blk c.LLVMBuildGEP2(self.builder, arr_llvm_ty, iter_ptr, &indices, 2, "for_elem");
const elem_val = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "it_val"); };
_ = c.LLVMBuildStore(self.builder, elem_val, it_alloca);
if (!std.mem.eql(u8, for_expr.capture_name, "_")) {
// Alias mode: capture points directly to element in array/slice
try self.named_values.put(for_expr.capture_name, .{
.ptr = elem_gep,
.ty = elem_ty,
.is_const = true,
});
} }
// Save and set loop context for break/continue // Save and set loop context for break/continue
@@ -6405,7 +6413,7 @@ pub const CodeGen = struct {
self.br(incr_bb); self.br(incr_bb);
} }
// Increment it_index, then branch back to condition // Increment index, then branch back to condition
self.positionAt(incr_bb); self.positionAt(incr_bb);
const inc_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "inc_idx"); const inc_idx = c.LLVMBuildLoad2(self.builder, i64_type, idx_alloca, "inc_idx");
const next_idx = c.LLVMBuildAdd(self.builder, inc_idx, c.LLVMConstInt(i64_type, 1, 0), "next_idx"); const next_idx = c.LLVMBuildAdd(self.builder, inc_idx, c.LLVMConstInt(i64_type, 1, 0), "next_idx");

View File

@@ -1428,11 +1428,33 @@ pub const Parser = struct {
self.advance(); // skip 'for' self.advance(); // skip 'for'
const iterable = try self.parseExpr(); const iterable = try self.parseExpr();
// Expect ': (' capture clause
try self.expect(.colon);
try self.expect(.l_paren);
// Capture variable name
if (self.current.tag != .identifier) return self.fail("expected capture variable name");
const capture_name = self.tokenSlice(self.current);
self.advance();
// Optional ', index_name'
var index_name: ?[]const u8 = null;
if (self.current.tag == .comma) {
self.advance();
if (self.current.tag != .identifier) return self.fail("expected index variable name");
index_name = self.tokenSlice(self.current);
self.advance();
}
try self.expect(.r_paren);
const body = try self.parseBlock(); const body = try self.parseBlock();
return try self.createNode(start, .{ .for_expr = .{ return try self.createNode(start, .{ .for_expr = .{
.iterable = iterable, .iterable = iterable,
.body = body, .body = body,
.capture_name = capture_name,
.index_name = index_name,
} }); } });
} }