1
//! Eval-only path — value-level interpretation that runs at compile
2
//! time for constant folding, macro expansion, and the type-inference
3
//! peek-ahead. `eval_value` is the entry point; everything else here
4
//! exists to support it.
5
//!
6
//! Distinct from the codegen path: `eval_value` doesn't emit any
7
//! wasm. The codegen entry points in [`super::compile`] /
8
//! [`super::effect`] / [`super::stack`] are the ones that produce
9
//! bytes; they use `eval_value` to decide whether a form is
10
//! constant-foldable or needs runtime emit.
11

            
12
use crate::ast::{Expr, LambdaParams};
13
use crate::error::{Error, Result};
14
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
15

            
16
use super::quasiquote::expand_quasiquote;
17

            
18
435533
pub(crate) fn call(symbols: &mut SymbolTable, elems: &[Expr]) -> Result<Expr> {
19
435533
    let (head, args) = elems
20
435533
        .split_first()
21
435533
        .ok_or_else(|| Error::Compile("empty function call".to_string()))?;
22
435533
    match head {
23
428869
        Expr::Symbol(name) => dispatch_symbol(symbols, name, args),
24
2244
        Expr::Quote(inner) => match inner.as_ref() {
25
2244
            Expr::Symbol(name) => dispatch_symbol(symbols, name, args),
26
            _ => Err(Error::Compile(format!("not callable: {head:?}"))),
27
        },
28
340
        Expr::Lambda(params, body) => call_lambda(symbols, params, body, args),
29
4080
        Expr::List(inner) => {
30
4080
            let resolved = call(symbols, inner)?;
31
4080
            match resolved {
32
4080
                Expr::Lambda(params, body) => call_lambda(symbols, &params, &body, args),
33
                _ => Err(Error::Compile("not callable".to_string())),
34
            }
35
        }
36
        _ => Err(Error::Compile(format!("not callable: {head:?}"))),
37
    }
38
435533
}
39

            
40
1347066
pub(crate) fn eval_value(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
41
422885
    match expr {
42
422885
        Expr::List(elems) if !elems.is_empty() => call(symbols, elems),
43
924181
        _ => resolve_arg(symbols, expr),
44
    }
45
1347066
}
46

            
47
431113
fn dispatch_symbol(symbols: &mut SymbolTable, name: &str, args: &[Expr]) -> Result<Expr> {
48
431113
    let (func, kind, value) = {
49
431113
        let sym = symbols
50
431113
            .lookup(name)
51
431113
            .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
52
431113
        (sym.function().cloned(), sym.kind(), sym.value().cloned())
53
    };
54
431113
    if matches!(kind, SymbolKind::Native)
55
7617
        && let Some(stand_in @ Expr::WasmRuntime(_)) = value
56
    {
57
        // Host-fn shim: the symbol carries the result type as a
58
        // WasmRuntime stand-in. Eval each arg so any nested constant
59
        // folding still runs, then surface the stand-in as the call's
60
        // value — no host-side dispatch happens until codegen.
61
7617
        for arg in args {
62
3468
            eval_value(symbols, arg)?;
63
        }
64
7617
        return Ok(stand_in);
65
423496
    }
66
423496
    if kind == SymbolKind::Macro
67
680
        && let Some(Expr::Lambda(params, body)) = func
68
    {
69
680
        return expand_macro_then(symbols, &params, &body, args, |symbols, code| {
70
680
            eval_value(symbols, &code)
71
680
        });
72
422816
    }
73
21278
    if let Some(Expr::Lambda(params, body)) = func {
74
        // A (mutually) recursive defun re-entered with a runtime arg never
75
        // folds to a base case — inlining it again would recurse the
76
        // compiler's stack forever. Hand it to the codegen monomorph path by
77
        // surfacing a runtime placeholder of the call's inferred result type;
78
        // the inline depth guard below is the backstop for any recursion the
79
        // re-entry check misses (e.g. const recursion with no base case).
80
21278
        if symbols.is_inlining(name)
81
4692
            && let Some(ret_ty) = recursive_runtime_call_type(symbols, &params, args)
82
        {
83
4148
            return Ok(Expr::WasmRuntime(ret_ty));
84
17130
        }
85
17130
        symbols.enter_inline(name)?;
86
17130
        let result = call_lambda(symbols, &params, &body, args);
87
17130
        symbols.exit_inline();
88
17130
        return result;
89
401538
    }
90
401538
    match kind {
91
        SymbolKind::Native | SymbolKind::Operator => {
92
306720
            crate::compiler::native::call(symbols, name, args)
93
        }
94
94478
        SymbolKind::SpecialForm => crate::compiler::special::call(symbols, name, args),
95
340
        _ => Err(Error::Compile(format!("symbol '{name}' is not callable"))),
96
    }
97
431113
}
98

            
99
/// The runtime result type to surface for a re-entrant recursive defun call,
100
/// or `None` when no argument resolves to a runtime value (so the call is
101
/// still const-foldable and should keep inlining). Must agree with what the
102
/// codegen monomorph path emits the helper at: `monomorph::initial_ret_guess`
103
/// is the FIRST required parameter's signature type, where each arg's
104
/// signature type comes from the shared `expr::classify_stack_type`. Each arg
105
/// is resolved on a CLONE so this probe applies no side effects to the live
106
/// table.
107
4692
fn recursive_runtime_call_type(
108
4692
    symbols: &mut SymbolTable,
109
4692
    params: &LambdaParams,
110
4692
    args: &[Expr],
111
4692
) -> Option<crate::ast::WasmType> {
112
4692
    let resolved: Vec<Expr> = args
113
4692
        .iter()
114
4692
        .map(|arg| eval_value(&mut symbols.clone(), arg).unwrap_or(Expr::Nil))
115
4692
        .collect();
116
4692
    if !resolved.iter().any(Expr::is_wasm_runtime) {
117
544
        return None;
118
4148
    }
119
    // `initial_ret_guess` = first param's signature type, classified the same
120
    // way the monomorph path classifies it; falls back to Ratio for a nullary
121
    // signature (matching `monomorph::initial_ret_guess`'s `unwrap_or`).
122
4148
    let first = params.required.first().and(resolved.first());
123
4148
    Some(
124
4148
        first
125
4148
            .and_then(super::stack::classify_stack_type)
126
4148
            .unwrap_or(crate::ast::WasmType::Ratio),
127
4148
    )
128
4692
}
129

            
130
/// Expand a macro call and run `process` on the resulting code, with a
131
/// depth guard spanning the whole expand-then-reprocess step. Every
132
/// expansion site (eval / effect / stack / call compile paths) routes
133
/// through here so a self-referential macro — whose re-expansion happens
134
/// inside `process` — is bounded uniformly and turned into a structured
135
/// compile error instead of a native stack overflow. The depth is carried
136
/// on the persistent `symbols` (threaded through the recursion) and
137
/// restored afterwards so sibling expansions don't accumulate.
138
15806
pub(crate) fn expand_macro_then<T>(
139
15806
    symbols: &mut SymbolTable,
140
15806
    params: &LambdaParams,
141
15806
    body: &Expr,
142
15806
    args: &[Expr],
143
15806
    process: impl FnOnce(&mut SymbolTable, Expr) -> Result<T>,
144
15806
) -> Result<T> {
145
15806
    symbols.enter_macro_expansion()?;
146
15738
    let result = expand_macro(symbols, params, body, args).and_then(|expansion| {
147
15602
        let code = match expansion {
148
10842
            Expr::Quote(inner) => *inner,
149
4760
            other => other,
150
        };
151
15602
        process(symbols, code)
152
15602
    });
153
15738
    symbols.exit_macro_expansion();
154
15738
    result
155
15806
}
156

            
157
25190
pub(crate) fn expand_macro(
158
25190
    symbols: &mut SymbolTable,
159
25190
    params: &LambdaParams,
160
25190
    body: &Expr,
161
25190
    args: &[Expr],
162
25190
) -> Result<Expr> {
163
25190
    if !params.aux.is_empty() {
164
        return Err(Error::Compile(
165
            "&aux not yet supported in macros".to_string(),
166
        ));
167
25190
    }
168

            
169
25190
    let min_args = params.required.len();
170
25190
    let max_args = if params.rest.is_some() || !params.key.is_empty() {
171
5334
        None
172
    } else {
173
19856
        Some(min_args + params.optional.len())
174
    };
175
25190
    if args.len() < min_args || max_args.is_some_and(|max| args.len() > max) {
176
68
        return Err(Error::Arity {
177
68
            name: "macro".to_string(),
178
68
            expected: min_args,
179
68
            actual: args.len(),
180
68
        });
181
25122
    }
182

            
183
25122
    let mut local_symbols = symbols.clone();
184
25122
    let mut arg_idx = 0;
185

            
186
25122
    for param in &params.required {
187
8802
        local_symbols
188
8802
            .define(Symbol::new(param, SymbolKind::Variable).with_value(args[arg_idx].clone()));
189
8802
        arg_idx += 1;
190
8802
    }
191

            
192
25122
    for (param, default) in &params.optional {
193
        let value = if arg_idx < args.len() {
194
            let arg = args[arg_idx].clone();
195
            arg_idx += 1;
196
            arg
197
        } else if let Some(default_expr) = default {
198
            default_expr.clone()
199
        } else {
200
            Expr::Nil
201
        };
202
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
203
    }
204

            
205
25122
    if let Some(rest_param) = &params.rest {
206
5266
        let rest = Expr::Quote(Box::new(Expr::List(args[arg_idx..].to_vec())));
207
5266
        local_symbols.define(Symbol::new(rest_param, SymbolKind::Variable).with_value(rest));
208
19886
    }
209

            
210
    // Bind key parameters in macros
211
25122
    if !params.key.is_empty() {
212
68
        let remaining_args = &args[arg_idx..];
213
68
        for (param, default) in &params.key {
214
68
            let keyword = Expr::Keyword(param.to_uppercase());
215
68
            let mut found_value = None;
216

            
217
            // Look for keyword argument pairs
218
68
            for i in (0..remaining_args.len() - 1).step_by(2) {
219
68
                if remaining_args[i] == keyword {
220
68
                    found_value = Some(remaining_args[i + 1].clone());
221
68
                    break;
222
                }
223
            }
224

            
225
68
            let value = if let Some(val) = found_value {
226
68
                val
227
            } else if let Some(default_expr) = default {
228
                default_expr.clone()
229
            } else {
230
                Expr::Nil
231
            };
232

            
233
68
            local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
234
        }
235
25054
    }
236

            
237
25122
    eval_value(&mut local_symbols, body)
238
25190
}
239

            
240
21550
fn call_lambda(
241
21550
    symbols: &mut SymbolTable,
242
21550
    params: &LambdaParams,
243
21550
    body: &Expr,
244
21550
    args: &[Expr],
245
21550
) -> Result<Expr> {
246
21550
    let min_args = params.required.len();
247
    // If we have &key or &rest, we can have unlimited args
248
21550
    let max_args = if params.rest.is_some() || !params.key.is_empty() {
249
612
        None
250
    } else {
251
20938
        Some(min_args + params.optional.len())
252
    };
253

            
254
21550
    if args.len() < min_args {
255
        return Err(Error::Arity {
256
            name: "lambda".to_string(),
257
            expected: min_args,
258
            actual: args.len(),
259
        });
260
21550
    }
261
21550
    if let Some(max) = max_args
262
20938
        && args.len() > max
263
    {
264
        return Err(Error::Arity {
265
            name: "lambda".to_string(),
266
            expected: max,
267
            actual: args.len(),
268
        });
269
21550
    }
270

            
271
21550
    let mut local_symbols = symbols.clone();
272
21550
    let mut arg_idx = 0;
273

            
274
    // Bind required parameters
275
24440
    for param in &params.required {
276
24440
        let resolved = eval_value(symbols, &args[arg_idx])?;
277
24440
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(resolved));
278
24440
        arg_idx += 1;
279
    }
280

            
281
    // Bind optional parameters
282
21550
    for (param, default) in &params.optional {
283
204
        let value = if arg_idx < args.len() {
284
68
            eval_value(symbols, &args[arg_idx])?
285
136
        } else if let Some(default_expr) = default {
286
68
            eval_value(symbols, default_expr)?
287
        } else {
288
68
            Expr::Nil
289
        };
290
204
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
291
204
        if arg_idx < args.len() {
292
68
            arg_idx += 1;
293
136
        }
294
    }
295

            
296
    // Bind rest parameter
297
21550
    if let Some(rest_param) = &params.rest {
298
204
        let rest_args: Vec<Expr> = args[arg_idx..]
299
204
            .iter()
300
340
            .map(|arg| eval_value(symbols, arg))
301
204
            .collect::<Result<_>>()?;
302
204
        let rest_list = if rest_args.is_empty() {
303
68
            Expr::Nil
304
        } else {
305
136
            Expr::List(rest_args)
306
        };
307
204
        local_symbols.define(Symbol::new(rest_param, SymbolKind::Variable).with_value(rest_list));
308
21346
    }
309

            
310
    // Bind key parameters
311
21550
    if !params.key.is_empty() {
312
        // Find keyword arguments in the remaining args
313
408
        let remaining_args = &args[arg_idx..];
314
952
        for (param, default) in &params.key {
315
952
            let keyword = Expr::Keyword(param.to_uppercase());
316
952
            let mut found_value = None;
317

            
318
            // Look for keyword argument pairs
319
952
            if remaining_args.len() >= 2 {
320
2244
                for i in (0..remaining_args.len() - 1).step_by(2) {
321
2244
                    if remaining_args[i] == keyword {
322
748
                        found_value = Some(eval_value(symbols, &remaining_args[i + 1])?);
323
748
                        break;
324
1496
                    }
325
                }
326
68
            }
327

            
328
952
            let value = if let Some(val) = found_value {
329
748
                val
330
204
            } else if let Some(default_expr) = default {
331
                eval_value(symbols, default_expr)?
332
            } else {
333
204
                Expr::Nil
334
            };
335

            
336
952
            local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
337
        }
338
21142
    }
339

            
340
    // Bind aux parameters (auxiliary variables)
341
21550
    for (param, init) in &params.aux {
342
136
        let value = if let Some(init_expr) = init {
343
136
            eval_value(&mut local_symbols, init_expr)?
344
        } else {
345
            Expr::Nil
346
        };
347
136
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
348
    }
349

            
350
21550
    eval_value(&mut local_symbols, body)
351
21550
}
352

            
353
1484319
pub(crate) fn resolve_arg(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
354
1484319
    match expr {
355
        Expr::Nil
356
        | Expr::Bool(_)
357
        | Expr::Number(_)
358
        | Expr::String(_)
359
        | Expr::Bytes(_)
360
        | Expr::Quote(_)
361
        | Expr::Keyword(_)
362
699652
        | Expr::RuntimeValue(_) => Ok(expr.clone()),
363
68
        Expr::Lambda(_, _) => Ok(expr.clone()),
364
5402
        Expr::Cons(_, _) | Expr::List(_) => Ok(expr.clone()),
365
5674
        Expr::Quasiquote(inner) => expand_quasiquote(symbols, inner),
366
        Expr::Unquote(_) | Expr::UnquoteSplicing(_) => {
367
            Err(Error::Compile("unquote outside of quasiquote".to_string()))
368
        }
369
480589
        Expr::Symbol(name) => {
370
476566
            let (value, kind, has_fn) = {
371
480589
                let sym = symbols
372
480589
                    .lookup(name)
373
480589
                    .ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
374
476566
                (sym.value().cloned(), sym.kind(), sym.function().is_some())
375
            };
376
476566
            if let Some(value) = value {
377
476294
                return resolve_arg(symbols, &value);
378
272
            }
379
            match kind {
380
136
                SymbolKind::Native | SymbolKind::Operator => Err(Error::Compile(format!(
381
136
                    "'{name}' is a function and cannot be used as a value"
382
136
                ))),
383
68
                SymbolKind::SpecialForm => Err(Error::Compile(format!(
384
68
                    "'{name}' is a special form and cannot be used as a value"
385
68
                ))),
386
68
                SymbolKind::Macro => Err(Error::Compile(format!(
387
68
                    "'{name}' is a macro and cannot be used as a value"
388
68
                ))),
389
                _ if has_fn => Err(Error::Compile(format!(
390
                    "'{name}' is a function; use #' or FUNCTION to access"
391
                ))),
392
                _ => Err(Error::Compile(format!("symbol '{name}' has no value"))),
393
            }
394
        }
395
292934
        Expr::WasmRuntime(_) | Expr::WasmLocal(_, _) => Ok(expr.clone()),
396
    }
397
1484319
}