1
//! `LENGTH` — count the cells of a list. Constant-folds over a list
2
//! literal; for a runtime `PairRef` chain, walks the cells with an i32
3
//! counter. ADR-0028: a length is a count, so the result is an **Index**
4
//! (`I32`) — it composes with Index arithmetic / comparison, and crosses to
5
//! Scalar only via an explicit `(index->scalar …)`.
6

            
7
use crate::ast::{Expr, Fraction, WasmType};
8
use crate::compiler::context::CompileContext;
9
use crate::compiler::emit::FunctionEmitter;
10
use crate::compiler::expr::{
11
    compile_expr, compile_for_stack, eval_value, format_expr, serialize_stack_to_output,
12
};
13
use crate::error::{Error, Result};
14
use crate::runtime::SymbolTable;
15

            
16
use super::map::extract_list_elements;
17

            
18
2490
pub(super) fn length(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
19
2490
    if args.len() != 1 {
20
        return Err(Error::Arity {
21
            name: "LENGTH".to_string(),
22
            expected: 1,
23
            actual: args.len(),
24
        });
25
2490
    }
26
2490
    let arg = eval_value(symbols, &args[0])?;
27
2490
    if matches!(arg.wasm_type(), Some(WasmType::PairRef(_))) {
28
2324
        return Ok(Expr::WasmRuntime(WasmType::I32));
29
166
    }
30
166
    let count = extract_list_elements(&arg)
31
166
        .map_err(|_| Error::Compile(format!("LENGTH expects a list, got {}", format_expr(&arg))))?
32
166
        .len();
33
166
    Ok(Expr::Number(Fraction::from_integer(count as i64)))
34
2490
}
35

            
36
166
pub(super) fn compile_length(
37
166
    ctx: &mut CompileContext,
38
166
    emit: &mut FunctionEmitter,
39
166
    symbols: &mut SymbolTable,
40
166
    args: &[Expr],
41
166
) -> Result<()> {
42
166
    let folded = length(symbols, args)?;
43
166
    if folded.is_wasm_runtime() {
44
83
        let ty = compile_length_to_stack(ctx, emit, symbols, args)?;
45
83
        return serialize_stack_to_output(ctx, emit, ty);
46
83
    }
47
83
    compile_expr(ctx, emit, symbols, &folded)
48
166
}
49

            
50
1245
pub(super) fn compile_length_to_stack(
51
1245
    ctx: &mut CompileContext,
52
1245
    emit: &mut FunctionEmitter,
53
1245
    symbols: &mut SymbolTable,
54
1245
    args: &[Expr],
55
1245
) -> Result<WasmType> {
56
1245
    if args.len() != 1 {
57
        return Err(Error::Arity {
58
            name: "LENGTH".to_string(),
59
            expected: 1,
60
            actual: args.len(),
61
        });
62
1245
    }
63
1245
    let resolved = eval_value(symbols, &args[0])?;
64
1245
    let elem = match resolved.wasm_type() {
65
1162
        Some(WasmType::PairRef(e)) => e,
66
        _ => {
67
            // Constant list folds to its count (an Index) straight onto the stack.
68
83
            let count = extract_list_elements(&resolved).map_err(|_| {
69
                Error::Compile(format!(
70
                    "LENGTH expects a list, got {}",
71
                    format_expr(&resolved)
72
                ))
73
            })?;
74
83
            let count = i32::try_from(count.len())
75
83
                .map_err(|_| Error::Compile("LENGTH: list length exceeds i32 range".to_string()))?;
76
83
            emit.i32_const(count);
77
83
            return Ok(WasmType::I32);
78
        }
79
    };
80

            
81
1162
    let pair_idx = ctx.ids.ty_pair;
82
1162
    let pair_local = ctx.alloc_local(WasmType::PairRef(elem))?;
83
1162
    let count_local = ctx.alloc_local(WasmType::I32)?;
84

            
85
1162
    compile_for_stack(ctx, emit, symbols, &args[0])?;
86
1162
    emit.local_set(pair_local);
87
1162
    emit.i32_const(0);
88
1162
    emit.local_set(count_local);
89

            
90
1162
    emit.block_start();
91
1162
    emit.loop_start();
92

            
93
1162
    emit.local_get(pair_local);
94
1162
    emit.ref_is_null();
95
1162
    emit.br_if(1);
96

            
97
1162
    emit.local_get(count_local);
98
1162
    emit.i32_const(1);
99
1162
    emit.i32_add();
100
1162
    emit.local_set(count_local);
101

            
102
1162
    emit.local_get(pair_local);
103
1162
    emit.struct_get(pair_idx, 1);
104
1162
    emit.local_set(pair_local);
105

            
106
1162
    emit.br(0);
107
1162
    emit.block_end();
108
1162
    emit.block_end();
109

            
110
1162
    emit.local_get(count_local);
111
1162
    Ok(WasmType::I32)
112
1245
}