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
454336
pub(crate) fn call(symbols: &mut SymbolTable, elems: &[Expr]) -> Result<Expr> {
19
454336
    let (head, args) = elems
20
454336
        .split_first()
21
454336
        .ok_or_else(|| Error::Compile("empty function call".to_string()))?;
22
454336
    match head {
23
447378
        Expr::Symbol(name) => dispatch_symbol(symbols, name, args),
24
2343
        Expr::Quote(inner) => match inner.as_ref() {
25
2343
            Expr::Symbol(name) => dispatch_symbol(symbols, name, args),
26
            _ => Err(Error::Compile(format!("not callable: {head:?}"))),
27
        },
28
355
        Expr::Lambda(params, body) => call_lambda(symbols, params, body, args),
29
4260
        Expr::List(inner) => {
30
4260
            let resolved = call(symbols, inner)?;
31
4260
            match resolved {
32
4260
                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
454336
}
39

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

            
47
449721
fn dispatch_symbol(symbols: &mut SymbolTable, name: &str, args: &[Expr]) -> Result<Expr> {
48
449721
    let (func, kind, value) = {
49
449721
        let sym = symbols
50
449721
            .lookup(name)
51
449721
            .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
52
449721
        (sym.function().cloned(), sym.kind(), sym.value().cloned())
53
    };
54
449721
    if matches!(kind, SymbolKind::Native)
55
8166
        && 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
8166
        for arg in args {
62
4402
            eval_value(symbols, arg)?;
63
        }
64
8166
        return Ok(stand_in);
65
441555
    }
66
441555
    if kind == SymbolKind::Macro
67
710
        && let Some(Expr::Lambda(params, body)) = func
68
    {
69
710
        return expand_macro_then(symbols, &params, &body, args, |symbols, code| {
70
710
            eval_value(symbols, &code)
71
710
        });
72
440845
    }
73
22152
    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
22152
        if symbols.is_inlining(name)
81
4899
            && let Some(ret_ty) = recursive_runtime_call_type(symbols, &params, args)
82
        {
83
4331
            return Ok(Expr::WasmRuntime(ret_ty));
84
17821
        }
85
17821
        symbols.enter_inline(name)?;
86
17821
        let result = call_lambda(symbols, &params, &body, args);
87
17821
        symbols.exit_inline();
88
17821
        return result;
89
418693
    }
90
418693
    match kind {
91
        SymbolKind::Native | SymbolKind::Operator => {
92
319859
            crate::compiler::native::call(symbols, name, args)
93
        }
94
98479
        SymbolKind::SpecialForm => crate::compiler::special::call(symbols, name, args),
95
355
        _ => Err(Error::Compile(format!("symbol '{name}' is not callable"))),
96
    }
97
449721
}
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
4899
fn recursive_runtime_call_type(
108
4899
    symbols: &mut SymbolTable,
109
4899
    params: &LambdaParams,
110
4899
    args: &[Expr],
111
4899
) -> Option<crate::ast::WasmType> {
112
4899
    let resolved: Vec<Expr> = args
113
4899
        .iter()
114
4899
        .map(|arg| eval_value(&mut symbols.clone(), arg).unwrap_or(Expr::Nil))
115
4899
        .collect();
116
4899
    if !resolved.iter().any(Expr::is_wasm_runtime) {
117
568
        return None;
118
4331
    }
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
4331
    let first = params.required.first().and(resolved.first());
123
4331
    Some(
124
4331
        first
125
4331
            .and_then(super::stack::classify_stack_type)
126
4331
            .unwrap_or(crate::ast::WasmType::Ratio),
127
4331
    )
128
4899
}
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
16472
pub(crate) fn expand_macro_then<T>(
139
16472
    symbols: &mut SymbolTable,
140
16472
    params: &LambdaParams,
141
16472
    body: &Expr,
142
16472
    args: &[Expr],
143
16472
    process: impl FnOnce(&mut SymbolTable, Expr) -> Result<T>,
144
16472
) -> Result<T> {
145
16472
    symbols.enter_macro_expansion()?;
146
16401
    let result = expand_macro(symbols, params, body, args).and_then(|expansion| {
147
16259
        let code = match expansion {
148
11289
            Expr::Quote(inner) => *inner,
149
4970
            other => other,
150
        };
151
16259
        process(symbols, code)
152
16259
    });
153
16401
    symbols.exit_macro_expansion();
154
16401
    result
155
16472
}
156

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

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

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

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

            
192
26199
    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
26199
    if let Some(rest_param) = &params.rest {
206
5467
        let rest = Expr::Quote(Box::new(Expr::List(args[arg_idx..].to_vec())));
207
5467
        local_symbols.define(Symbol::new(rest_param, SymbolKind::Variable).with_value(rest));
208
20732
    }
209

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

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

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

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

            
237
26199
    eval_value(&mut local_symbols, body)
238
26270
}
239

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

            
254
22436
    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
22436
    }
261
22436
    if let Some(max) = max_args
262
21797
        && args.len() > max
263
    {
264
        return Err(Error::Arity {
265
            name: "lambda".to_string(),
266
            expected: max,
267
            actual: args.len(),
268
        });
269
22436
    }
270

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

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

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

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

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

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

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

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

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

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

            
353
1561187
pub(crate) fn resolve_arg(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
354
1561187
    match expr {
355
        Expr::Nil
356
        | Expr::Bool(_)
357
        | Expr::Number(_)
358
        | Expr::String(_)
359
        | Expr::Bytes(_)
360
        | Expr::Quote(_)
361
        | Expr::Keyword(_)
362
744175
        | Expr::RuntimeValue(_) => Ok(expr.clone()),
363
71
        Expr::Lambda(_, _) => Ok(expr.clone()),
364
5609
        Expr::Cons(_, _) | Expr::List(_) => Ok(expr.clone()),
365
5893
        Expr::Quasiquote(inner) => expand_quasiquote(symbols, inner),
366
        Expr::Unquote(_) | Expr::UnquoteSplicing(_) => {
367
            Err(Error::Compile("unquote outside of quasiquote".to_string()))
368
        }
369
500561
        Expr::Symbol(name) => {
370
496371
            let (value, kind, has_fn) = {
371
500561
                let sym = symbols
372
500561
                    .lookup(name)
373
500561
                    .ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
374
496371
                (sym.value().cloned(), sym.kind(), sym.function().is_some())
375
            };
376
496371
            if let Some(value) = value {
377
496087
                return resolve_arg(symbols, &value);
378
284
            }
379
            match kind {
380
142
                SymbolKind::Native | SymbolKind::Operator => Err(Error::Compile(format!(
381
142
                    "'{name}' is a function and cannot be used as a value"
382
142
                ))),
383
71
                SymbolKind::SpecialForm => Err(Error::Compile(format!(
384
71
                    "'{name}' is a special form and cannot be used as a value"
385
71
                ))),
386
71
                SymbolKind::Macro => Err(Error::Compile(format!(
387
71
                    "'{name}' is a macro and cannot be used as a value"
388
71
                ))),
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
304878
        Expr::WasmRuntime(_) | Expr::WasmLocal(_, _) => Ok(expr.clone()),
396
    }
397
1561187
}