Lines
75.73 %
Functions
20 %
Branches
100 %
//! `REVERSE` — produce the input list in reversed order. Constant-folds
//! when the input is a compile-time list literal; for runtime
//! `PairRef(elem)` chains, walks the input cell-by-cell building a fresh
//! reversed chain via the `pair_new` helper.
use crate::ast::{Expr, PairElement, WasmType};
use crate::compiler::context::CompileContext;
use crate::compiler::emit::FunctionEmitter;
use crate::compiler::expr::{
compile_expr, compile_for_stack, eval_value, format_expr, serialize_stack_to_output,
};
use crate::error::{Error, Result};
use crate::runtime::SymbolTable;
use super::datum::{compile_folded_to_stack, is_datum_result};
pub(super) fn reverse(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 1 {
return Err(Error::Arity {
name: "REVERSE".to_string(),
expected: 1,
actual: args.len(),
});
}
let arg = eval_value(symbols, &args[0])?;
if let Some(WasmType::PairRef(elem)) = arg.wasm_type() {
return Ok(Expr::WasmRuntime(WasmType::PairRef(elem)));
let elements = match arg {
Expr::Nil => return Ok(Expr::Nil),
Expr::List(elems) => elems,
Expr::Quote(inner) => match *inner {
other => {
return Err(Error::Compile(format!(
"REVERSE expects a list, got {}",
format_expr(&other)
)));
},
let reversed: Vec<Expr> = elements.into_iter().rev().collect();
Ok(Expr::Quote(Box::new(Expr::List(reversed))))
pub(super) fn compile_reverse(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
let folded = reverse(symbols, args)?;
if matches!(folded.wasm_type(), Some(WasmType::PairRef(_))) {
let ty = compile_reverse_to_stack(ctx, emit, symbols, args)?;
serialize_stack_to_output(ctx, emit, ty)?;
return Ok(());
// A fully-constant (reverse '(…)) folds to a quoted reversed list — render
// it as a datum so both compile surfaces agree (it used to trap on the
// eval-with-type stack path with no runtime pair).
if is_datum_result(&folded) {
let ty = compile_folded_to_stack(ctx, emit, symbols, folded)?;
return serialize_stack_to_output(ctx, emit, ty);
compile_expr(ctx, emit, symbols, &folded)
pub(super) fn compile_reverse_to_stack(
) -> Result<WasmType> {
let resolved = eval_value(symbols, &args[0])?;
let elem = match resolved.wasm_type() {
Some(WasmType::PairRef(e)) => e,
_ => {
// Not a runtime pair: const-fold and render the reversed datum,
// matching the effect path (and CAR/CDR) so a constant or
// runtime-builder list at value position lowers instead of trapping.
return compile_folded_to_stack(ctx, emit, symbols, folded);
emit_reverse_loop(ctx, emit, symbols, &args[0], elem)?;
Ok(WasmType::PairRef(elem))
/// Walks `list_expr` cell by cell, prepending each car onto a fresh
/// accumulator chain. The accumulator is initialised to a null `$pair`
/// ref and rebuilt every iteration via `pair_new(car, acc)`. After the
/// loop, the accumulator holds the input reversed and is left on the
/// wasm stack.
pub(in crate::compiler::native::list) fn emit_reverse_loop(
list_expr: &Expr,
elem: PairElement,
let pair_idx = ctx.ids.ty_pair;
let pair_local = ctx.alloc_local(WasmType::PairRef(elem))?;
let acc_local = ctx.alloc_local(WasmType::PairRef(elem))?;
compile_for_stack(ctx, emit, symbols, list_expr)?;
emit.local_set(pair_local);
emit.ref_null(pair_idx);
emit.local_set(acc_local);
emit.block_start();
emit.loop_start();
emit.local_get(pair_local);
emit.ref_is_null();
emit.br_if(1);
// pair_new expects (anyref car, ref null $pair cdr). The car comes
// straight off the input pair as anyref — no downcast needed since
// we're rebuilding into a fresh pair of the same element type.
emit.struct_get(pair_idx, 0);
emit.local_get(acc_local);
emit.call(ctx.ids.pair_new);
emit.struct_get(pair_idx, 1);
emit.br(0);
emit.block_end();
Ok(())