1
use tracing::debug;
2

            
3
use crate::ast::{Expr, LambdaParams, Program, WasmType};
4
use crate::error::{Error, Result};
5
use crate::runtime::Value;
6
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
7
use scripting_format::ValueType;
8

            
9
use super::context::CompileContext;
10
use super::emit::FunctionEmitter;
11
use super::layout::GcLocals;
12

            
13
pub const LOCAL_GC_ARR: u32 = 0;
14
pub const LOCAL_IDX: u32 = 1;
15
pub const LOCAL_OUTPUT_BASE: u32 = 2;
16
pub const LOCAL_TEMP_RATIO: u32 = 3;
17
pub const LOCAL_TEMP_I32: u32 = 4;
18

            
19
20251
pub fn compile_program(
20
20251
    ctx: &mut CompileContext,
21
20251
    emit: &mut FunctionEmitter,
22
20251
    symbols: &mut SymbolTable,
23
20251
    program: &Program,
24
20251
) -> Result<()> {
25
20251
    ctx.serializer().begin_output(emit);
26
20251
    if program.exprs.is_empty() {
27
1
        compile_nil(ctx, emit);
28
1
        return Ok(());
29
20250
    }
30
30634
    let has_trigger = program.exprs.iter().any(|e| {
31
222
        matches!(
32
29484
            e,
33
29484
            Expr::List(elems)
34
29484
                if matches!(elems.first(), Some(Expr::Symbol(name)) if name == "DEFUN")
35
840
                && matches!(elems.get(1), Some(Expr::Symbol(name)) if name == "SHOULD-APPLY")
36
        )
37
30634
    });
38
20250
    if has_trigger {
39
1114
        for expr in &program.exprs {
40
1114
            compile_for_effect(ctx, emit, symbols, expr)?;
41
        }
42
222
        Ok(())
43
    } else {
44
20028
        for expr in &program.exprs[..program.exprs.len() - 1] {
45
10384
            compile_for_effect(ctx, emit, symbols, expr)?;
46
        }
47
20028
        compile_expr(ctx, emit, symbols, program.exprs.last().unwrap())
48
    }
49
20251
}
50

            
51
36971
pub(super) fn compile_expr(
52
36971
    ctx: &mut CompileContext,
53
36971
    emit: &mut FunctionEmitter,
54
36971
    symbols: &mut SymbolTable,
55
36971
    expr: &Expr,
56
36971
) -> Result<()> {
57
22618
    match expr {
58
        Expr::Nil => {
59
309
            debug!("compiling nil");
60
309
            compile_nil(ctx, emit);
61
309
            Ok(())
62
        }
63
178
        Expr::Bool(b) => {
64
178
            debug!(value = b, "compiling bool");
65
178
            compile_bool(ctx, emit, *b);
66
178
            Ok(())
67
        }
68
1365
        Expr::Number(n) => {
69
1365
            debug!(numer = *n.numer(), denom = *n.denom(), "compiling number");
70
1365
            compile_number(ctx, emit, *n.numer(), *n.denom());
71
1365
            Ok(())
72
        }
73
925
        Expr::String(s) => {
74
925
            debug!(len = s.len(), "compiling string");
75
925
            compile_string(ctx, emit, s);
76
925
            Ok(())
77
        }
78
1366
        Expr::Symbol(name) => {
79
1366
            debug!(symbol = %name, "resolving symbol");
80
1366
            let resolved = resolve_arg(symbols, expr)?;
81
1145
            compile_expr(ctx, emit, symbols, &resolved)
82
        }
83
        Expr::Keyword(name) => {
84
            debug!(keyword = %name, "compiling keyword");
85
            compile_symbol(ctx, emit, &format!(":{name}"));
86
            Ok(())
87
        }
88
9550
        Expr::Quote(inner) => {
89
9550
            debug!("compiling quote");
90
9550
            compile_quoted_expr(ctx, emit, inner)
91
        }
92
22618
        Expr::List(elems) if elems.is_empty() => {
93
44
            debug!("compiling empty list as nil");
94
44
            compile_nil(ctx, emit);
95
44
            Ok(())
96
        }
97
22574
        Expr::List(elems) => compile_call(ctx, emit, symbols, elems),
98
88
        Expr::Quasiquote(inner) => {
99
88
            let expanded = expand_quasiquote(symbols, inner)?;
100
88
            compile_expr(ctx, emit, symbols, &expanded)
101
        }
102
        Expr::Unquote(_) | Expr::UnquoteSplicing(_) => {
103
44
            Err(Error::Compile("unquote outside of quasiquote".to_string()))
104
        }
105
        Expr::Lambda(_, _) => {
106
            let s = format_expr(expr);
107
            compile_string(ctx, emit, &s);
108
            Ok(())
109
        }
110
        Expr::RuntimeValue(_) => {
111
            if let Expr::RuntimeValue(Value::Struct { name, fields }) = expr
112
                && name == "TAG"
113
            {
114
                let name_val = fields.first().cloned().unwrap_or(Value::Nil);
115
                let value_val = fields.get(1).cloned().unwrap_or(Value::Nil);
116
                let name_str = match name_val {
117
                    Value::String(s) => s,
118
                    Value::Symbol(s) => s,
119
                    Value::Nil => String::new(),
120
                    other => {
121
                        return Err(Error::Compile(format!(
122
                            "TAG name must be string/symbol, got {}",
123
                            other.type_name()
124
                        )));
125
                    }
126
                };
127
                let value_str = match value_val {
128
                    Value::String(s) => s,
129
                    Value::Symbol(s) => s,
130
                    Value::Nil => String::new(),
131
                    other => {
132
                        return Err(Error::Compile(format!(
133
                            "TAG value must be string/symbol, got {}",
134
                            other.type_name()
135
                        )));
136
                    }
137
                };
138
                if name_str.len() > u16::MAX as usize || value_str.len() > u16::MAX as usize {
139
                    return Err(Error::Compile(
140
                        "TAG name/value exceed u16 size limit".to_string(),
141
                    ));
142
                }
143
                let name_data_idx = ctx.add_data(name_str.as_bytes());
144
                let value_data_idx = ctx.add_data(value_str.as_bytes());
145
                let gc = GcLocals {
146
                    type_idx: ctx.type_idx("i8_array"),
147
                    arr: LOCAL_GC_ARR,
148
                    idx: LOCAL_IDX,
149
                };
150
                ctx.serializer().write_tag_gc(
151
                    emit,
152
                    name_data_idx,
153
                    name_str.len() as u32,
154
                    value_data_idx,
155
                    value_str.len() as u32,
156
                    &gc,
157
                );
158
                return Ok(());
159
            }
160
            if let Expr::RuntimeValue(val) = expr {
161
                compile_string(ctx, emit, &format_runtime_value(val));
162
                Ok(())
163
            } else {
164
                unreachable!()
165
            }
166
        }
167
        Expr::WasmRuntime(ty) => {
168
            serialize_stack_to_output(ctx, emit, *ty);
169
            Ok(())
170
        }
171
528
        Expr::WasmLocal(idx, ty) => {
172
528
            emit.local_get(*idx);
173
528
            serialize_stack_to_output(ctx, emit, *ty);
174
528
            Ok(())
175
        }
176
        _ => Err(Error::Compile(format!("unsupported expression: {expr:?}"))),
177
    }
178
36971
}
179

            
180
9638
pub(super) fn compile_quoted_expr(
181
9638
    ctx: &mut CompileContext,
182
9638
    emit: &mut FunctionEmitter,
183
9638
    inner: &Expr,
184
9638
) -> Result<()> {
185
9638
    match inner {
186
        Expr::Nil => {
187
            compile_nil(ctx, emit);
188
            Ok(())
189
        }
190
        Expr::Bool(b) => {
191
            compile_bool(ctx, emit, *b);
192
            Ok(())
193
        }
194
132
        Expr::Number(n) => {
195
132
            compile_number(ctx, emit, *n.numer(), *n.denom());
196
132
            Ok(())
197
        }
198
44
        Expr::String(s) => {
199
44
            compile_string(ctx, emit, s);
200
44
            Ok(())
201
        }
202
9066
        Expr::Symbol(s) => {
203
9066
            compile_symbol(ctx, emit, s);
204
9066
            Ok(())
205
        }
206
        Expr::Keyword(s) => {
207
            compile_symbol(ctx, emit, &format!(":{s}"));
208
            Ok(())
209
        }
210
396
        Expr::List(elems) => {
211
396
            let s = format!(
212
                "({})",
213
396
                elems.iter().map(format_expr).collect::<Vec<_>>().join(" ")
214
            );
215
396
            compile_string(ctx, emit, &s);
216
396
            Ok(())
217
        }
218
        Expr::Cons(car, cdr) => {
219
            compile_string(
220
                ctx,
221
                emit,
222
                &format!("({} . {})", format_expr(car), format_expr(cdr)),
223
            );
224
            Ok(())
225
        }
226
        Expr::Quasiquote(e) => {
227
            compile_string(ctx, emit, &format!("`{}", format_expr(e)));
228
            Ok(())
229
        }
230
        Expr::Unquote(e) => {
231
            compile_string(ctx, emit, &format!(",{}", format_expr(e)));
232
            Ok(())
233
        }
234
        Expr::UnquoteSplicing(e) => {
235
            compile_string(ctx, emit, &format!(",@{}", format_expr(e)));
236
            Ok(())
237
        }
238
        Expr::RuntimeValue(_) | Expr::WasmRuntime(_) | Expr::WasmLocal(_, _) => Err(
239
            Error::Compile("runtime values cannot be quoted in WASM".to_string()),
240
        ),
241
        _ => Err(Error::Compile(format!(
242
            "unsupported quoted expression: {inner:?}"
243
        ))),
244
    }
245
9638
}
246

            
247
1234
pub(super) fn compile_nil(ctx: &mut CompileContext, emit: &mut FunctionEmitter) {
248
1234
    ctx.serializer().write_debug_nil(emit);
249
1234
}
250

            
251
794
pub(super) fn compile_bool(ctx: &mut CompileContext, emit: &mut FunctionEmitter, value: bool) {
252
794
    ctx.serializer().write_debug_bool(emit, value);
253
794
}
254

            
255
3741
pub(super) fn compile_number(
256
3741
    ctx: &mut CompileContext,
257
3741
    emit: &mut FunctionEmitter,
258
3741
    numer: i64,
259
3741
    denom: i64,
260
3741
) {
261
3741
    ctx.serializer().write_debug_number(emit, numer, denom);
262
3741
}
263

            
264
10475
fn compile_text(
265
10475
    ctx: &mut CompileContext,
266
10475
    emit: &mut FunctionEmitter,
267
10475
    value_type: ValueType,
268
10475
    s: &str,
269
10475
) {
270
10475
    let data_idx = ctx.add_data(s.as_bytes());
271
10475
    let gc = GcLocals {
272
10475
        type_idx: ctx.type_idx("i8_array"),
273
10475
        arr: LOCAL_GC_ARR,
274
10475
        idx: LOCAL_IDX,
275
10475
    };
276
10475
    ctx.serializer()
277
10475
        .write_debug_string_gc(emit, value_type, data_idx, s.len() as u32, &gc);
278
10475
}
279

            
280
1409
pub(super) fn compile_string(ctx: &mut CompileContext, emit: &mut FunctionEmitter, s: &str) {
281
1409
    compile_text(ctx, emit, ValueType::String, s);
282
1409
}
283

            
284
9066
pub(super) fn compile_symbol(ctx: &mut CompileContext, emit: &mut FunctionEmitter, s: &str) {
285
9066
    compile_text(ctx, emit, ValueType::Symbol, s);
286
9066
}
287

            
288
99226
pub(crate) fn call(symbols: &mut SymbolTable, elems: &[Expr]) -> Result<Expr> {
289
99226
    let (head, args) = elems
290
99226
        .split_first()
291
99226
        .ok_or_else(|| Error::Compile("empty function call".to_string()))?;
292
99226
    match head {
293
97334
        Expr::Symbol(name) => dispatch_symbol(symbols, name, args),
294
1408
        Expr::Quote(inner) => match inner.as_ref() {
295
1408
            Expr::Symbol(name) => dispatch_symbol(symbols, name, args),
296
            _ => Err(Error::Compile(format!("not callable: {head:?}"))),
297
        },
298
176
        Expr::Lambda(params, body) => call_lambda(symbols, params, body, args),
299
308
        Expr::List(inner) => {
300
308
            let resolved = call(symbols, inner)?;
301
308
            match resolved {
302
308
                Expr::Lambda(params, body) => call_lambda(symbols, &params, &body, args),
303
                _ => Err(Error::Compile("not callable".to_string())),
304
            }
305
        }
306
        _ => Err(Error::Compile(format!("not callable: {head:?}"))),
307
    }
308
99226
}
309

            
310
265658
pub(crate) fn eval_value(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
311
97510
    match expr {
312
97510
        Expr::List(elems) if !elems.is_empty() => call(symbols, elems),
313
168148
        _ => resolve_arg(symbols, expr),
314
    }
315
265658
}
316

            
317
98742
fn dispatch_symbol(symbols: &mut SymbolTable, name: &str, args: &[Expr]) -> Result<Expr> {
318
98742
    let (func, kind) = {
319
98742
        let sym = symbols
320
98742
            .lookup(name)
321
98742
            .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
322
98742
        (sym.function().cloned(), sym.kind())
323
    };
324
98742
    if kind == SymbolKind::Macro
325
486
        && let Some(Expr::Lambda(params, body)) = func
326
    {
327
486
        let expansion = expand_macro(symbols, &params, &body, args)?;
328
486
        let code = match expansion {
329
442
            Expr::Quote(inner) => *inner,
330
44
            other => other,
331
        };
332
486
        return eval_value(symbols, &code);
333
98256
    }
334
5154
    if let Some(Expr::Lambda(params, body)) = func {
335
5154
        return call_lambda(symbols, &params, &body, args);
336
93102
    }
337
93102
    match kind {
338
67004
        SymbolKind::Native | SymbolKind::Operator => super::native::call(symbols, name, args),
339
26098
        SymbolKind::SpecialForm => super::special::call(symbols, name, args),
340
        _ => Err(Error::Compile(format!("symbol '{name}' is not callable"))),
341
    }
342
98742
}
343

            
344
2662
pub(crate) fn expand_macro(
345
2662
    symbols: &mut SymbolTable,
346
2662
    params: &LambdaParams,
347
2662
    body: &Expr,
348
2662
    args: &[Expr],
349
2662
) -> Result<Expr> {
350
2662
    if !params.aux.is_empty() {
351
        return Err(Error::Compile(
352
            "&aux not yet supported in macros".to_string(),
353
        ));
354
2662
    }
355

            
356
2662
    let min_args = params.required.len();
357
2662
    let max_args = if params.rest.is_some() || !params.key.is_empty() {
358
1782
        None
359
    } else {
360
880
        Some(min_args + params.optional.len())
361
    };
362
2662
    if args.len() < min_args || max_args.is_some_and(|max| args.len() > max) {
363
44
        return Err(Error::Arity {
364
44
            name: "macro".to_string(),
365
44
            expected: min_args,
366
44
            actual: args.len(),
367
44
        });
368
2618
    }
369

            
370
2618
    let mut local_symbols = symbols.clone();
371
2618
    let mut arg_idx = 0;
372

            
373
3454
    for param in &params.required {
374
3454
        local_symbols
375
3454
            .define(Symbol::new(param, SymbolKind::Variable).with_value(args[arg_idx].clone()));
376
3454
        arg_idx += 1;
377
3454
    }
378

            
379
2618
    for (param, default) in &params.optional {
380
        let value = if arg_idx < args.len() {
381
            let arg = args[arg_idx].clone();
382
            arg_idx += 1;
383
            arg
384
        } else if let Some(default_expr) = default {
385
            default_expr.clone()
386
        } else {
387
            Expr::Nil
388
        };
389
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
390
    }
391

            
392
2618
    if let Some(rest_param) = &params.rest {
393
1738
        let rest = Expr::Quote(Box::new(Expr::List(args[arg_idx..].to_vec())));
394
1738
        local_symbols.define(Symbol::new(rest_param, SymbolKind::Variable).with_value(rest));
395
1738
    }
396

            
397
    // Bind key parameters in macros
398
2618
    if !params.key.is_empty() {
399
44
        let remaining_args = &args[arg_idx..];
400
44
        for (param, default) in &params.key {
401
44
            let keyword = Expr::Keyword(param.to_uppercase());
402
44
            let mut found_value = None;
403

            
404
            // Look for keyword argument pairs
405
44
            for i in (0..remaining_args.len() - 1).step_by(2) {
406
44
                if remaining_args[i] == keyword {
407
44
                    found_value = Some(remaining_args[i + 1].clone());
408
44
                    break;
409
                }
410
            }
411

            
412
44
            let value = if let Some(val) = found_value {
413
44
                val
414
            } else if let Some(default_expr) = default {
415
                default_expr.clone()
416
            } else {
417
                Expr::Nil
418
            };
419

            
420
44
            local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
421
        }
422
2574
    }
423

            
424
2618
    eval_value(&mut local_symbols, body)
425
2662
}
426

            
427
5638
fn call_lambda(
428
5638
    symbols: &mut SymbolTable,
429
5638
    params: &LambdaParams,
430
5638
    body: &Expr,
431
5638
    args: &[Expr],
432
5638
) -> Result<Expr> {
433
5638
    let min_args = params.required.len();
434
    // If we have &key or &rest, we can have unlimited args
435
5638
    let max_args = if params.rest.is_some() || !params.key.is_empty() {
436
396
        None
437
    } else {
438
5242
        Some(min_args + params.optional.len())
439
    };
440

            
441
5638
    if args.len() < min_args {
442
        return Err(Error::Arity {
443
            name: "lambda".to_string(),
444
            expected: min_args,
445
            actual: args.len(),
446
        });
447
5638
    }
448
5638
    if let Some(max) = max_args
449
5242
        && args.len() > max
450
    {
451
        return Err(Error::Arity {
452
            name: "lambda".to_string(),
453
            expected: max,
454
            actual: args.len(),
455
        });
456
5638
    }
457

            
458
5638
    let mut local_symbols = symbols.clone();
459
5638
    let mut arg_idx = 0;
460

            
461
    // Bind required parameters
462
6728
    for param in &params.required {
463
6728
        let resolved = eval_value(symbols, &args[arg_idx])?;
464
6728
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(resolved));
465
6728
        arg_idx += 1;
466
    }
467

            
468
    // Bind optional parameters
469
5638
    for (param, default) in &params.optional {
470
132
        let value = if arg_idx < args.len() {
471
44
            eval_value(symbols, &args[arg_idx])?
472
88
        } else if let Some(default_expr) = default {
473
44
            eval_value(symbols, default_expr)?
474
        } else {
475
44
            Expr::Nil
476
        };
477
132
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
478
132
        if arg_idx < args.len() {
479
44
            arg_idx += 1;
480
88
        }
481
    }
482

            
483
    // Bind rest parameter
484
5638
    if let Some(rest_param) = &params.rest {
485
132
        let rest_args: Vec<Expr> = args[arg_idx..]
486
132
            .iter()
487
220
            .map(|arg| eval_value(symbols, arg))
488
132
            .collect::<Result<_>>()?;
489
132
        let rest_list = if rest_args.is_empty() {
490
44
            Expr::Nil
491
        } else {
492
88
            Expr::List(rest_args)
493
        };
494
132
        local_symbols.define(Symbol::new(rest_param, SymbolKind::Variable).with_value(rest_list));
495
5506
    }
496

            
497
    // Bind key parameters
498
5638
    if !params.key.is_empty() {
499
        // Find keyword arguments in the remaining args
500
264
        let remaining_args = &args[arg_idx..];
501
616
        for (param, default) in &params.key {
502
616
            let keyword = Expr::Keyword(param.to_uppercase());
503
616
            let mut found_value = None;
504

            
505
            // Look for keyword argument pairs
506
616
            if remaining_args.len() >= 2 {
507
1452
                for i in (0..remaining_args.len() - 1).step_by(2) {
508
1452
                    if remaining_args[i] == keyword {
509
484
                        found_value = Some(eval_value(symbols, &remaining_args[i + 1])?);
510
484
                        break;
511
968
                    }
512
                }
513
44
            }
514

            
515
616
            let value = if let Some(val) = found_value {
516
484
                val
517
132
            } else if let Some(default_expr) = default {
518
                eval_value(symbols, default_expr)?
519
            } else {
520
132
                Expr::Nil
521
            };
522

            
523
616
            local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
524
        }
525
5374
    }
526

            
527
    // Bind aux parameters (auxiliary variables)
528
5638
    for (param, init) in &params.aux {
529
88
        let value = if let Some(init_expr) = init {
530
88
            eval_value(&mut local_symbols, init_expr)?
531
        } else {
532
            Expr::Nil
533
        };
534
88
        local_symbols.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
535
    }
536

            
537
5638
    eval_value(&mut local_symbols, body)
538
5638
}
539

            
540
2420
pub(super) fn compile_body(
541
2420
    ctx: &mut CompileContext,
542
2420
    emit: &mut FunctionEmitter,
543
2420
    symbols: &mut SymbolTable,
544
2420
    body: &[Expr],
545
2420
) -> Result<()> {
546
2420
    for expr in &body[..body.len() - 1] {
547
352
        compile_for_effect(ctx, emit, symbols, expr)?;
548
    }
549
2420
    compile_expr(ctx, emit, symbols, body.last().unwrap())
550
2420
}
551

            
552
1072
pub(super) fn compile_body_for_stack(
553
1072
    ctx: &mut CompileContext,
554
1072
    emit: &mut FunctionEmitter,
555
1072
    symbols: &mut SymbolTable,
556
1072
    body: &[Expr],
557
1072
) -> Result<WasmType> {
558
1072
    for expr in &body[..body.len() - 1] {
559
268
        compile_for_effect(ctx, emit, symbols, expr)?;
560
    }
561
1072
    compile_for_stack(ctx, emit, symbols, body.last().unwrap())
562
1072
}
563

            
564
20244
pub(super) fn compile_for_effect(
565
20244
    ctx: &mut CompileContext,
566
20244
    emit: &mut FunctionEmitter,
567
20244
    symbols: &mut SymbolTable,
568
20244
    expr: &Expr,
569
20244
) -> Result<()> {
570
20244
    if let Expr::List(elems) = expr
571
18860
        && let Some(Expr::Symbol(name)) = elems.first()
572
    {
573
18860
        let args = &elems[1..];
574
        // Expand macros (e.g. WHEN → IF) and recurse
575
18860
        if let Some(sym) = symbols.lookup(name)
576
18860
            && sym.kind() == SymbolKind::Macro
577
1340
            && let Some(Expr::Lambda(params, body)) = sym.function().cloned()
578
        {
579
1340
            let expansion = expand_macro(symbols, &params, &body, args)?;
580
1340
            let code = match expansion {
581
1340
                Expr::Quote(inner) => *inner,
582
                other => other,
583
            };
584
1340
            return compile_for_effect(ctx, emit, symbols, &code);
585
17520
        }
586
17520
        match name.as_str() {
587
17520
            "DEBUG" => {
588
                return super::native::compile_debug_effect(ctx, emit, symbols, args);
589
            }
590
17520
            "SETF" => {
591
1336
                return compile_setf_for_effect(ctx, emit, symbols, args);
592
            }
593
16184
            "DOLIST" | "DO" | "DO*" => {
594
758
                return super::special::compile_for_effect(ctx, emit, symbols, name, args);
595
            }
596
15426
            "CREATE-TAG" | "DELETE-ENTITY" => {
597
624
                return super::native::compile(ctx, emit, symbols, name, args);
598
            }
599
14802
            "IF" => {
600
1340
                return compile_if_for_effect(ctx, emit, symbols, args);
601
            }
602
13462
            "BEGIN" => {
603
1340
                for arg in args {
604
1340
                    compile_for_effect(ctx, emit, symbols, arg)?;
605
                }
606
1340
                return Ok(());
607
            }
608
12122
            "LET" | "LET*" | "COND" | "AND" | "OR" => {
609
402
                let val = eval_value(symbols, expr)?;
610
402
                if val.is_wasm_runtime() {
611
402
                    return super::special::compile_for_effect(ctx, emit, symbols, name, args);
612
                }
613
                return Ok(());
614
            }
615
            _ => {
616
11720
                if let Some(sym) = symbols.lookup(name)
617
11720
                    && let Some(Expr::Lambda(params, body)) = sym.function().cloned()
618
                {
619
268
                    let val = eval_value(symbols, expr)?;
620
268
                    if val.is_wasm_runtime() {
621
268
                        let mut local = bind_lambda_params(symbols, &params, args)?;
622
268
                        compile_for_effect(ctx, emit, &mut local, &body)?;
623
268
                        return Ok(());
624
                    }
625
11452
                }
626
            }
627
        }
628
1384
    }
629
12836
    eval_value(symbols, expr)?;
630
12836
    Ok(())
631
20244
}
632

            
633
1340
fn compile_if_for_effect(
634
1340
    ctx: &mut CompileContext,
635
1340
    emit: &mut FunctionEmitter,
636
1340
    symbols: &mut SymbolTable,
637
1340
    args: &[Expr],
638
1340
) -> Result<()> {
639
1340
    if args.len() < 2 || args.len() > 3 {
640
        return Err(Error::Compile(
641
            "IF requires a test, a then-form, and an optional else-form".to_string(),
642
        ));
643
1340
    }
644
1340
    let test = eval_value(symbols, &args[0])?;
645
1340
    if test.is_wasm_runtime() {
646
1340
        compile_for_stack(ctx, emit, symbols, &args[0])?;
647
1340
        emit.if_block(wasm_encoder::BlockType::Empty);
648
1340
        compile_for_effect(ctx, emit, symbols, &args[1])?;
649
1340
        if args.len() == 3 {
650
1340
            emit.else_block();
651
1340
            compile_for_effect(ctx, emit, symbols, &args[2])?;
652
        }
653
1340
        emit.block_end();
654
1340
        return Ok(());
655
    }
656
    if super::special::is_truthy(&test) {
657
        compile_for_effect(ctx, emit, symbols, &args[1])
658
    } else if args.len() == 3 {
659
        compile_for_effect(ctx, emit, symbols, &args[2])
660
    } else {
661
        Ok(())
662
    }
663
1340
}
664

            
665
1336
fn compile_setf_for_effect(
666
1336
    ctx: &mut CompileContext,
667
1336
    emit: &mut FunctionEmitter,
668
1336
    symbols: &mut SymbolTable,
669
1336
    args: &[Expr],
670
1336
) -> Result<()> {
671
1336
    if !args.len().is_multiple_of(2) {
672
        return Err(Error::Compile(
673
            "SETF requires an even number of arguments".to_string(),
674
        ));
675
1336
    }
676
1336
    for pair in args.chunks(2) {
677
1336
        let place = &pair[0];
678
1336
        let value_expr = &pair[1];
679
1336
        if let Expr::Symbol(name) = place {
680
1336
            let wasm_local = symbols
681
1336
                .lookup(name)
682
1336
                .and_then(|s| s.value())
683
1336
                .and_then(|v| match v {
684
1336
                    Expr::WasmLocal(idx, ty) => Some((*idx, *ty)),
685
                    _ => None,
686
1336
                });
687
1336
            if let Some((idx, _ty)) = wasm_local {
688
1336
                compile_for_stack(ctx, emit, symbols, value_expr)?;
689
1336
                emit.local_set(idx);
690
1336
                continue;
691
            }
692
        }
693
        let value = eval_value(symbols, value_expr)?;
694
        super::special::setf_set_place(symbols, place, value)?;
695
    }
696
1336
    Ok(())
697
1336
}
698

            
699
56418
pub(super) fn compile_for_stack(
700
56418
    ctx: &mut CompileContext,
701
56418
    emit: &mut FunctionEmitter,
702
56418
    symbols: &mut SymbolTable,
703
56418
    expr: &Expr,
704
56418
) -> Result<WasmType> {
705
20212
    match expr {
706
5750
        Expr::Number(n) => {
707
5750
            push_ratio(ctx, emit, *n.numer(), *n.denom());
708
5750
            Ok(WasmType::Ratio)
709
        }
710
        Expr::Bool(b) => {
711
            emit.i32_const(i32::from(*b));
712
            Ok(WasmType::I32)
713
        }
714
        Expr::Nil => {
715
            emit.i32_const(0);
716
            Ok(WasmType::I32)
717
        }
718
1628
        Expr::WasmRuntime(ty) => Ok(*ty),
719
13534
        Expr::WasmLocal(idx, ty) => {
720
13534
            emit.local_get(*idx);
721
13534
            Ok(*ty)
722
        }
723
        Expr::String(s) => {
724
            let data_idx = ctx.add_data(s.as_bytes());
725
            emit.i32_const(0);
726
            emit.i32_const(s.len() as i32);
727
            emit.array_new_data(ctx.type_idx("i8_array"), data_idx);
728
            Ok(WasmType::StringRef)
729
        }
730
        Expr::Symbol(_) => {
731
15294
            let resolved = resolve_arg(symbols, expr)?;
732
15250
            compile_for_stack(ctx, emit, symbols, &resolved)
733
        }
734
20212
        Expr::List(elems) if elems.is_empty() => {
735
            emit.i32_const(0);
736
            Ok(WasmType::I32)
737
        }
738
20212
        Expr::List(elems) => compile_call_for_stack(ctx, emit, symbols, elems),
739
        Expr::Quasiquote(inner) => {
740
            let expanded = expand_quasiquote(symbols, inner)?;
741
            compile_for_stack(ctx, emit, symbols, &expanded)
742
        }
743
        _ => Err(Error::Compile(format!(
744
            "cannot compile to WASM stack value: {}",
745
            format_expr(expr)
746
        ))),
747
    }
748
56418
}
749

            
750
6188
pub(super) fn compile_for_stack_ratio(
751
6188
    ctx: &mut CompileContext,
752
6188
    emit: &mut FunctionEmitter,
753
6188
    symbols: &mut SymbolTable,
754
6188
    expr: &Expr,
755
6188
) -> Result<()> {
756
6188
    let ty = compile_for_stack(ctx, emit, symbols, expr)?;
757
6144
    match ty {
758
6100
        WasmType::Ratio => Ok(()),
759
44
        WasmType::I32 | WasmType::ConsRef | WasmType::StringRef => Err(Error::Compile(
760
44
            "arithmetic requires ratio values, not indices".to_string(),
761
44
        )),
762
    }
763
6188
}
764

            
765
5838
pub(super) fn push_ratio(ctx: &CompileContext, emit: &mut FunctionEmitter, numer: i64, denom: i64) {
766
5838
    emit.i64_const(numer);
767
5838
    emit.i64_const(denom);
768
5838
    emit.call(ctx.func("ratio_new"));
769
5838
}
770

            
771
20212
fn compile_call_for_stack(
772
20212
    ctx: &mut CompileContext,
773
20212
    emit: &mut FunctionEmitter,
774
20212
    symbols: &mut SymbolTable,
775
20212
    elems: &[Expr],
776
20212
) -> Result<WasmType> {
777
20212
    let (head, args) = elems
778
20212
        .split_first()
779
20212
        .ok_or_else(|| Error::Compile("empty function call".to_string()))?;
780
20212
    match head {
781
20212
        Expr::Symbol(name) => compile_symbol_call_for_stack(ctx, emit, symbols, name, args),
782
        Expr::Lambda(params, body) => {
783
            compile_lambda_call_for_stack(ctx, emit, symbols, params, body, args)
784
        }
785
        Expr::List(inner) => {
786
            let resolved = call(symbols, inner)?;
787
            match resolved {
788
                Expr::Lambda(params, body) => {
789
                    compile_lambda_call_for_stack(ctx, emit, symbols, &params, &body, args)
790
                }
791
                _ => Err(Error::Compile("not callable".to_string())),
792
            }
793
        }
794
        _ => {
795
            let result = call(symbols, elems)?;
796
            compile_for_stack(ctx, emit, symbols, &result)
797
        }
798
    }
799
20212
}
800

            
801
20212
fn compile_symbol_call_for_stack(
802
20212
    ctx: &mut CompileContext,
803
20212
    emit: &mut FunctionEmitter,
804
20212
    symbols: &mut SymbolTable,
805
20212
    name: &str,
806
20212
    args: &[Expr],
807
20212
) -> Result<WasmType> {
808
20212
    let (func, kind) = {
809
20212
        let sym = symbols
810
20212
            .lookup(name)
811
20212
            .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
812
20212
        (sym.function().cloned(), sym.kind())
813
    };
814
20212
    if kind == SymbolKind::Macro
815
        && let Some(Expr::Lambda(params, body)) = func
816
    {
817
        let expansion = expand_macro(symbols, &params, &body, args)?;
818
        let code = match expansion {
819
            Expr::Quote(inner) => *inner,
820
            other => other,
821
        };
822
        return compile_for_stack(ctx, emit, symbols, &code);
823
20212
    }
824
1474
    if let Some(Expr::Lambda(params, body)) = func {
825
1474
        return compile_lambda_call_for_stack(ctx, emit, symbols, &params, &body, args);
826
18738
    }
827
18738
    match kind {
828
        SymbolKind::Native | SymbolKind::Operator => {
829
15524
            super::native::compile_for_stack(ctx, emit, symbols, name, args)
830
        }
831
        SymbolKind::SpecialForm => {
832
3214
            super::special::compile_for_stack(ctx, emit, symbols, name, args)
833
        }
834
        _ => Err(Error::Compile(format!(
835
            "symbol '{name}' is not callable for stack value"
836
        ))),
837
    }
838
20212
}
839

            
840
2464
pub(super) fn serialize_stack_to_output(
841
2464
    ctx: &mut CompileContext,
842
2464
    emit: &mut FunctionEmitter,
843
2464
    ty: WasmType,
844
2464
) {
845
2464
    let ratio_idx = ctx.type_idx("ratio");
846
2464
    match ty {
847
1276
        WasmType::Ratio => {
848
1276
            ctx.serializer()
849
1276
                .write_debug_number_from_stack(emit, ratio_idx, LOCAL_TEMP_RATIO);
850
1276
        }
851
1188
        WasmType::I32 => {
852
1188
            ctx.serializer()
853
1188
                .write_debug_i32_from_stack(emit, LOCAL_TEMP_I32);
854
1188
        }
855
        WasmType::ConsRef => {
856
            ctx.serializer()
857
                .write_debug_bool_from_stack(emit, LOCAL_TEMP_I32);
858
        }
859
        WasmType::StringRef => {
860
            let gc = GcLocals {
861
                type_idx: ctx.type_idx("i8_array"),
862
                arr: LOCAL_GC_ARR,
863
                idx: LOCAL_IDX,
864
            };
865
            ctx.serializer().write_debug_string_from_stack(emit, &gc);
866
        }
867
    }
868
2464
}
869

            
870
22882
pub(super) fn compile_call(
871
22882
    ctx: &mut CompileContext,
872
22882
    emit: &mut FunctionEmitter,
873
22882
    symbols: &mut SymbolTable,
874
22882
    elems: &[Expr],
875
22882
) -> Result<()> {
876
22882
    let (head, args) = elems
877
22882
        .split_first()
878
22882
        .ok_or_else(|| Error::Compile("empty function call".to_string()))?;
879
22882
    match head {
880
22442
        Expr::Symbol(name) => compile_symbol_call(ctx, emit, symbols, name, args),
881
352
        Expr::Quote(inner) => match inner.as_ref() {
882
352
            Expr::Symbol(name) => compile_symbol_call(ctx, emit, symbols, name, args),
883
            _ => Err(Error::Compile(format!("not callable: {head:?}"))),
884
        },
885
        Expr::Lambda(params, body) => compile_lambda_call(ctx, emit, symbols, params, body, args),
886
88
        Expr::List(inner) => {
887
88
            let resolved = call(symbols, inner)?;
888
88
            match resolved {
889
88
                Expr::Lambda(params, body) => {
890
88
                    compile_lambda_call(ctx, emit, symbols, &params, &body, args)
891
                }
892
                _ => Err(Error::Compile("not callable".to_string())),
893
            }
894
        }
895
        _ => Err(Error::Compile(format!("not callable: {head:?}"))),
896
    }
897
22882
}
898

            
899
22794
fn compile_symbol_call(
900
22794
    ctx: &mut CompileContext,
901
22794
    emit: &mut FunctionEmitter,
902
22794
    symbols: &mut SymbolTable,
903
22794
    name: &str,
904
22794
    args: &[Expr],
905
22794
) -> Result<()> {
906
22750
    let (func, kind) = {
907
22794
        let sym = symbols
908
22794
            .lookup(name)
909
22794
            .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
910
22750
        (sym.function().cloned(), sym.kind())
911
    };
912
22750
    if kind == SymbolKind::Macro
913
660
        && let Some(Expr::Lambda(params, body)) = func
914
    {
915
660
        let expansion = expand_macro(symbols, &params, &body, args)?;
916
616
        let code = match expansion {
917
484
            Expr::Quote(inner) => *inner,
918
132
            other => other,
919
        };
920
616
        return compile_expr(ctx, emit, symbols, &code);
921
22090
    }
922
1012
    if let Some(Expr::Lambda(params, body)) = func {
923
1012
        return compile_lambda_call(ctx, emit, symbols, &params, &body, args);
924
21078
    }
925
21078
    match kind {
926
        SymbolKind::Native | SymbolKind::Operator => {
927
6556
            super::native::compile(ctx, emit, symbols, name, args)
928
        }
929
14522
        SymbolKind::SpecialForm => super::special::compile(ctx, emit, symbols, name, args),
930
        _ => Err(Error::Compile(format!("symbol '{name}' is not callable"))),
931
    }
932
22794
}
933

            
934
2842
fn bind_lambda_params(
935
2842
    symbols: &mut SymbolTable,
936
2842
    params: &LambdaParams,
937
2842
    args: &[Expr],
938
2842
) -> Result<SymbolTable> {
939
2842
    let min_args = params.required.len();
940
2842
    let max_args = if params.rest.is_some() || !params.key.is_empty() {
941
        None
942
    } else {
943
2842
        Some(min_args + params.optional.len())
944
    };
945

            
946
2842
    if args.len() < min_args {
947
44
        return Err(Error::Arity {
948
44
            name: "lambda".to_string(),
949
44
            expected: min_args,
950
44
            actual: args.len(),
951
44
        });
952
2798
    }
953
2798
    if let Some(max) = max_args
954
2798
        && args.len() > max
955
    {
956
        return Err(Error::Arity {
957
            name: "lambda".to_string(),
958
            expected: max,
959
            actual: args.len(),
960
        });
961
2798
    }
962

            
963
2798
    let mut local = symbols.clone();
964
2798
    let mut arg_idx = 0;
965

            
966
4530
    for param in &params.required {
967
4530
        let resolved = eval_value(symbols, &args[arg_idx])?;
968
4530
        local.define(Symbol::new(param, SymbolKind::Variable).with_value(resolved));
969
4530
        arg_idx += 1;
970
    }
971

            
972
2798
    for (param, default) in &params.optional {
973
        let value = if arg_idx < args.len() {
974
            eval_value(symbols, &args[arg_idx])?
975
        } else if let Some(default_expr) = default {
976
            eval_value(symbols, default_expr)?
977
        } else {
978
            Expr::Nil
979
        };
980
        local.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
981
        if arg_idx < args.len() {
982
            arg_idx += 1;
983
        }
984
    }
985

            
986
2798
    if let Some(rest_param) = &params.rest {
987
        let rest_args: Vec<Expr> = args[arg_idx..]
988
            .iter()
989
            .map(|arg| eval_value(symbols, arg))
990
            .collect::<Result<_>>()?;
991
        let rest_list = if rest_args.is_empty() {
992
            Expr::Nil
993
        } else {
994
            Expr::List(rest_args)
995
        };
996
        local.define(Symbol::new(rest_param, SymbolKind::Variable).with_value(rest_list));
997
2798
    }
998

            
999
2798
    if !params.key.is_empty() {
        let remaining_args = &args[arg_idx..];
        for (param, default) in &params.key {
            let keyword = Expr::Keyword(param.to_uppercase());
            let mut found_value = None;
            if remaining_args.len() >= 2 {
                for i in (0..remaining_args.len() - 1).step_by(2) {
                    if remaining_args[i] == keyword {
                        found_value = Some(eval_value(symbols, &remaining_args[i + 1])?);
                        break;
                    }
                }
            }
            let value = if let Some(val) = found_value {
                val
            } else if let Some(default_expr) = default {
                eval_value(symbols, default_expr)?
            } else {
                Expr::Nil
            };
            local.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
        }
2798
    }
2798
    for (param, init) in &params.aux {
        let value = if let Some(init_expr) = init {
            eval_value(&mut local, init_expr)?
        } else {
            Expr::Nil
        };
        local.define(Symbol::new(param, SymbolKind::Variable).with_value(value));
    }
2798
    Ok(local)
2842
}
1100
fn compile_lambda_call(
1100
    ctx: &mut CompileContext,
1100
    emit: &mut FunctionEmitter,
1100
    symbols: &mut SymbolTable,
1100
    params: &LambdaParams,
1100
    body: &Expr,
1100
    args: &[Expr],
1100
) -> Result<()> {
1100
    let mut local = bind_lambda_params(symbols, params, args)?;
1056
    compile_expr(ctx, emit, &mut local, body)
1100
}
1474
fn compile_lambda_call_for_stack(
1474
    ctx: &mut CompileContext,
1474
    emit: &mut FunctionEmitter,
1474
    symbols: &mut SymbolTable,
1474
    params: &LambdaParams,
1474
    body: &Expr,
1474
    args: &[Expr],
1474
) -> Result<WasmType> {
1474
    let mut local = bind_lambda_params(symbols, params, args)?;
1474
    compile_for_stack(ctx, emit, &mut local, body)
1474
}
1694
fn expand_quasiquote(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
1518
    fn splice_elements(resolved: Expr) -> Result<Vec<Expr>> {
1518
        match resolved {
            Expr::Nil => Ok(Vec::new()),
            Expr::List(elems) => Ok(elems),
1518
            Expr::Quote(inner) => match *inner {
                Expr::Nil => Ok(Vec::new()),
1518
                Expr::List(elems) => Ok(elems),
                other => Err(Error::Compile(format!(
                    "unquote-splicing requires a list, got {}",
                    format_expr(&other)
                ))),
            },
            other => Err(Error::Compile(format!(
                "unquote-splicing requires a list, got {}",
                format_expr(&other)
            ))),
        }
1518
    }
9416
    fn quasiquote_data(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
9416
        match expr {
1650
            Expr::Unquote(inner) => resolve_arg(symbols, inner),
            Expr::UnquoteSplicing(_) => Err(Error::Compile(
                "unquote-splicing outside of list in quasiquote".to_string(),
            )),
3080
            Expr::List(elems) => {
3080
                let mut resolved: Vec<Expr> = Vec::new();
9240
                for elem in elems {
9240
                    match elem {
1518
                        Expr::UnquoteSplicing(inner) => {
1518
                            let spliced = resolve_arg(symbols, inner)?;
1518
                            resolved.extend(splice_elements(spliced)?);
                        }
7722
                        _ => resolved.push(quasiquote_data(symbols, elem)?),
                    }
                }
3080
                Ok(Expr::List(resolved))
            }
            Expr::Cons(car, cdr) => {
                let car = quasiquote_data(symbols, car)?;
                let cdr = quasiquote_data(symbols, cdr)?;
                Ok(Expr::cons(car, cdr))
            }
4686
            _ => Ok(expr.clone()),
        }
9416
    }
1694
    Ok(Expr::Quote(Box::new(quasiquote_data(symbols, expr)?)))
1694
}
304901
pub(crate) fn resolve_arg(symbols: &mut SymbolTable, expr: &Expr) -> Result<Expr> {
304901
    match expr {
        Expr::Nil
        | Expr::Bool(_)
        | Expr::Number(_)
        | Expr::String(_)
        | Expr::Quote(_)
        | Expr::Keyword(_)
138625
        | Expr::RuntimeValue(_) => Ok(expr.clone()),
        Expr::Lambda(_, _) => Ok(expr.clone()),
1826
        Expr::Cons(_, _) | Expr::List(_) => Ok(expr.clone()),
1606
        Expr::Quasiquote(inner) => expand_quasiquote(symbols, inner),
        Expr::Unquote(_) | Expr::UnquoteSplicing(_) => {
            Err(Error::Compile("unquote outside of quasiquote".to_string()))
        }
116882
        Expr::Symbol(name) => {
116705
            let (value, kind, has_fn) = {
116882
                let sym = symbols
116882
                    .lookup(name)
116882
                    .ok_or_else(|| Error::UndefinedSymbol(name.clone()))?;
116705
                (sym.value().cloned(), sym.kind(), sym.function().is_some())
            };
116705
            if let Some(value) = value {
116529
                return resolve_arg(symbols, &value);
176
            }
            match kind {
88
                SymbolKind::Native | SymbolKind::Operator => Err(Error::Compile(format!(
88
                    "'{name}' is a function and cannot be used as a value"
88
                ))),
44
                SymbolKind::SpecialForm => Err(Error::Compile(format!(
44
                    "'{name}' is a special form and cannot be used as a value"
44
                ))),
44
                SymbolKind::Macro => Err(Error::Compile(format!(
44
                    "'{name}' is a macro and cannot be used as a value"
44
                ))),
                _ if has_fn => Err(Error::Compile(format!(
                    "'{name}' is a function; use #' or FUNCTION to access"
                ))),
                _ => Err(Error::Compile(format!("symbol '{name}' has no value"))),
            }
        }
45962
        Expr::WasmRuntime(_) | Expr::WasmLocal(_, _) => Ok(expr.clone()),
    }
304901
}
4180
pub(crate) fn format_expr(expr: &Expr) -> String {
2376
    match expr {
        Expr::Nil => "NIL".to_string(),
132
        Expr::Bool(true) => "#T".to_string(),
        Expr::Bool(false) => "NIL".to_string(),
2376
        Expr::Number(n) if *n.denom() == 1 => n.numer().to_string(),
        Expr::Number(n) => format!("{}/{}", n.numer(), n.denom()),
352
        Expr::String(s) => s.clone(),
660
        Expr::Symbol(s) => s.clone(),
        Expr::Keyword(s) => format!(":{s}"),
        Expr::Cons(car, cdr) => format!("({} . {})", format_expr(car), format_expr(cdr)),
264
        Expr::List(elems) => {
264
            let inner: Vec<_> = elems.iter().map(format_expr).collect();
264
            format!("({})", inner.join(" "))
        }
264
        Expr::Quote(e) => format!("'{}", format_expr(e)),
        Expr::Quasiquote(e) => format!("`{}", format_expr(e)),
        Expr::Unquote(e) => format!(",{}", format_expr(e)),
        Expr::UnquoteSplicing(e) => format!(",@{}", format_expr(e)),
132
        Expr::Lambda(params, body) => {
132
            format!(
                "(LAMBDA ({}) {})",
132
                format_lambda_params(params),
132
                format_expr(body)
            )
        }
        Expr::RuntimeValue(val) => format_runtime_value(val),
        Expr::WasmRuntime(ty) => format!("#<wasm:{ty}>"),
        Expr::WasmLocal(idx, ty) => format!("#<local:{idx}:{ty}>"),
    }
4180
}
fn format_runtime_value(val: &Value) -> String {
    match val {
        Value::Nil => "NIL".to_string(),
        Value::Bool(true) => "#T".to_string(),
        Value::Bool(false) => "NIL".to_string(),
        Value::Number(n) if *n.denom() == 1 => n.numer().to_string(),
        Value::Number(n) => format!("{}/{}", n.numer(), n.denom()),
        Value::String(s) => format!("\"{s}\""),
        Value::Symbol(s) => s.clone(),
        Value::Pair(_) | Value::Vector(_) | Value::Closure(_) => {
            format!("#<{}>", val.type_name())
        }
        Value::Struct { name, fields } => {
            let field_strs: Vec<_> = fields.iter().map(format_runtime_value).collect();
            format!("#S({name} {})", field_strs.join(" "))
        }
    }
}
132
fn format_lambda_params(params: &LambdaParams) -> String {
132
    let mut parts = Vec::new();
    // Required parameters
132
    parts.extend(params.required.iter().cloned());
    // Optional parameters
132
    if !params.optional.is_empty() {
        parts.push("&optional".to_string());
        for (name, default) in &params.optional {
            if let Some(default_expr) = default {
                parts.push(format!("({} {})", name, format_expr(default_expr)));
            } else {
                parts.push(name.clone());
            }
        }
132
    }
    // Rest parameter
132
    if let Some(rest) = &params.rest {
        parts.push("&rest".to_string());
        parts.push(rest.clone());
132
    }
    // Key parameters
132
    if !params.key.is_empty() {
        parts.push("&key".to_string());
        for (name, default) in &params.key {
            if let Some(default_expr) = default {
                parts.push(format!("({} {})", name, format_expr(default_expr)));
            } else {
                parts.push(name.clone());
            }
        }
132
    }
    // Aux parameters
132
    if !params.aux.is_empty() {
        parts.push("&aux".to_string());
        for (name, init) in &params.aux {
            if let Some(init_expr) = init {
                parts.push(format!("({} {})", name, format_expr(init_expr)));
            } else {
                parts.push(name.clone());
            }
        }
132
    }
132
    parts.join(" ")
132
}