Lines
75.31 %
Functions
10 %
Branches
100 %
//! `LENGTH` — count the cells of a list. Constant-folds over a list
//! literal; for a runtime `PairRef` chain, walks the cells with an i32
//! counter. ADR-0028: a length is a count, so the result is an **Index**
//! (`I32`) — it composes with Index arithmetic / comparison, and crosses to
//! Scalar only via an explicit `(index->scalar …)`.
use crate::ast::{Expr, Fraction, 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::map::extract_list_elements;
pub(super) fn length(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
if args.len() != 1 {
return Err(Error::Arity {
name: "LENGTH".to_string(),
expected: 1,
actual: args.len(),
});
}
let arg = eval_value(symbols, &args[0])?;
if matches!(arg.wasm_type(), Some(WasmType::PairRef(_))) {
return Ok(Expr::WasmRuntime(WasmType::I32));
let count = extract_list_elements(&arg)
.map_err(|_| Error::Compile(format!("LENGTH expects a list, got {}", format_expr(&arg))))?
.len();
Ok(Expr::Number(Fraction::from_integer(count as i64)))
pub(super) fn compile_length(
ctx: &mut CompileContext,
emit: &mut FunctionEmitter,
symbols: &mut SymbolTable,
args: &[Expr],
) -> Result<()> {
let folded = length(symbols, args)?;
if folded.is_wasm_runtime() {
let ty = compile_length_to_stack(ctx, emit, symbols, args)?;
return serialize_stack_to_output(ctx, emit, ty);
compile_expr(ctx, emit, symbols, &folded)
pub(super) fn compile_length_to_stack(
) -> Result<WasmType> {
let resolved = eval_value(symbols, &args[0])?;
let elem = match resolved.wasm_type() {
Some(WasmType::PairRef(e)) => e,
_ => {
// Constant list folds to its count (an Index) straight onto the stack.
let count = extract_list_elements(&resolved).map_err(|_| {
Error::Compile(format!(
"LENGTH expects a list, got {}",
format_expr(&resolved)
))
})?;
let count = i32::try_from(count.len())
.map_err(|_| Error::Compile("LENGTH: list length exceeds i32 range".to_string()))?;
emit.i32_const(count);
return Ok(WasmType::I32);
let pair_idx = ctx.ids.ty_pair;
let pair_local = ctx.alloc_local(WasmType::PairRef(elem))?;
let count_local = ctx.alloc_local(WasmType::I32)?;
compile_for_stack(ctx, emit, symbols, &args[0])?;
emit.local_set(pair_local);
emit.i32_const(0);
emit.local_set(count_local);
emit.block_start();
emit.loop_start();
emit.local_get(pair_local);
emit.ref_is_null();
emit.br_if(1);
emit.local_get(count_local);
emit.i32_const(1);
emit.i32_add();
emit.struct_get(pair_idx, 1);
emit.br(0);
emit.block_end();
Ok(WasmType::I32)