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, WasmType};
35
use crate::compiler::context::CompileContext;
36
use crate::compiler::expr::eval_value;
37
use crate::error::Result;
38
use crate::runtime::SymbolTable;
39

            
40
pub(in crate::compiler) use car_cdr::emit_pair_car_downcast;
41

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

            
77
pub(in crate::compiler::native) const NATIVES: &[NativeSpec] = &[
78
    NativeSpec {
79
        name: "LIST",
80
        eval: list_fn::list,
81
        stack: Some(list_fn::compile_list_to_stack),
82
        effect: Some(list_fn::compile_list),
83
    },
84
    NativeSpec {
85
        name: "CONS",
86
        eval: cons::cons,
87
        stack: Some(cons::compile_cons_to_stack),
88
        effect: Some(cons::compile_cons),
89
    },
90
    NativeSpec {
91
        name: "CAR",
92
        eval: car_cdr::car,
93
        stack: Some(car_cdr::compile_car_to_stack),
94
        effect: Some(car_cdr::compile_car),
95
    },
96
    NativeSpec {
97
        name: "CDR",
98
        eval: car_cdr::cdr,
99
        stack: Some(car_cdr::compile_cdr_to_stack),
100
        effect: Some(car_cdr::compile_cdr),
101
    },
102
    NativeSpec {
103
        name: "MAP",
104
        eval: map::map_fn,
105
        stack: Some(map::compile_map_to_stack),
106
        effect: Some(map::compile_map),
107
    },
108
    NativeSpec {
109
        name: "FILTER",
110
        eval: filter::filter,
111
        stack: Some(filter::compile_filter_to_stack),
112
        effect: Some(filter::compile_filter),
113
    },
114
    NativeSpec {
115
        name: "FOLD",
116
        eval: fold::fold,
117
        stack: Some(fold::compile_fold_to_stack),
118
        effect: Some(fold::compile_fold),
119
    },
120
    NativeSpec {
121
        name: "REVERSE",
122
        eval: reverse::reverse,
123
        stack: Some(reverse::compile_reverse_to_stack),
124
        effect: Some(reverse::compile_reverse),
125
    },
126
    NativeSpec {
127
        name: "LENGTH",
128
        eval: length::length,
129
        stack: Some(length::compile_length_to_stack),
130
        effect: Some(length::compile_length),
131
    },
132
    NativeSpec {
133
        name: "APPEND",
134
        eval: append::append,
135
        stack: Some(append::compile_append_to_stack),
136
        effect: Some(append::compile_append),
137
    },
138
    NativeSpec {
139
        name: "PAIR?",
140
        eval: pair_p::pair_p,
141
        stack: Some(pair_p::compile_pair_p_to_stack),
142
        effect: Some(pair_p::compile_pair_p),
143
    },
144
];