Compiles a `FOR` loop and emits bytecode into `ctx`.
(ctx: &mut Context, symtable: &mut LocalSymtable<'_>, span: ForSpan)
| 604 | |
| 605 | /// Compiles a `FOR` loop and emits bytecode into `ctx`. |
| 606 | fn compile_for(ctx: &mut Context, symtable: &mut LocalSymtable<'_>, span: ForSpan) -> Result<()> { |
| 607 | if span.iter_double && span.iter.ref_type.is_none() { |
| 608 | match symtable.get_local_or_global(&span.iter) { |
| 609 | Ok(..) => { |
| 610 | // Keep existing iterators as-is. This mirrors core behavior where implicit |
| 611 | // widening to DOUBLE only happens when the iterator does not exist yet. |
| 612 | } |
| 613 | |
| 614 | Err(syms::Error::UndefinedSymbol(..)) => { |
| 615 | let key = SymbolKey::from(&span.iter.name); |
| 616 | if symtable.get_callable(&key).is_some() { |
| 617 | return Err(Error::from_syms( |
| 618 | syms::Error::AlreadyDefined(span.iter.clone()), |
| 619 | span.iter_pos, |
| 620 | )); |
| 621 | } |
| 622 | symtable |
| 623 | .put_local(key, SymbolPrototype::Scalar(ExprType::Double)) |
| 624 | .map_err(|e| Error::from_syms(e, span.iter_pos))?; |
| 625 | } |
| 626 | |
| 627 | Err(e) => return Err(Error::from_syms(e, span.iter_pos)), |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | compile_assignment( |
| 632 | &mut ctx.codegen, |
| 633 | symtable, |
| 634 | AssignmentSpan { vref: span.iter.clone(), vref_pos: span.iter_pos, expr: span.start }, |
| 635 | )?; |
| 636 | |
| 637 | let start_pc = ctx.codegen.next_pc(); |
| 638 | let (jump_end_pc, cond_reg, cond_pos) = { |
| 639 | let cond_pos = span.end.start_pos(); |
| 640 | let mut frozen = symtable.frozen(); |
| 641 | let mut scope = frozen.temp_scope(); |
| 642 | let reg = scope.alloc().map_err(|e| Error::from_syms(e, cond_pos))?; |
| 643 | compile_expr_as_type(&mut ctx.codegen, &mut frozen, reg, span.end, ExprType::Boolean)?; |
| 644 | (ctx.codegen.emit(bytecode::make_nop(), cond_pos), reg, cond_pos) |
| 645 | }; |
| 646 | ctx.codegen.mark_statement_start(start_pc); |
| 647 | |
| 648 | ctx.for_exit_stack.push(vec![]); |
| 649 | |
| 650 | for stmt in span.body { |
| 651 | compile_stmt(ctx, symtable, stmt)?; |
| 652 | } |
| 653 | |
| 654 | compile_assignment( |
| 655 | &mut ctx.codegen, |
| 656 | symtable, |
| 657 | AssignmentSpan { vref: span.iter, vref_pos: span.iter_pos, expr: span.next }, |
| 658 | )?; |
| 659 | |
| 660 | let start_target = |
| 661 | u16::try_from(start_pc).map_err(|_| Error::TargetTooFar(cond_pos, start_pc))?; |
| 662 | ctx.codegen.emit(bytecode::make_jump(start_target), cond_pos); |
| 663 |
no test coverage detected