1
//! List-shaped natives: `LIST`, `CONS`, `CAR`, `CDR`, `MAP`, `FILTER`,
2
//! `FOLD`, `REVERSE`.
3
//!
4
//! Split by form:
5
//! - [`infer`] — `infer_pair_element` (static element-type inference
6
//!   for CONS) + per-shape unit tests.
7
//! - [`list_fn`] — `LIST` eval + compile paths.
8
//! - [`cons`] — `CONS` eval + compile + the car/cdr push helpers.
9
//! - [`car_cdr`] — `CAR` / `CDR` eval + compile + the shared
10
//!   `emit_pair_car_downcast` helper reused by `DOLIST`.
11
//! - [`map`] — `MAP` eval + compile + `extract_list_elements`.
12
//! - [`filter`] — `FILTER` eval + compile, predicate-truthy keeps.
13
//! - [`fold`] — `FOLD` eval + compile, left fold threading accumulator.
14
//! - [`reverse`] — `REVERSE` eval + compile, runtime path emits a
15
//!   prepend-loop using `pair_new` as the cell allocator.
16

            
17
mod append;
18
mod car_cdr;
19
mod cons;
20
mod datum;
21
mod filter;
22
mod fold;
23
mod infer;
24
mod length;
25
mod list_fn;
26
mod map;
27
mod pair_p;
28
mod reverse;
29

            
30
#[cfg(test)]
31
mod tests;
32

            
33
use super::NativeSpec;
34
use crate::ast::{Expr, PairElement, WasmType};
35
use crate::compiler::context::CompileContext;
36
use crate::compiler::emit::FunctionEmitter;
37
use crate::compiler::expr::eval_value;
38
use crate::error::Result;
39
use crate::runtime::SymbolTable;
40

            
41
pub(in crate::compiler) use car_cdr::emit_pair_car_downcast;
42
pub(in crate::compiler) use infer::{chain_pair_element, literal_pair_element};
43

            
44
/// The function expression FOLD should apply per element. If `fn_arg` resolves
45
/// to a let-bound closure whose source body was recorded, returns that
46
/// `(lambda …)` so FOLD INLINES it — binding each parameter to the actual
47
/// element/accumulator type — instead of `call_ref`ing the closure's fixed
48
/// (Ratio-default) signature, which mismatches a non-Ratio element list.
49
/// Otherwise returns `fn_arg` unchanged (an inline lambda already types from its
50
/// args; a non-recoverable runtime closure keeps `call_ref`).
51
///
52
/// FOLD-only on purpose: MAP / FILTER build a fresh result list whose element
53
/// type the eval surface sizes from the closure SIG, so inlining there (which
54
/// re-types per the actual element) drifts the eval-vs-codegen element type.
55
/// They keep the `call_ref` path, relying on `param_infer` to pin non-Ratio
56
/// params (a non-pinnable non-Ratio closure surfaces a clean type error there).
57
///
58
/// The body was vetted capture-free against its CREATION scope; inlining
59
/// recompiles it under THIS (fold-site) symbol table, so a global the body uses
60
/// (`+`, `cons`, …) that an inner binder has since shadowed would resolve to the
61
/// shadow instead of the creation-site definition the emitted closure used. Re-
62
/// run the capture-free check against the fold-site table: a now-shadowed global
63
/// resolves to a `Variable` here and fails it, so we keep `call_ref` (which calls
64
/// the closure value compiled at creation) — correct, not inlined-wrong.
65
2490
fn inline_closure_fn(
66
2490
    ctx: &CompileContext,
67
2490
    symbols: &mut SymbolTable,
68
2490
    fn_arg: &Expr,
69
2490
) -> Result<Expr> {
70
2490
    if let Expr::WasmLocal(idx, WasmType::Closure(_)) = eval_value(symbols, fn_arg)?
71
996
        && let Some((params, body)) = ctx.closure_body(idx)
72
747
        && crate::compiler::special::is_capture_free(symbols, params, body)
73
    {
74
581
        return Ok(Expr::Lambda(params.clone(), Box::new(body.clone())));
75
1909
    }
76
1909
    Ok(fn_arg.clone())
77
2490
}
78

            
79
/// Materializes a resolved constant list's `members` as a `$pair` chain with
80
/// cells of `elem`, for a consumer whose source form (e.g. a let-bound list
81
/// symbol) does not itself lower to a chain.
82
1992
pub(in crate::compiler) fn compile_constant_list(
83
1992
    ctx: &mut CompileContext,
84
1992
    emit: &mut FunctionEmitter,
85
1992
    symbols: &mut SymbolTable,
86
1992
    members: Vec<Expr>,
87
1992
    elem: PairElement,
88
1992
) -> Result<WasmType> {
89
1992
    cons::emit_constant_chain(ctx, emit, symbols, members, elem)?;
90
1992
    Ok(WasmType::PairRef(elem))
91
1992
}
92

            
93
pub(in crate::compiler::native) const NATIVES: &[NativeSpec] = &[
94
    NativeSpec {
95
        name: "LIST",
96
        eval: list_fn::list,
97
        stack: Some(list_fn::compile_list_to_stack),
98
        effect: Some(list_fn::compile_list),
99
    },
100
    NativeSpec {
101
        name: "CONS",
102
        eval: cons::cons,
103
        stack: Some(cons::compile_cons_to_stack),
104
        effect: Some(cons::compile_cons),
105
    },
106
    NativeSpec {
107
        name: "CAR",
108
        eval: car_cdr::car,
109
        stack: Some(car_cdr::compile_car_to_stack),
110
        effect: Some(car_cdr::compile_car),
111
    },
112
    NativeSpec {
113
        name: "CDR",
114
        eval: car_cdr::cdr,
115
        stack: Some(car_cdr::compile_cdr_to_stack),
116
        effect: Some(car_cdr::compile_cdr),
117
    },
118
    NativeSpec {
119
        name: "MAP",
120
        eval: map::map_fn,
121
        stack: Some(map::compile_map_to_stack),
122
        effect: Some(map::compile_map),
123
    },
124
    NativeSpec {
125
        name: "FILTER",
126
        eval: filter::filter,
127
        stack: Some(filter::compile_filter_to_stack),
128
        effect: Some(filter::compile_filter),
129
    },
130
    NativeSpec {
131
        name: "FOLD",
132
        eval: fold::fold,
133
        stack: Some(fold::compile_fold_to_stack),
134
        effect: Some(fold::compile_fold),
135
    },
136
    NativeSpec {
137
        name: "REVERSE",
138
        eval: reverse::reverse,
139
        stack: Some(reverse::compile_reverse_to_stack),
140
        effect: Some(reverse::compile_reverse),
141
    },
142
    NativeSpec {
143
        name: "LENGTH",
144
        eval: length::length,
145
        stack: Some(length::compile_length_to_stack),
146
        effect: Some(length::compile_length),
147
    },
148
    NativeSpec {
149
        name: "APPEND",
150
        eval: append::append,
151
        stack: Some(append::compile_append_to_stack),
152
        effect: Some(append::compile_append),
153
    },
154
    NativeSpec {
155
        name: "PAIR?",
156
        eval: pair_p::pair_p,
157
        stack: Some(pair_p::compile_pair_p_to_stack),
158
        effect: Some(pair_p::compile_pair_p),
159
    },
160
];