Skip to content

Commit 47497a8

Browse files
feat: support range(start, end, step) in for-loops
3-arg range desugars to ForIterable::Expr(Call("range",...)) so it routes through the existing range() builtin that already handles step and negative step (countdown). Preserves the fast ForIterable::Range path for range(n) and range(start, end). 18/18 parser-extras tests, 1360/1360 full suite. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3cd9d5e commit 47497a8

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

examples/tests/test_parser_extras.omc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,29 @@ fn test_power_aug_assign() {
148148
y **= 8;
149149
assert_eq(y, 256, "y **= 8");
150150
}
151+
152+
# ---- range(start, end, step) ----
153+
154+
fn test_range_step_forward() {
155+
h total = 0;
156+
for i in range(0, 10, 2) {
157+
total += i;
158+
}
159+
assert_eq(total, 20, "range(0,10,2) sum=20");
160+
}
161+
162+
fn test_range_step_countdown() {
163+
h total = 0;
164+
for i in range(5, 0, -1) {
165+
total += i;
166+
}
167+
assert_eq(total, 15, "range(5,0,-1) sum=15");
168+
}
169+
170+
fn test_range_step_skip() {
171+
h count = 0;
172+
for i in range(1, 10, 3) {
173+
count += 1;
174+
}
175+
assert_eq(count, 3, "range(1,10,3) has 3 elements: 1,4,7");
176+
}

omnimcode-core/src/parser.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,12 +1402,28 @@ impl Parser {
14021402
self.advance();
14031403
self.expect(Token::LParen)?;
14041404
let first = self.parse_expression()?;
1405-
// Canonical OMC supports both range(end) and range(start, end).
1405+
// Canonical OMC supports range(end), range(start, end), and
1406+
// range(start, end, step). The fast ForIterable::Range path is used
1407+
// for the first two forms; a 3-arg range desugars to a builtin call
1408+
// (ForIterable::Expr) which the interpreter evaluates via range().
14061409
if self.current() == Token::Comma {
14071410
self.advance();
1408-
let end = self.parse_expression()?;
1409-
self.expect(Token::RParen)?;
1410-
ForIterable::Range { start: first, end }
1411+
let second = self.parse_expression()?;
1412+
if self.current() == Token::Comma {
1413+
// range(start, end, step) — desugar to Expr(Call("range", ...))
1414+
self.advance();
1415+
let step = self.parse_expression()?;
1416+
self.expect(Token::RParen)?;
1417+
let pos = crate::ast::Pos { line: 0, col: 0 };
1418+
ForIterable::Expr(Expression::Call {
1419+
name: "range".to_string(),
1420+
args: vec![first, second, step],
1421+
pos,
1422+
})
1423+
} else {
1424+
self.expect(Token::RParen)?;
1425+
ForIterable::Range { start: first, end: second }
1426+
}
14111427
} else {
14121428
self.expect(Token::RParen)?;
14131429
ForIterable::Range {

0 commit comments

Comments
 (0)