1
use crate::ast::{Expr, WasmType};
2
use crate::error::{Error, Result};
3
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
4

            
5
use super::super::context::CompileContext;
6
use super::super::emit::FunctionEmitter;
7
use super::super::expr::{
8
    compile_body, compile_expr, compile_for_effect, compile_for_stack, compile_nil, eval_value,
9
};
10
use super::binding::eval_body;
11
use super::control::is_truthy;
12

            
13
704
pub(super) fn compile_do(
14
704
    ctx: &mut CompileContext,
15
704
    emit: &mut FunctionEmitter,
16
704
    symbols: &mut SymbolTable,
17
704
    args: &[Expr],
18
704
) -> Result<()> {
19
704
    if args.len() < 2 {
20
88
        return Err(Error::Compile(
21
88
            "DO requires a variable list and an end clause".to_string(),
22
88
        ));
23
616
    }
24
616
    let vars = parse_do_vars("DO", &args[0])?;
25
616
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
26
616
    let end_test = end_test.clone();
27
616
    let result_forms: Vec<Expr> = result_forms.to_vec();
28
616
    let body = &args[2..];
29

            
30
616
    let init_results: Vec<Expr> = vars
31
616
        .iter()
32
880
        .map(|v| match &v.init {
33
836
            Some(expr) => eval_value(symbols, expr),
34
44
            None => Ok(Expr::Nil),
35
880
        })
36
616
        .collect::<Result<_>>()?;
37

            
38
616
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
39

            
40
616
    if !needs_runtime {
41
572
        let mut trial = symbols.clone();
42
792
        for (v, val) in vars.iter().zip(&init_results) {
43
792
            trial.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
44
792
        }
45
572
        let test_result = eval_value(&mut trial, &end_test)?;
46
572
        needs_runtime = test_result.is_wasm_runtime();
47
44
    }
48

            
49
616
    if needs_runtime {
50
308
        let dl = DoLoop {
51
308
            vars: &vars,
52
308
            end_test: &end_test,
53
308
            result_forms: &result_forms,
54
308
            body,
55
308
            sequential: false,
56
308
        };
57
308
        return compile_do_runtime(ctx, emit, symbols, &dl);
58
308
    }
59

            
60
308
    let mut local = symbols.clone();
61
308
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
62
484
    for (v, val) in vars.iter().zip(&init_results) {
63
484
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
64
484
        stepped.push((v.name.clone(), v.step.clone()));
65
484
    }
66

            
67
308
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
68
88
        let dl = DoLoop {
69
88
            vars: &vars,
70
88
            end_test: &end_test,
71
88
            result_forms: &result_forms,
72
88
            body,
73
88
            sequential: false,
74
88
        };
75
88
        return compile_do_runtime(ctx, emit, symbols, &dl);
76
220
    }
77

            
78
    loop {
79
924
        let test = eval_value(&mut local, &end_test)?;
80
924
        if is_truthy(&test) {
81
220
            return if result_forms.is_empty() {
82
44
                compile_nil(ctx, emit);
83
44
                Ok(())
84
            } else {
85
176
                compile_body(ctx, emit, &mut local, &result_forms)
86
            };
87
704
        }
88
704
        for expr in body {
89
            compile_for_effect(ctx, emit, &mut local, expr)?;
90
        }
91
704
        let new_values: Vec<(&str, Expr)> = stepped
92
704
            .iter()
93
1056
            .filter_map(|(name, step)| {
94
1056
                step.as_ref()
95
1056
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
96
1056
            })
97
704
            .collect::<Result<_>>()?;
98
924
        for (name, val) in new_values {
99
924
            local
100
924
                .lookup_mut(name)
101
924
                .expect("DO variable must exist")
102
924
                .set_value(val);
103
924
        }
104
    }
105
704
}
106

            
107
848
pub(super) fn compile_do_for_stack(
108
848
    ctx: &mut CompileContext,
109
848
    emit: &mut FunctionEmitter,
110
848
    symbols: &mut SymbolTable,
111
848
    args: &[Expr],
112
848
) -> Result<WasmType> {
113
848
    if args.len() < 2 {
114
        return Err(Error::Compile(
115
            "DO requires a variable list and an end clause".to_string(),
116
        ));
117
848
    }
118
848
    let vars = parse_do_vars("DO", &args[0])?;
119
848
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
120
848
    let end_test = end_test.clone();
121
848
    let result_forms: Vec<Expr> = result_forms.to_vec();
122
848
    let body = &args[2..];
123
848
    let dl = DoLoop {
124
848
        vars: &vars,
125
848
        end_test: &end_test,
126
848
        result_forms: &result_forms,
127
848
        body,
128
848
        sequential: false,
129
848
    };
130
848
    compile_do_runtime_for_stack(ctx, emit, symbols, &dl)
131
848
}
132

            
133
pub(super) fn compile_do_star_for_stack(
134
    ctx: &mut CompileContext,
135
    emit: &mut FunctionEmitter,
136
    symbols: &mut SymbolTable,
137
    args: &[Expr],
138
) -> Result<WasmType> {
139
    if args.len() < 2 {
140
        return Err(Error::Compile(
141
            "DO* requires a variable list and an end clause".to_string(),
142
        ));
143
    }
144
    let vars = parse_do_vars("DO*", &args[0])?;
145
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
146
    let end_test = end_test.clone();
147
    let result_forms: Vec<Expr> = result_forms.to_vec();
148
    let body = &args[2..];
149
    let dl = DoLoop {
150
        vars: &vars,
151
        end_test: &end_test,
152
        result_forms: &result_forms,
153
        body,
154
        sequential: true,
155
    };
156
    compile_do_runtime_for_stack(ctx, emit, symbols, &dl)
157
}
158

            
159
220
pub(super) fn compile_do_star(
160
220
    ctx: &mut CompileContext,
161
220
    emit: &mut FunctionEmitter,
162
220
    symbols: &mut SymbolTable,
163
220
    args: &[Expr],
164
220
) -> Result<()> {
165
220
    if args.len() < 2 {
166
        return Err(Error::Compile(
167
            "DO* requires a variable list and an end clause".to_string(),
168
        ));
169
220
    }
170
220
    let vars = parse_do_vars("DO*", &args[0])?;
171
220
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
172
220
    let end_test = end_test.clone();
173
220
    let result_forms: Vec<Expr> = result_forms.to_vec();
174
220
    let body = &args[2..];
175

            
176
    // DO* evaluates inits sequentially — each binding is visible to later inits
177
220
    let mut local = symbols.clone();
178
220
    let mut needs_runtime = false;
179
396
    for v in &vars {
180
396
        let val = match &v.init {
181
396
            Some(expr) => eval_value(&mut local, expr)?,
182
            None => Expr::Nil,
183
        };
184
396
        if val.is_wasm_runtime() {
185
            needs_runtime = true;
186
396
        }
187
396
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
188
    }
189

            
190
220
    if !needs_runtime {
191
220
        let test_result = eval_value(&mut local, &end_test)?;
192
220
        needs_runtime = test_result.is_wasm_runtime();
193
    }
194

            
195
220
    if needs_runtime {
196
44
        let dl = DoLoop {
197
44
            vars: &vars,
198
44
            end_test: &end_test,
199
44
            result_forms: &result_forms,
200
44
            body,
201
44
            sequential: true,
202
44
        };
203
44
        return compile_do_runtime(ctx, emit, symbols, &dl);
204
176
    }
205

            
206
176
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
207
352
    for v in &vars {
208
352
        stepped.push((v.name.clone(), v.step.clone()));
209
352
    }
210

            
211
176
    if !static_loop_terminates(&local, &end_test, &stepped, true) {
212
88
        let dl = DoLoop {
213
88
            vars: &vars,
214
88
            end_test: &end_test,
215
88
            result_forms: &result_forms,
216
88
            body,
217
88
            sequential: true,
218
88
        };
219
88
        return compile_do_runtime(ctx, emit, symbols, &dl);
220
88
    }
221

            
222
    loop {
223
220
        let test = eval_value(&mut local, &end_test)?;
224
220
        if is_truthy(&test) {
225
88
            return if result_forms.is_empty() {
226
                compile_nil(ctx, emit);
227
                Ok(())
228
            } else {
229
88
                compile_body(ctx, emit, &mut local, &result_forms)
230
            };
231
132
        }
232
132
        for expr in body {
233
            compile_for_effect(ctx, emit, &mut local, expr)?;
234
        }
235
264
        for (name, step) in &stepped {
236
264
            if let Some(s) = step {
237
264
                let val = eval_value(&mut local, s)?;
238
264
                local
239
264
                    .lookup_mut(name)
240
264
                    .expect("DO* variable must exist")
241
264
                    .set_value(val);
242
            }
243
        }
244
    }
245
220
}
246

            
247
pub(super) fn compile_do_for_effect(
248
    ctx: &mut CompileContext,
249
    emit: &mut FunctionEmitter,
250
    symbols: &mut SymbolTable,
251
    args: &[Expr],
252
) -> Result<()> {
253
    if args.len() < 2 {
254
        return Err(Error::Compile(
255
            "DO requires a variable list and an end clause".to_string(),
256
        ));
257
    }
258
    let vars = parse_do_vars("DO", &args[0])?;
259
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
260
    let end_test = end_test.clone();
261
    let result_forms: Vec<Expr> = result_forms.to_vec();
262
    let body = &args[2..];
263

            
264
    let init_results: Vec<Expr> = vars
265
        .iter()
266
        .map(|v| match &v.init {
267
            Some(expr) => eval_value(symbols, expr),
268
            None => Ok(Expr::Nil),
269
        })
270
        .collect::<Result<_>>()?;
271

            
272
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
273
    if !needs_runtime {
274
        let mut trial = symbols.clone();
275
        for (v, val) in vars.iter().zip(&init_results) {
276
            trial.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
277
        }
278
        let test_result = eval_value(&mut trial, &end_test)?;
279
        needs_runtime = test_result.is_wasm_runtime();
280
    }
281

            
282
    if needs_runtime {
283
        let dl = DoLoop {
284
            vars: &vars,
285
            end_test: &end_test,
286
            result_forms: &result_forms,
287
            body,
288
            sequential: false,
289
        };
290
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
291
    }
292

            
293
    let mut local = symbols.clone();
294
    for (v, val) in vars.iter().zip(&init_results) {
295
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
296
    }
297
    let stepped: Vec<(String, Option<Expr>)> = vars
298
        .iter()
299
        .map(|v| (v.name.clone(), v.step.clone()))
300
        .collect();
301

            
302
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
303
        let dl = DoLoop {
304
            vars: &vars,
305
            end_test: &end_test,
306
            result_forms: &result_forms,
307
            body,
308
            sequential: false,
309
        };
310
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
311
    }
312

            
313
    loop {
314
        let test = eval_value(&mut local, &end_test)?;
315
        if is_truthy(&test) {
316
            for expr in &result_forms {
317
                compile_for_effect(ctx, emit, &mut local, expr)?;
318
            }
319
            return Ok(());
320
        }
321
        for expr in body {
322
            compile_for_effect(ctx, emit, &mut local, expr)?;
323
        }
324
        let new_values: Vec<(&str, Expr)> = stepped
325
            .iter()
326
            .filter_map(|(name, step)| {
327
                step.as_ref()
328
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
329
            })
330
            .collect::<Result<_>>()?;
331
        for (name, val) in new_values {
332
            local
333
                .lookup_mut(name)
334
                .expect("DO variable must exist")
335
                .set_value(val);
336
        }
337
    }
338
}
339

            
340
pub(super) fn compile_do_star_for_effect(
341
    ctx: &mut CompileContext,
342
    emit: &mut FunctionEmitter,
343
    symbols: &mut SymbolTable,
344
    args: &[Expr],
345
) -> Result<()> {
346
    if args.len() < 2 {
347
        return Err(Error::Compile(
348
            "DO* requires a variable list and an end clause".to_string(),
349
        ));
350
    }
351
    let vars = parse_do_vars("DO*", &args[0])?;
352
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
353
    let end_test = end_test.clone();
354
    let result_forms: Vec<Expr> = result_forms.to_vec();
355
    let body = &args[2..];
356

            
357
    let mut local = symbols.clone();
358
    let mut needs_runtime = false;
359
    for v in &vars {
360
        let val = match &v.init {
361
            Some(expr) => eval_value(&mut local, expr)?,
362
            None => Expr::Nil,
363
        };
364
        if val.is_wasm_runtime() {
365
            needs_runtime = true;
366
        }
367
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
368
    }
369

            
370
    if !needs_runtime {
371
        let test_result = eval_value(&mut local, &end_test)?;
372
        needs_runtime = test_result.is_wasm_runtime();
373
    }
374

            
375
    if needs_runtime {
376
        let dl = DoLoop {
377
            vars: &vars,
378
            end_test: &end_test,
379
            result_forms: &result_forms,
380
            body,
381
            sequential: true,
382
        };
383
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
384
    }
385

            
386
    let stepped: Vec<(String, Option<Expr>)> = vars
387
        .iter()
388
        .map(|v| (v.name.clone(), v.step.clone()))
389
        .collect();
390

            
391
    if !static_loop_terminates(&local, &end_test, &stepped, true) {
392
        let dl = DoLoop {
393
            vars: &vars,
394
            end_test: &end_test,
395
            result_forms: &result_forms,
396
            body,
397
            sequential: true,
398
        };
399
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
400
    }
401

            
402
    loop {
403
        let test = eval_value(&mut local, &end_test)?;
404
        if is_truthy(&test) {
405
            for expr in &result_forms {
406
                compile_for_effect(ctx, emit, &mut local, expr)?;
407
            }
408
            return Ok(());
409
        }
410
        for expr in body {
411
            compile_for_effect(ctx, emit, &mut local, expr)?;
412
        }
413
        for (name, step) in &stepped {
414
            if let Some(s) = step {
415
                let val = eval_value(&mut local, s)?;
416
                local
417
                    .lookup_mut(name)
418
                    .expect("DO* variable must exist")
419
                    .set_value(val);
420
            }
421
        }
422
    }
423
}
424

            
425
758
pub(super) fn compile_dolist_for_effect(
426
758
    ctx: &mut CompileContext,
427
758
    emit: &mut FunctionEmitter,
428
758
    symbols: &mut SymbolTable,
429
758
    args: &[Expr],
430
758
) -> Result<()> {
431
758
    if args.len() < 2 {
432
        return Err(Error::Compile(
433
            "DOLIST requires a variable specification and at least one body form".to_string(),
434
        ));
435
758
    }
436

            
437
758
    let var_spec = args[0].as_list().ok_or_else(|| {
438
        Error::Compile(format!(
439
            "DOLIST: expected variable specification list, got {:?}",
440
            args[0]
441
        ))
442
    })?;
443

            
444
758
    if var_spec.len() < 2 || var_spec.len() > 3 {
445
        return Err(Error::Compile(
446
            "DOLIST: variable specification must be (var list) or (var list result)".to_string(),
447
        ));
448
758
    }
449

            
450
758
    let var_name = var_spec[0].as_symbol().ok_or_else(|| {
451
        Error::Compile(format!(
452
            "DOLIST: variable must be a symbol, got {:?}",
453
            var_spec[0]
454
        ))
455
    })?;
456

            
457
758
    let list_expr = &var_spec[1];
458
758
    let body = &args[1..];
459

            
460
758
    let list_value = eval_value(symbols, list_expr)?;
461

            
462
758
    if list_value.wasm_type() == Some(WasmType::ConsRef) {
463
758
        return compile_dolist_runtime_for_effect(ctx, emit, symbols, var_name, list_expr, body);
464
    }
465

            
466
    let elements = match &list_value {
467
        Expr::List(elems) => elems.clone(),
468
        Expr::Nil => vec![],
469
        Expr::Quote(inner) => match inner.as_ref() {
470
            Expr::List(elems) => elems.clone(),
471
            Expr::Nil => vec![],
472
            _ => {
473
                return Err(Error::Compile(
474
                    "DOLIST: list expression must evaluate to a list".to_string(),
475
                ));
476
            }
477
        },
478
        _ => {
479
            return Err(Error::Compile(
480
                "DOLIST: list expression must evaluate to a list".to_string(),
481
            ));
482
        }
483
    };
484

            
485
    let saved_loop_var = symbols.lookup(var_name).cloned();
486
    for element in elements {
487
        symbols.define(Symbol::new(var_name, SymbolKind::Variable).with_value(element));
488
        for expr in body {
489
            compile_for_effect(ctx, emit, symbols, expr)?;
490
        }
491
    }
492
    if let Some(saved) = saved_loop_var {
493
        symbols.define(saved);
494
    } else {
495
        symbols.remove(var_name);
496
    }
497
    Ok(())
498
758
}
499

            
500
758
fn compile_dolist_runtime_for_effect(
501
758
    ctx: &mut CompileContext,
502
758
    emit: &mut FunctionEmitter,
503
758
    symbols: &mut SymbolTable,
504
758
    var_name: &str,
505
758
    list_expr: &Expr,
506
758
    body: &[Expr],
507
758
) -> Result<()> {
508
758
    for (target, rhs) in collect_setf_targets(body) {
509
356
        if target != var_name {
510
356
            promote_to_wasm_local(ctx, emit, symbols, &target, rhs.as_ref())?;
511
        }
512
    }
513

            
514
758
    let cons_local = ctx.alloc_local(WasmType::ConsRef);
515
758
    compile_for_stack(ctx, emit, symbols, list_expr)?;
516
758
    emit.local_set(cons_local);
517

            
518
758
    let saved_loop_var = symbols.lookup(var_name).cloned();
519
758
    let var_local = ctx.alloc_local(WasmType::I32);
520
758
    symbols.define(
521
758
        Symbol::new(var_name, SymbolKind::Variable)
522
758
            .with_value(Expr::WasmLocal(var_local, WasmType::I32)),
523
    );
524

            
525
758
    emit.block_start();
526
758
    emit.loop_start();
527

            
528
758
    emit.local_get(cons_local);
529
758
    emit.ref_is_null();
530
758
    emit.br_if(1);
531

            
532
758
    emit.local_get(cons_local);
533
758
    emit.struct_get(ctx.type_idx("cons"), 0);
534
758
    emit.local_set(var_local);
535

            
536
892
    for expr in body {
537
892
        compile_for_effect(ctx, emit, symbols, expr)?;
538
    }
539

            
540
758
    emit.local_get(cons_local);
541
758
    emit.struct_get(ctx.type_idx("cons"), 1);
542
758
    emit.local_set(cons_local);
543

            
544
758
    emit.br(0);
545
758
    emit.block_end();
546
758
    emit.block_end();
547

            
548
758
    if let Some(saved) = saved_loop_var {
549
        symbols.define(saved);
550
758
    } else {
551
758
        symbols.remove(var_name);
552
758
    }
553
758
    Ok(())
554
758
}
555

            
556
2976
fn infer_wasm_type(expr: &Expr, step_hint: Option<&Expr>) -> WasmType {
557
2976
    match expr {
558
44
        Expr::WasmRuntime(ty) | Expr::WasmLocal(_, ty) => *ty,
559
1906
        Expr::Number(_) => WasmType::Ratio,
560
        Expr::Nil => {
561
1026
            if let Some(step) = step_hint
562
1026
                && step_contains_cons(step)
563
            {
564
1026
                return WasmType::ConsRef;
565
            }
566
            WasmType::I32
567
        }
568
        _ => WasmType::I32,
569
    }
570
2976
}
571

            
572
1730
fn step_contains_cons(expr: &Expr) -> bool {
573
1730
    match expr {
574
1290
        Expr::List(elems) => {
575
1290
            if let Some(Expr::Symbol(name)) = elems.first()
576
1290
                && name == "CONS"
577
            {
578
1026
                return true;
579
264
            }
580
264
            elems.iter().any(step_contains_cons)
581
        }
582
440
        _ => false,
583
    }
584
1730
}
585

            
586
struct DoLoop<'a> {
587
    vars: &'a [DoVar],
588
    end_test: &'a Expr,
589
    result_forms: &'a [Expr],
590
    body: &'a [Expr],
591
    sequential: bool,
592
}
593

            
594
1376
fn compile_do_runtime_loop(
595
1376
    ctx: &mut CompileContext,
596
1376
    emit: &mut FunctionEmitter,
597
1376
    local: &mut SymbolTable,
598
1376
    dl: &DoLoop<'_>,
599
1376
) -> Result<()> {
600
1684
    let do_var_names: Vec<_> = dl.vars.iter().map(|v| v.name.clone()).collect();
601
1376
    for (target, rhs) in collect_setf_targets(dl.body) {
602
936
        if !do_var_names.contains(&target) {
603
936
            promote_to_wasm_local(ctx, emit, local, &target, rhs.as_ref())?;
604
        }
605
    }
606

            
607
1376
    let mut wasm_vars: Vec<(String, u32, WasmType, Option<Expr>)> = Vec::new();
608
1684
    for v in dl.vars {
609
1684
        let init_expr = v.init.clone().unwrap_or(Expr::Nil);
610
1684
        let init_val = eval_value(local, &init_expr)?;
611
1684
        let ty = infer_wasm_type(&init_val, v.step.as_ref());
612
1684
        let idx = ctx.alloc_local(ty);
613
1684
        if ty == WasmType::ConsRef && matches!(init_val, Expr::Nil) {
614
88
            emit.ref_null(ctx.type_idx("cons"));
615
88
        } else {
616
1596
            compile_for_stack(ctx, emit, local, &init_expr)?;
617
        }
618
1684
        emit.local_set(idx);
619
1684
        local.define(
620
1684
            Symbol::new(&v.name, SymbolKind::Variable).with_value(Expr::WasmLocal(idx, ty)),
621
        );
622
1684
        wasm_vars.push((v.name.clone(), idx, ty, v.step.clone()));
623
    }
624

            
625
    // block $exit
626
1376
    emit.block_start();
627
    // loop $continue
628
1376
    emit.loop_start();
629

            
630
1376
    compile_for_stack(ctx, emit, local, dl.end_test)?;
631
1376
    emit.br_if(1);
632

            
633
1376
    for expr in dl.body {
634
936
        compile_for_effect(ctx, emit, local, expr)?;
635
    }
636

            
637
1376
    if dl.sequential {
638
220
        for (_, idx, _, step) in &wasm_vars {
639
220
            if let Some(step_expr) = step {
640
220
                compile_for_stack(ctx, emit, local, step_expr)?;
641
220
                emit.local_set(*idx);
642
            }
643
        }
644
    } else {
645
1244
        let step_vars: Vec<_> = wasm_vars
646
1244
            .iter()
647
1464
            .filter(|(_, _, _, step)| step.is_some())
648
1244
            .collect();
649
1464
        for (_, _, _, step) in &step_vars {
650
1464
            compile_for_stack(ctx, emit, local, step.as_ref().unwrap())?;
651
        }
652
1464
        for (_, idx, _, _) in step_vars.iter().rev() {
653
1464
            emit.local_set(*idx);
654
1464
        }
655
    }
656

            
657
1376
    emit.br(0);
658
1376
    emit.block_end(); // end loop
659
1376
    emit.block_end(); // end block
660

            
661
1376
    Ok(())
662
1376
}
663

            
664
528
fn compile_do_runtime(
665
528
    ctx: &mut CompileContext,
666
528
    emit: &mut FunctionEmitter,
667
528
    symbols: &mut SymbolTable,
668
528
    dl: &DoLoop<'_>,
669
528
) -> Result<()> {
670
528
    let mut local = symbols.clone();
671
528
    compile_do_runtime_loop(ctx, emit, &mut local, dl)?;
672
528
    if dl.result_forms.is_empty() {
673
44
        compile_nil(ctx, emit);
674
44
    } else {
675
484
        compile_body(ctx, emit, &mut local, dl.result_forms)?;
676
    }
677
528
    Ok(())
678
528
}
679

            
680
fn compile_do_runtime_for_effect(
681
    ctx: &mut CompileContext,
682
    emit: &mut FunctionEmitter,
683
    symbols: &mut SymbolTable,
684
    dl: &DoLoop<'_>,
685
) -> Result<()> {
686
    let mut local = symbols.clone();
687
    compile_do_runtime_loop(ctx, emit, &mut local, dl)?;
688
    for expr in dl.result_forms {
689
        compile_for_effect(ctx, emit, &mut local, expr)?;
690
    }
691
    Ok(())
692
}
693

            
694
848
fn compile_do_runtime_for_stack(
695
848
    ctx: &mut CompileContext,
696
848
    emit: &mut FunctionEmitter,
697
848
    symbols: &mut SymbolTable,
698
848
    dl: &DoLoop<'_>,
699
848
) -> Result<WasmType> {
700
848
    let mut local = symbols.clone();
701
848
    compile_do_runtime_loop(ctx, emit, &mut local, dl)?;
702
848
    if dl.result_forms.is_empty() {
703
        emit.i32_const(0);
704
        Ok(WasmType::I32)
705
    } else {
706
848
        let last = dl.result_forms.last().unwrap();
707
848
        for expr in &dl.result_forms[..dl.result_forms.len() - 1] {
708
            compile_for_effect(ctx, emit, &mut local, expr)?;
709
        }
710
848
        compile_for_stack(ctx, emit, &mut local, last)
711
    }
712
848
}
713

            
714
pub(super) fn compile_dolist(
715
    ctx: &mut CompileContext,
716
    emit: &mut FunctionEmitter,
717
    symbols: &mut SymbolTable,
718
    args: &[Expr],
719
) -> Result<()> {
720
    if args.len() < 2 {
721
        return Err(Error::Compile(
722
            "DOLIST requires a variable specification and at least one body form".to_string(),
723
        ));
724
    }
725

            
726
    let var_spec = args[0].as_list().ok_or_else(|| {
727
        Error::Compile(format!(
728
            "DOLIST: expected variable specification list, got {:?}",
729
            args[0]
730
        ))
731
    })?;
732

            
733
    if var_spec.len() < 2 || var_spec.len() > 3 {
734
        return Err(Error::Compile(
735
            "DOLIST: variable specification must be (var list) or (var list result)".to_string(),
736
        ));
737
    }
738

            
739
    let var_name = var_spec[0].as_symbol().ok_or_else(|| {
740
        Error::Compile(format!(
741
            "DOLIST: variable must be a symbol, got {:?}",
742
            var_spec[0]
743
        ))
744
    })?;
745

            
746
    let list_expr = &var_spec[1];
747
    let result_expr = var_spec.get(2).cloned();
748
    let body = &args[1..];
749

            
750
    let list_value = eval_value(symbols, list_expr)?;
751

            
752
    if list_value.wasm_type() == Some(WasmType::ConsRef) {
753
        return compile_dolist_runtime(ctx, emit, symbols, var_name, list_expr, result_expr, body);
754
    }
755

            
756
    let elements = match &list_value {
757
        Expr::List(elems) => elems.clone(),
758
        Expr::Nil => vec![],
759
        Expr::Quote(inner) => match inner.as_ref() {
760
            Expr::List(elems) => elems.clone(),
761
            Expr::Nil => vec![],
762
            _ => {
763
                return Err(Error::Compile(
764
                    "DOLIST: list expression must evaluate to a list".to_string(),
765
                ));
766
            }
767
        },
768
        _ => {
769
            return Err(Error::Compile(
770
                "DOLIST: list expression must evaluate to a list".to_string(),
771
            ));
772
        }
773
    };
774

            
775
    let saved_loop_var = symbols.lookup(var_name).cloned();
776
    for element in elements {
777
        symbols.define(Symbol::new(var_name, SymbolKind::Variable).with_value(element));
778
        for expr in body {
779
            compile_for_effect(ctx, emit, symbols, expr)?;
780
        }
781
    }
782
    if let Some(saved) = saved_loop_var {
783
        symbols.define(saved);
784
    } else {
785
        symbols.remove(var_name);
786
    }
787

            
788
    if let Some(result) = result_expr {
789
        compile_expr(ctx, emit, symbols, &result)
790
    } else {
791
        compile_nil(ctx, emit);
792
        Ok(())
793
    }
794
}
795

            
796
2134
fn collect_setf_targets(exprs: &[Expr]) -> Vec<(String, Option<Expr>)> {
797
2134
    let mut targets = Vec::new();
798
2136
    for expr in exprs {
799
1828
        collect_setf_targets_inner(expr, &mut targets);
800
1828
    }
801
2134
    targets
802
2134
}
803

            
804
28742
fn collect_setf_targets_inner(expr: &Expr, targets: &mut Vec<(String, Option<Expr>)>) {
805
28742
    if let Expr::List(elems) = expr {
806
9820
        if let Some(Expr::Symbol(name)) = elems.first()
807
9820
            && name == "SETF"
808
        {
809
1292
            for pair in elems[1..].chunks(2) {
810
1292
                if let Some(Expr::Symbol(var)) = pair.first()
811
1292
                    && !targets.iter().any(|(n, _)| n == var)
812
1292
                {
813
1292
                    targets.push((var.clone(), pair.get(1).cloned()));
814
1292
                }
815
            }
816
8528
        }
817
26914
        for elem in elems {
818
26914
            collect_setf_targets_inner(elem, targets);
819
26914
        }
820
18922
    }
821
28742
}
822

            
823
1292
fn promote_to_wasm_local(
824
1292
    ctx: &mut CompileContext,
825
1292
    emit: &mut FunctionEmitter,
826
1292
    symbols: &mut SymbolTable,
827
1292
    name: &str,
828
1292
    step_hint: Option<&Expr>,
829
1292
) -> Result<()> {
830
1292
    let sym = symbols
831
1292
        .lookup(name)
832
1292
        .ok_or_else(|| Error::UndefinedSymbol(name.to_string()))?;
833
1292
    if matches!(
834
1292
        sym.value(),
835
        Some(Expr::WasmLocal(_, _) | Expr::WasmRuntime(_))
836
    ) {
837
        return Ok(());
838
1292
    }
839
1292
    let val = sym.value().cloned().unwrap_or(Expr::Nil);
840
1292
    let ty = infer_wasm_type(&val, step_hint);
841
1292
    let idx = ctx.alloc_local(ty);
842
1292
    if ty == WasmType::ConsRef && matches!(val, Expr::Nil) {
843
938
        emit.ref_null(ctx.type_idx("cons"));
844
938
    } else {
845
354
        compile_for_stack(ctx, emit, symbols, &val)?;
846
    }
847
1292
    emit.local_set(idx);
848
1292
    symbols.define(Symbol::new(name, SymbolKind::Variable).with_value(Expr::WasmLocal(idx, ty)));
849
1292
    Ok(())
850
1292
}
851

            
852
fn compile_dolist_runtime(
853
    ctx: &mut CompileContext,
854
    emit: &mut FunctionEmitter,
855
    symbols: &mut SymbolTable,
856
    var_name: &str,
857
    list_expr: &Expr,
858
    result_expr: Option<Expr>,
859
    body: &[Expr],
860
) -> Result<()> {
861
    // Promote SETF targets in body to WASM locals so runtime updates persist
862
    for (target, rhs) in collect_setf_targets(body) {
863
        if target != var_name {
864
            promote_to_wasm_local(ctx, emit, symbols, &target, rhs.as_ref())?;
865
        }
866
    }
867

            
868
    let cons_local = ctx.alloc_local(WasmType::ConsRef);
869
    compile_for_stack(ctx, emit, symbols, list_expr)?;
870
    emit.local_set(cons_local);
871

            
872
    let saved_loop_var = symbols.lookup(var_name).cloned();
873
    let var_local = ctx.alloc_local(WasmType::I32);
874
    symbols.define(
875
        Symbol::new(var_name, SymbolKind::Variable)
876
            .with_value(Expr::WasmLocal(var_local, WasmType::I32)),
877
    );
878

            
879
    // block $exit
880
    emit.block_start();
881
    // loop $continue
882
    emit.loop_start();
883

            
884
    // test: ref.is_null → exit
885
    emit.local_get(cons_local);
886
    emit.ref_is_null();
887
    emit.br_if(1);
888

            
889
    // car → loop variable
890
    emit.local_get(cons_local);
891
    emit.struct_get(ctx.type_idx("cons"), 0);
892
    emit.local_set(var_local);
893

            
894
    for expr in body {
895
        compile_for_effect(ctx, emit, symbols, expr)?;
896
    }
897

            
898
    // cdr → update cons local
899
    emit.local_get(cons_local);
900
    emit.struct_get(ctx.type_idx("cons"), 1);
901
    emit.local_set(cons_local);
902

            
903
    emit.br(0);
904
    emit.block_end(); // end loop
905
    emit.block_end(); // end block
906

            
907
    // Restore loop variable
908
    if let Some(saved) = saved_loop_var {
909
        symbols.define(saved);
910
    } else {
911
        symbols.remove(var_name);
912
    }
913

            
914
    if let Some(result) = result_expr {
915
        compile_expr(ctx, emit, symbols, &result)
916
    } else {
917
        compile_nil(ctx, emit);
918
        Ok(())
919
    }
920
}
921

            
922
2054
pub(super) fn do_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
923
2054
    if args.len() < 2 {
924
        return Err(Error::Compile(
925
            "DO requires a variable list and an end clause".to_string(),
926
        ));
927
2054
    }
928
2054
    let vars = parse_do_vars("DO", &args[0])?;
929
2054
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
930
2054
    let end_test = end_test.clone();
931
2054
    let result_forms: Vec<Expr> = result_forms.to_vec();
932
2054
    let body = &args[2..];
933

            
934
2054
    let init_values: Vec<(String, Expr, Option<Expr>)> = vars
935
2054
        .into_iter()
936
2098
        .map(|v| {
937
2098
            let val = match v.init {
938
2098
                Some(expr) => eval_value(symbols, &expr)?,
939
                None => Expr::Nil,
940
            };
941
2098
            Ok((v.name, val, v.step))
942
2098
        })
943
2054
        .collect::<Result<_>>()?;
944

            
945
2098
    let needs_runtime = init_values.iter().any(|(_, v, _)| v.is_wasm_runtime());
946

            
947
2054
    let mut local = symbols.clone();
948
2054
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
949
2098
    for (name, val, step) in &init_values {
950
2098
        local.define(Symbol::new(name, SymbolKind::Variable).with_value(val.clone()));
951
2098
        stepped.push((name.clone(), step.clone()));
952
2098
    }
953

            
954
2054
    if needs_runtime {
955
        return Ok(Expr::WasmRuntime(WasmType::ConsRef));
956
    } else {
957
2054
        let test_result = eval_value(&mut local, &end_test)?;
958
2054
        if test_result.is_wasm_runtime() {
959
2054
            return Ok(Expr::WasmRuntime(WasmType::ConsRef));
960
        }
961
    }
962

            
963
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
964
        return Ok(Expr::WasmRuntime(WasmType::ConsRef));
965
    }
966

            
967
    loop {
968
        let test = eval_value(&mut local, &end_test)?;
969
        if is_truthy(&test) {
970
            return if result_forms.is_empty() {
971
                Ok(Expr::Nil)
972
            } else {
973
                eval_body(&mut local, &result_forms)
974
            };
975
        }
976
        for expr in body {
977
            eval_value(&mut local, expr)?;
978
        }
979
        let new_values: Vec<(&str, Expr)> = stepped
980
            .iter()
981
            .filter_map(|(name, step)| {
982
                step.as_ref()
983
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
984
            })
985
            .collect::<Result<_>>()?;
986
        for (name, val) in new_values {
987
            local
988
                .lookup_mut(name)
989
                .expect("DO variable must exist")
990
                .set_value(val);
991
        }
992
    }
993
2054
}
994

            
995
pub(super) fn do_star_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
996
    if args.len() < 2 {
997
        return Err(Error::Compile(
998
            "DO* requires a variable list and an end clause".to_string(),
999
        ));
    }
    let vars = parse_do_vars("DO*", &args[0])?;
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
    let end_test = end_test.clone();
    let result_forms: Vec<Expr> = result_forms.to_vec();
    let body = &args[2..];
    let mut local = symbols.clone();
    let mut needs_runtime = false;
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
    for v in vars {
        let val = match v.init {
            Some(expr) => eval_value(&mut local, &expr)?,
            None => Expr::Nil,
        };
        if val.is_wasm_runtime() {
            needs_runtime = true;
        }
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
        stepped.push((v.name, v.step));
    }
    if !needs_runtime {
        let test_result = eval_value(&mut local, &end_test)?;
        if test_result.is_wasm_runtime() {
            needs_runtime = true;
        }
    }
    if needs_runtime {
        return Ok(Expr::WasmRuntime(WasmType::ConsRef));
    }
    if !static_loop_terminates(&local, &end_test, &stepped, true) {
        return Ok(Expr::WasmRuntime(WasmType::ConsRef));
    }
    loop {
        let test = eval_value(&mut local, &end_test)?;
        if is_truthy(&test) {
            return if result_forms.is_empty() {
                Ok(Expr::Nil)
            } else {
                eval_body(&mut local, &result_forms)
            };
        }
        for expr in body {
            eval_value(&mut local, expr)?;
        }
        for (name, step) in &stepped {
            if let Some(s) = step {
                let val = eval_value(&mut local, s)?;
                local
                    .lookup_mut(name)
                    .expect("DO* variable must exist")
                    .set_value(val);
            }
        }
    }
}
1290
pub(super) fn dolist_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
1290
    if args.len() < 2 {
44
        return Err(Error::Compile(
44
            "DOLIST requires a variable specification and at least one body form".to_string(),
44
        ));
1246
    }
1246
    let var_spec = args[0].as_list().ok_or_else(|| {
44
        Error::Compile(format!(
44
            "DOLIST: expected variable specification list, got {:?}",
44
            args[0]
44
        ))
44
    })?;
1202
    if var_spec.len() < 2 || var_spec.len() > 3 {
        return Err(Error::Compile(
            "DOLIST: variable specification must be (var list) or (var list result)".to_string(),
        ));
1202
    }
1202
    let var_name = var_spec[0].as_symbol().ok_or_else(|| {
44
        Error::Compile(format!(
44
            "DOLIST: variable must be a symbol, got {:?}",
44
            var_spec[0]
44
        ))
44
    })?;
1158
    let list_expr = &var_spec[1];
1158
    let result_expr = var_spec.get(2).cloned();
1158
    let body = &args[1..];
1158
    let list_value = eval_value(symbols, list_expr)?;
1158
    if list_value.wasm_type() == Some(WasmType::ConsRef) {
938
        return Ok(Expr::WasmRuntime(WasmType::ConsRef));
220
    }
220
    let elements = match &list_value {
        Expr::List(elems) => elems.clone(),
44
        Expr::Nil => vec![],
132
        Expr::Quote(inner) => match inner.as_ref() {
132
            Expr::List(elems) => elems.clone(),
            Expr::Nil => vec![],
            _ => {
                return Err(Error::Compile(
                    "DOLIST: list expression must evaluate to a list".to_string(),
                ));
            }
        },
        _ => {
44
            return Err(Error::Compile(
44
                "DOLIST: list expression must evaluate to a list".to_string(),
44
            ));
        }
    };
176
    let saved_loop_var = symbols.lookup(var_name).cloned();
264
    for element in elements {
264
        symbols.define(Symbol::new(var_name, SymbolKind::Variable).with_value(element));
264
        for expr in body {
264
            eval_value(symbols, expr)?;
        }
    }
176
    if let Some(saved) = saved_loop_var {
        symbols.define(saved);
176
    } else {
176
        symbols.remove(var_name);
176
    }
176
    if let Some(result) = result_expr {
44
        eval_value(symbols, &result)
    } else {
132
        Ok(Expr::Nil)
    }
1290
}
struct DoVar {
    name: String,
    init: Option<Expr>,
    step: Option<Expr>,
}
3738
fn parse_do_vars(context: &str, expr: &Expr) -> Result<Vec<DoVar>> {
3738
    let list = match expr {
3738
        Expr::List(elems) => elems,
        _ => {
            return Err(Error::Compile(format!(
                "{context}: expected variable list, got {expr:?}"
            )));
        }
    };
3738
    list.iter()
4266
        .map(|spec| match spec {
            Expr::Symbol(name) => Ok(DoVar {
                name: name.clone(),
                init: None,
                step: None,
            }),
4266
            Expr::List(elems) if !elems.is_empty() && elems.len() <= 3 => {
4266
                let name = elems[0].as_symbol().ok_or_else(|| {
                    Error::Compile(format!(
                        "{context}: variable name must be a symbol, got {:?}",
                        elems[0]
                    ))
                })?;
4266
                Ok(DoVar {
4266
                    name: name.to_string(),
4266
                    init: elems.get(1).cloned(),
4266
                    step: elems.get(2).cloned(),
4266
                })
            }
            _ => Err(Error::Compile(format!(
                "{context}: malformed variable spec: {spec:?}"
            ))),
4266
        })
3738
        .collect()
3738
}
const MAX_STATIC_LOOP_ITERS: usize = 64;
484
fn static_loop_terminates(
484
    local: &SymbolTable,
484
    end_test: &Expr,
484
    stepped: &[(String, Option<Expr>)],
484
    sequential: bool,
484
) -> bool {
484
    let mut sim = local.clone();
484
    for _ in 0..MAX_STATIC_LOOP_ITERS {
12408
        match eval_value(&mut sim, end_test) {
12408
            Ok(test) if is_truthy(&test) => return true,
12100
            Ok(_) => {}
            Err(_) => return false,
        }
12100
        if sequential {
11528
            for (name, step) in stepped {
11528
                let Some(s) = step else { continue };
11528
                match eval_value(&mut sim, s) {
11528
                    Ok(val) => {
11528
                        if let Some(sym) = sim.lookup_mut(name) {
11528
                            sym.set_value(val);
11528
                        }
                    }
                    Err(_) => return false,
                }
            }
        } else {
6336
            let new_vals: Vec<_> = stepped
6336
                .iter()
12320
                .filter_map(|(name, step)| {
12320
                    step.as_ref()
12320
                        .and_then(|s| eval_value(&mut sim, s).ok().map(|v| (name.clone(), v)))
12320
                })
6336
                .collect();
12188
            for (name, val) in new_vals {
12188
                if let Some(sym) = sim.lookup_mut(&name) {
12188
                    sym.set_value(val);
12188
                }
            }
        }
    }
176
    false
484
}
3738
fn parse_end_clause<'a>(context: &str, expr: &'a Expr) -> Result<(&'a Expr, &'a [Expr])> {
3738
    let elems = expr.as_list().ok_or_else(|| {
        Error::Compile(format!(
            "{context}: end clause must be a list, got {expr:?}"
        ))
    })?;
3738
    if elems.is_empty() {
        return Err(Error::Compile(format!(
            "{context}: end clause must have a test form"
        )));
3738
    }
3738
    Ok((&elems[0], &elems[1..]))
3738
}