for arr: (it) {}
This commit is contained in:
@@ -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(" ");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
25
specs.md
25
specs.md
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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,
|
||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user