Compiles a `DO` loop and emits bytecode into `ctx`.
(ctx: &mut Context, symtable: &mut LocalSymtable<'_>, span: DoSpan)
| 493 | |
| 494 | /// Compiles a `DO` loop and emits bytecode into `ctx`. |
| 495 | fn compile_do(ctx: &mut Context, symtable: &mut LocalSymtable<'_>, span: DoSpan) -> Result<()> { |
| 496 | /// Compiles one loop guard expression to a temporary boolean register. |
| 497 | fn compile_guard( |
| 498 | ctx: &mut Context, |
| 499 | symtable: &mut LocalSymtable<'_>, |
| 500 | guard: Expr, |
| 501 | ) -> Result<(Register, LineCol)> { |
| 502 | let guard_pos = guard.start_pos(); |
| 503 | let mut frozen = symtable.frozen(); |
| 504 | let mut scope = frozen.temp_scope(); |
| 505 | let reg = scope.alloc().map_err(|e| Error::from_syms(e, guard_pos))?; |
| 506 | compile_expr_as_type(&mut ctx.codegen, &mut frozen, reg, guard, ExprType::Boolean)?; |
| 507 | Ok((reg, guard_pos)) |
| 508 | } |
| 509 | |
| 510 | ctx.do_exit_stack.push(vec![]); |
| 511 | |
| 512 | let end_addr = match span.guard { |
| 513 | DoGuard::Infinite => { |
| 514 | let start_pc = ctx.codegen.next_pc(); |
| 515 | for stmt in span.body { |
| 516 | compile_stmt(ctx, symtable, stmt)?; |
| 517 | } |
| 518 | let end_pos = LineCol { line: 0, col: 0 }; |
| 519 | let target = |
| 520 | u16::try_from(start_pc).map_err(|_| Error::TargetTooFar(end_pos, start_pc))?; |
| 521 | ctx.codegen.emit(bytecode::make_jump(target), end_pos); |
| 522 | ctx.codegen.next_pc() |
| 523 | } |
| 524 | |
| 525 | DoGuard::PreUntil(guard) => { |
| 526 | let start_pc = ctx.codegen.next_pc(); |
| 527 | let (cond_reg, guard_pos) = compile_guard(ctx, symtable, guard)?; |
| 528 | let jump_body_pc = ctx.codegen.emit(bytecode::make_nop(), guard_pos); |
| 529 | let jump_end_pc = ctx.codegen.emit(bytecode::make_nop(), guard_pos); |
| 530 | let body_addr = ctx.codegen.next_pc(); |
| 531 | let body_target = |
| 532 | u16::try_from(body_addr).map_err(|_| Error::TargetTooFar(guard_pos, body_addr))?; |
| 533 | ctx.codegen.patch(jump_body_pc, bytecode::make_jump_if_false(cond_reg, body_target)); |
| 534 | |
| 535 | for stmt in span.body { |
| 536 | compile_stmt(ctx, symtable, stmt)?; |
| 537 | } |
| 538 | let start_target = |
| 539 | u16::try_from(start_pc).map_err(|_| Error::TargetTooFar(guard_pos, start_pc))?; |
| 540 | ctx.codegen.emit(bytecode::make_jump(start_target), guard_pos); |
| 541 | let end_addr = ctx.codegen.next_pc(); |
| 542 | let end_target = |
| 543 | u16::try_from(end_addr).map_err(|_| Error::TargetTooFar(guard_pos, end_addr))?; |
| 544 | ctx.codegen.patch(jump_end_pc, bytecode::make_jump(end_target)); |
| 545 | end_addr |
| 546 | } |
| 547 | |
| 548 | DoGuard::PreWhile(guard) => { |
| 549 | let start_pc = ctx.codegen.next_pc(); |
| 550 | let (cond_reg, guard_pos) = compile_guard(ctx, symtable, guard)?; |
| 551 | let jump_end_pc = ctx.codegen.emit(bytecode::make_nop(), guard_pos); |
| 552 |
no test coverage detected