Lines
57.98 %
Functions
17.93 %
Branches
100 %
use wasm_encoder::BlockType;
use crate::ast::{Expr, WasmType};
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
use super::super::context::CompileContext;
use super::super::emit::FunctionEmitter;
use super::super::expr::{
compile_body, compile_body_for_stack, compile_expr, compile_for_effect, compile_for_stack,
compile_nil, compile_quoted_expr, eval_value, serialize_stack_to_output,
};
pub(super) fn compile_quote(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
args: &[Expr],
) -> Result<()> {
if args.len() != 1 {
return Err(Error::Arity {
name: "quote".to_string(),
expected: 1,
actual: args.len(),
});
}
compile_quoted_expr(ctx, emit, &args[0])
pub(super) fn compile_if(
symbols: &mut SymbolTable,
if args.len() < 2 || args.len() > 3 {
return Err(Error::Compile(
"IF requires a test, a then-form, and an optional else-form".to_string(),
));
let test = eval_value(symbols, &args[0])?;
if matches!(test, Expr::WasmRuntime(WasmType::I32)) {
// Runtime test — compile condition to stack, then emit WASM if/else
compile_for_stack(ctx, emit, symbols, &args[0])?;
emit.if_block(BlockType::Empty);
compile_expr(ctx, emit, symbols, &args[1])?;
if args.len() == 3 {
emit.else_block();
compile_expr(ctx, emit, symbols, &args[2])?;
emit.block_end();
return Ok(());
if is_truthy(&test) {
compile_expr(ctx, emit, symbols, &args[1])
} else if args.len() == 3 {
compile_expr(ctx, emit, symbols, &args[2])
} else {
compile_nil(ctx, emit);
Ok(())
pub(super) fn compile_begin(
if args.is_empty() {
compile_body(ctx, emit, symbols, args)
pub(super) fn compile_begin_for_stack(
) -> Result<WasmType> {
emit.i32_const(0);
return Ok(WasmType::I32);
compile_body_for_stack(ctx, emit, symbols, args)
pub(super) fn compile_and(
return compile_expr(ctx, emit, symbols, &Expr::Bool(true));
let mut last = Expr::Bool(true);
for (i, arg) in args.iter().enumerate() {
let value = eval_value(symbols, arg)?;
if matches!(value, Expr::WasmRuntime(WasmType::I32)) {
// Runtime AND: remaining args compiled as runtime short-circuit
return compile_and_runtime(ctx, emit, symbols, &args[i..]);
if !is_truthy(&value) {
return compile_expr(ctx, emit, symbols, &value);
last = value;
compile_expr(ctx, emit, symbols, &last)
pub(super) fn compile_or(
let mut last = Expr::Nil;
return compile_or_runtime(ctx, emit, symbols, &args[i..]);
if is_truthy(&value) {
pub(super) fn compile_cond(
for (i, clause) in args.iter().enumerate() {
let elems = clause.as_list().ok_or_else(|| {
Error::Compile(format!("COND: clause must be a list, got {clause:?}"))
})?;
if elems.is_empty() {
return Err(Error::Compile("COND: empty clause".to_string()));
let test = eval_value(symbols, &elems[0])?;
return compile_cond_runtime(ctx, emit, symbols, &args[i..]);
if elems.len() == 1 {
return compile_expr(ctx, emit, symbols, &test);
return compile_body(ctx, emit, symbols, &elems[1..]);
/// Runtime AND: first arg is already on WASM stack as i32.
/// Emit short-circuit: if first is false, skip rest.
fn compile_and_runtime(
// Compile first arg for stack (already known to be runtime I32)
if args.len() == 1 {
serialize_stack_to_output(ctx, emit, WasmType::I32);
// if first-arg != 0, evaluate rest; otherwise skip
for arg in &args[1..args.len() - 1] {
compile_for_stack(ctx, emit, symbols, arg)?;
// Check intermediate: if false, skip to end
// Last arg — compile for side effects
compile_expr(ctx, emit, symbols, args.last().unwrap())?;
// Close nested if blocks
for _ in 0..args.len() - 1 {
/// Runtime OR: first arg is already on WASM stack as i32.
/// Emit short-circuit: if first is true, skip rest.
fn compile_or_runtime(
// Compile first arg for stack
// if first-arg == 0 (false), evaluate rest
emit.i32_eqz();
/// Runtime COND: compile remaining clauses as nested if/else.
fn compile_cond_runtime(
let depth = args.len();
let is_last = i == depth - 1;
// Runtime test on stack
compile_for_stack(ctx, emit, symbols, &elems[0])?;
compile_body(ctx, emit, symbols, &elems[1..])?;
if !is_last {
} else if is_truthy(&test) {
// Constant true — always taken, compile body and stop
// Close all open if blocks
for _ in 0..i {
// Constant false — skip this clause
// Close all if blocks
for _ in 0..depth {
/// Compile IF for stack — both branches produce a value on the WASM stack.
pub(super) fn compile_if_for_stack(
// Runtime test on stack — emit if/else that produces a value
let then_ty = eval_value(symbols, &args[1])?;
let result_ty = match then_ty {
Expr::WasmRuntime(t) => t,
_ => WasmType::I32,
let val_type = ctx.wasm_val_type(result_ty);
emit.if_block(BlockType::Result(val_type));
compile_for_stack(ctx, emit, symbols, &args[1])?;
compile_for_stack(ctx, emit, symbols, &args[2])?;
return Ok(result_ty);
compile_for_stack(ctx, emit, symbols, &args[1])
compile_for_stack(ctx, emit, symbols, &args[2])
Ok(WasmType::I32)
/// Compile AND for stack — returns I32 (boolean result).
pub(super) fn compile_and_for_stack(
emit.i32_const(1);
return compile_for_stack(ctx, emit, symbols, &args[0]);
// Compile first arg
for arg in &args[1..] {
// Short-circuit: if top is false (0), skip rest
emit.if_block(BlockType::Result(wasm_encoder::ValType::I32));
/// Compile OR for stack — returns I32 (boolean result).
pub(super) fn compile_or_for_stack(
// Short-circuit: if top is true (nonzero), keep it; otherwise try next
pub(super) fn compile_cond_for_effect(
if test.is_wasm_runtime() {
return compile_cond_runtime_for_effect(ctx, emit, symbols, &args[i..]);
for expr in &elems[1..] {
compile_for_effect(ctx, emit, symbols, expr)?;
fn compile_cond_runtime_for_effect(
pub(super) fn compile_and_for_effect(
if value.is_wasm_runtime() {
for remaining in &args[i + 1..] {
compile_for_effect(ctx, emit, symbols, remaining)?;
pub(super) fn compile_or_for_effect(
pub(in crate::compiler) fn is_truthy(expr: &Expr) -> bool {
!matches!(expr, Expr::Nil | Expr::Bool(false))
pub(super) fn if_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if matches!(test, Expr::WasmRuntime(_)) {
// Runtime test — both branches may produce runtime values
let else_ty = if args.len() == 3 {
eval_value(symbols, &args[2])?
Expr::Nil
return match (&then_ty, &else_ty) {
(Expr::WasmRuntime(t), _) | (_, Expr::WasmRuntime(t)) => Ok(Expr::WasmRuntime(*t)),
_ => Ok(Expr::WasmRuntime(WasmType::I32)),
eval_value(symbols, &args[1])
eval_value(symbols, &args[2])
Ok(Expr::Nil)
pub(super) fn begin_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
return Ok(Expr::Nil);
super::binding::eval_body(symbols, args)
pub(super) fn and_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
return Ok(Expr::Bool(true));
for arg in args {
if matches!(value, Expr::WasmRuntime(_)) {
return Ok(Expr::WasmRuntime(WasmType::I32));
return Ok(value);
Ok(last)
pub(super) fn or_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
pub(super) fn cond_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
for clause in args {
return Ok(test);
return super::binding::eval_body(symbols, &elems[1..]);
pub(super) fn quote(args: &[Expr]) -> Result<Expr> {
Ok(args[0].clone())