mirror of
https://github.com/imjasonh/nescript
synced 2026-07-18 14:45:58 +00:00
Language: for i in start..end loops
Adds a \`for NAME in START..END { BODY }\` half-open range loop:
for i in 0..8 {
total += arr[i]
}
- Lexer: \`for\`, \`in\` keywords and the \`..\` range operator
- AST: new \`Statement::For\` variant with var/start/end/body
- Parser: \`parse_for\` after \`while\` / \`loop\`
- Analyzer: registers the loop variable as a u8 symbol for the body
(restoring any shadowed outer symbol afterwards), allocates it via
the normal RAM allocator, and tracks it as "used"
- IR lowering: desugars to \`var = start; while var < end { body;
var = var + 1 }\` using a \`for_step\` continue-edge block so
\`continue\` properly increments the index
- AST codegen: no-op (legacy path doesn't need for loops)
- Tests: parse + full-pipeline integration
https://claude.ai/code/session_01W6eQFStA66EuMKHUFo2rx3
This commit is contained in:
parent
8f0e0635df
commit
240da57b54
10 changed files with 248 additions and 1 deletions
|
|
@ -866,6 +866,50 @@ impl Analyzer {
|
|||
self.check_block(body, state_names);
|
||||
self.in_loop = was_in_loop;
|
||||
}
|
||||
Statement::For {
|
||||
var,
|
||||
start,
|
||||
end,
|
||||
body,
|
||||
span,
|
||||
} => {
|
||||
// Evaluate start/end (both u8) for reads and type
|
||||
// checking, then register the loop variable as a u8
|
||||
// for the duration of the body.
|
||||
self.walk_expr_reads(start);
|
||||
self.walk_expr_reads(end);
|
||||
self.check_expr_type(start, &NesType::U8);
|
||||
self.check_expr_type(end, &NesType::U8);
|
||||
let was_shadowed = self.symbols.remove(var);
|
||||
self.symbols.insert(
|
||||
var.clone(),
|
||||
Symbol {
|
||||
name: var.clone(),
|
||||
sym_type: NesType::U8,
|
||||
is_const: false,
|
||||
span: *span,
|
||||
},
|
||||
);
|
||||
// Synthesize a VarAllocation for the loop variable
|
||||
// so IR lowering / codegen can treat it like any
|
||||
// other u8 local.
|
||||
let loop_var_addr = self.allocate_ram(1, *span).unwrap_or(0x10);
|
||||
self.var_allocations.push(VarAllocation {
|
||||
name: var.clone(),
|
||||
address: loop_var_addr,
|
||||
size: 1,
|
||||
});
|
||||
// Loop variable is always "used" in the header.
|
||||
self.mark_var_used(var);
|
||||
let was_in_loop = self.in_loop;
|
||||
self.in_loop = true;
|
||||
self.check_block(body, state_names);
|
||||
self.in_loop = was_in_loop;
|
||||
self.symbols.remove(var);
|
||||
if let Some(old) = was_shadowed {
|
||||
self.symbols.insert(var.clone(), old);
|
||||
}
|
||||
}
|
||||
Statement::Loop(body, span) => {
|
||||
let was_in_loop = self.in_loop;
|
||||
self.in_loop = true;
|
||||
|
|
@ -1148,6 +1192,9 @@ fn collect_transitions_stmt(stmt: &Statement, queue: &mut Vec<String>) {
|
|||
Statement::While(_, body, _) | Statement::Loop(body, _) => {
|
||||
collect_transitions_block(body, queue);
|
||||
}
|
||||
Statement::For { body, .. } => {
|
||||
collect_transitions_block(body, queue);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1187,6 +1234,13 @@ fn collect_calls_stmt(stmt: &Statement, calls: &mut Vec<String>) {
|
|||
Statement::Loop(body, _) => {
|
||||
collect_calls_block(body, calls);
|
||||
}
|
||||
Statement::For {
|
||||
start, end, body, ..
|
||||
} => {
|
||||
collect_calls_expr(start, calls);
|
||||
collect_calls_expr(end, calls);
|
||||
collect_calls_block(body, calls);
|
||||
}
|
||||
Statement::Assign(_, _, expr, _) => {
|
||||
collect_calls_expr(expr, calls);
|
||||
}
|
||||
|
|
@ -1284,6 +1338,7 @@ fn stmt_can_exit_or_yield(stmt: &Statement) -> bool {
|
|||
// control, so check its body recursively.
|
||||
block_can_exit_or_yield(body)
|
||||
}
|
||||
Statement::For { body, .. } => block_can_exit_or_yield(body),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue