Compiles a `SELECT` statement and emits bytecode into `ctx`.
(
ctx: &mut Context,
symtable: &mut LocalSymtable<'_>,
span: SelectSpan,
)
| 390 | |
| 391 | /// Compiles a `SELECT` statement and emits bytecode into `ctx`. |
| 392 | fn compile_select( |
| 393 | ctx: &mut Context, |
| 394 | symtable: &mut LocalSymtable<'_>, |
| 395 | span: SelectSpan, |
| 396 | ) -> Result<()> { |
| 397 | let end_pos = span.end_pos; |
| 398 | let ncases = span.cases.len(); |
| 399 | let select_cases = span.cases; |
| 400 | let select_expr = span.expr; |
| 401 | let select_expr_pos = select_expr.start_pos(); |
| 402 | |
| 403 | /// Captures the data needed to materialize a `CASE` body after dispatch generation. |
| 404 | struct PendingCase { |
| 405 | body: Vec<Statement>, |
| 406 | body_jump_pcs: Vec<(usize, LineCol)>, |
| 407 | has_next_case: bool, |
| 408 | } |
| 409 | |
| 410 | let mut pending_cases = Vec::with_capacity(ncases); |
| 411 | let mut pending_next_case_jump: Option<(usize, LineCol)> = None; |
| 412 | |
| 413 | symtable.with_reserved_temp( |
| 414 | |e| Error::from_syms(e, select_expr_pos), |
| 415 | |test_reg, frozen| { |
| 416 | let test_type = compile_expr(&mut ctx.codegen, frozen, test_reg, select_expr)?; |
| 417 | |
| 418 | for (i, case) in select_cases.into_iter().enumerate() { |
| 419 | let has_next_case = i < ncases - 1; |
| 420 | let mut body_jump_pcs = vec![]; |
| 421 | let case_dispatch_addr = ctx.codegen.next_pc(); |
| 422 | if let Some((jump_pc, pos)) = pending_next_case_jump.take() { |
| 423 | let target = u16::try_from(case_dispatch_addr) |
| 424 | .map_err(|_| Error::TargetTooFar(pos, case_dispatch_addr))?; |
| 425 | ctx.codegen.patch(jump_pc, bytecode::make_jump(target)); |
| 426 | } |
| 427 | |
| 428 | if case.guards.is_empty() { |
| 429 | let jump_body_pc = ctx.codegen.emit(bytecode::make_nop(), end_pos); |
| 430 | body_jump_pcs.push((jump_body_pc, end_pos)); |
| 431 | } else { |
| 432 | for guard in case.guards { |
| 433 | let (cond_reg, pos) = |
| 434 | compile_case_guard(ctx, frozen, test_reg, test_type, guard)?; |
| 435 | let jump_next_guard_pc = ctx.codegen.emit(bytecode::make_nop(), pos); |
| 436 | let jump_body_pc = ctx.codegen.emit(bytecode::make_nop(), pos); |
| 437 | body_jump_pcs.push((jump_body_pc, pos)); |
| 438 | |
| 439 | let next_addr = ctx.codegen.next_pc(); |
| 440 | let target = u16::try_from(next_addr) |
| 441 | .map_err(|_| Error::TargetTooFar(pos, next_addr))?; |
| 442 | ctx.codegen.patch( |
| 443 | jump_next_guard_pc, |
| 444 | bytecode::make_jump_if_false(cond_reg, target), |
| 445 | ); |
| 446 | } |
| 447 | |
| 448 | let jump_next_case_pc = ctx.codegen.emit(bytecode::make_nop(), end_pos); |
| 449 | pending_next_case_jump = Some((jump_next_case_pc, end_pos)); |
no test coverage detected