1
//! `DO` (parallel-step variant) compile + eval handlers.
2
//!
3
//! The three compile entry points (`compile_do`, `_for_effect`,
4
//! `_for_stack`) each handle the static-fold path inline; if the
5
//! end-test or any init is runtime-typed, they hand off to the
6
//! corresponding `runtime::compile_do_runtime{,_for_effect,_for_stack}`.
7
//! `do_form` is the eval-only handler for value-position use.
8

            
9
use crate::ast::{Expr, PairElement, WasmType};
10
use crate::compiler::context::CompileContext;
11
use crate::compiler::emit::FunctionEmitter;
12
use crate::compiler::expr::{
13
    classify_stack_type, compile_body, compile_for_effect, compile_nil, eval_value,
14
};
15
use crate::error::{Error, Result};
16
use crate::runtime::{Symbol, SymbolKind, SymbolTable};
17

            
18
use super::super::binding::eval_body;
19
use super::super::control::is_truthy;
20
use super::common::{
21
    DoLoop, infer_result_pair_element, infer_wasm_type, parse_do_vars, parse_end_clause,
22
    static_loop_terminates,
23
};
24
use super::runtime::{
25
    compile_do_runtime, compile_do_runtime_for_effect, compile_do_runtime_for_stack,
26
};
27

            
28
1904
pub(super) fn compile_do(
29
1904
    ctx: &mut CompileContext,
30
1904
    emit: &mut FunctionEmitter,
31
1904
    symbols: &mut SymbolTable,
32
1904
    args: &[Expr],
33
1904
) -> Result<()> {
34
1904
    if args.len() < 2 {
35
272
        return Err(Error::Compile(
36
272
            "DO requires a variable list and an end clause".to_string(),
37
272
        ));
38
1632
    }
39
1632
    let vars = parse_do_vars("DO", &args[0])?;
40
1632
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
41
1632
    let end_test = end_test.clone();
42
1632
    let result_forms: Vec<Expr> = result_forms.to_vec();
43
1632
    let body = &args[2..];
44

            
45
1632
    let init_results: Vec<Expr> = vars
46
1632
        .iter()
47
2380
        .map(|v| match &v.init {
48
2312
            Some(expr) => eval_value(symbols, expr),
49
68
            None => Ok(Expr::Nil),
50
2380
        })
51
1632
        .collect::<Result<_>>()?;
52

            
53
1632
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
54

            
55
1632
    if !needs_runtime {
56
1564
        let mut trial = symbols.clone();
57
2244
        for (v, val) in vars.iter().zip(&init_results) {
58
2244
            trial.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
59
2244
        }
60
1564
        let test_result = eval_value(&mut trial, &end_test)?;
61
1564
        needs_runtime = test_result.is_wasm_runtime();
62
68
    }
63

            
64
1632
    if needs_runtime {
65
816
        let dl = DoLoop {
66
816
            vars: &vars,
67
816
            end_test: &end_test,
68
816
            result_forms: &result_forms,
69
816
            body,
70
816
            sequential: false,
71
816
        };
72
816
        return compile_do_runtime(ctx, emit, symbols, &dl);
73
816
    }
74

            
75
816
    let mut local = symbols.clone();
76
816
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
77
1156
    for (v, val) in vars.iter().zip(&init_results) {
78
1156
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
79
1156
        stepped.push((v.name.clone(), v.step.clone()));
80
1156
    }
81

            
82
816
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
83
204
        let dl = DoLoop {
84
204
            vars: &vars,
85
204
            end_test: &end_test,
86
204
            result_forms: &result_forms,
87
204
            body,
88
204
            sequential: false,
89
204
        };
90
204
        return compile_do_runtime(ctx, emit, symbols, &dl);
91
612
    }
92

            
93
    loop {
94
2448
        let test = eval_value(&mut local, &end_test)?;
95
2448
        if is_truthy(&test) {
96
612
            return if result_forms.is_empty() {
97
68
                compile_nil(ctx, emit);
98
68
                Ok(())
99
            } else {
100
544
                compile_body(ctx, emit, &mut local, &result_forms)
101
            };
102
1836
        }
103
1836
        for expr in body {
104
            compile_for_effect(ctx, emit, &mut local, expr)?;
105
        }
106
1836
        let new_values: Vec<(&str, Expr)> = stepped
107
1836
            .iter()
108
2380
            .filter_map(|(name, step)| {
109
2380
                step.as_ref()
110
2380
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
111
2380
            })
112
1836
            .collect::<Result<_>>()?;
113
2176
        for (name, val) in new_values {
114
2176
            local
115
2176
                .lookup_mut(name)
116
2176
                .expect("DO variable must exist")
117
2176
                .set_value(val);
118
2176
        }
119
    }
120
1904
}
121

            
122
2396
pub(super) fn compile_do_for_stack(
123
2396
    ctx: &mut CompileContext,
124
2396
    emit: &mut FunctionEmitter,
125
2396
    symbols: &mut SymbolTable,
126
2396
    args: &[Expr],
127
2396
) -> Result<WasmType> {
128
2396
    if args.len() < 2 {
129
        return Err(Error::Compile(
130
            "DO requires a variable list and an end clause".to_string(),
131
        ));
132
2396
    }
133
2396
    let vars = parse_do_vars("DO", &args[0])?;
134
2396
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
135
2396
    let end_test = end_test.clone();
136
2396
    let result_forms: Vec<Expr> = result_forms.to_vec();
137
2396
    let body = &args[2..];
138
2396
    let dl = DoLoop {
139
2396
        vars: &vars,
140
2396
        end_test: &end_test,
141
2396
        result_forms: &result_forms,
142
2396
        body,
143
2396
        sequential: false,
144
2396
    };
145
2396
    compile_do_runtime_for_stack(ctx, emit, symbols, &dl)
146
2396
}
147

            
148
272
pub(super) fn compile_do_for_effect(
149
272
    ctx: &mut CompileContext,
150
272
    emit: &mut FunctionEmitter,
151
272
    symbols: &mut SymbolTable,
152
272
    args: &[Expr],
153
272
) -> Result<()> {
154
272
    if args.len() < 2 {
155
        return Err(Error::Compile(
156
            "DO requires a variable list and an end clause".to_string(),
157
        ));
158
272
    }
159
272
    let vars = parse_do_vars("DO", &args[0])?;
160
272
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
161
272
    let end_test = end_test.clone();
162
272
    let result_forms: Vec<Expr> = result_forms.to_vec();
163
272
    let body = &args[2..];
164

            
165
272
    let init_results: Vec<Expr> = vars
166
272
        .iter()
167
272
        .map(|v| match &v.init {
168
272
            Some(expr) => eval_value(symbols, expr),
169
            None => Ok(Expr::Nil),
170
272
        })
171
272
        .collect::<Result<_>>()?;
172

            
173
272
    let mut needs_runtime = init_results.iter().any(Expr::is_wasm_runtime);
174
272
    if !needs_runtime {
175
272
        let mut trial = symbols.clone();
176
272
        for (v, val) in vars.iter().zip(&init_results) {
177
272
            trial.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
178
272
        }
179
272
        let test_result = eval_value(&mut trial, &end_test)?;
180
272
        needs_runtime = test_result.is_wasm_runtime();
181
    }
182

            
183
272
    if needs_runtime {
184
        let dl = DoLoop {
185
            vars: &vars,
186
            end_test: &end_test,
187
            result_forms: &result_forms,
188
            body,
189
            sequential: false,
190
        };
191
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
192
272
    }
193

            
194
272
    let mut local = symbols.clone();
195
272
    for (v, val) in vars.iter().zip(&init_results) {
196
272
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val.clone()));
197
272
    }
198
272
    let stepped: Vec<(String, Option<Expr>)> = vars
199
272
        .iter()
200
272
        .map(|v| (v.name.clone(), v.step.clone()))
201
272
        .collect();
202

            
203
272
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
204
        let dl = DoLoop {
205
            vars: &vars,
206
            end_test: &end_test,
207
            result_forms: &result_forms,
208
            body,
209
            sequential: false,
210
        };
211
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
212
272
    }
213

            
214
    loop {
215
884
        let test = eval_value(&mut local, &end_test)?;
216
884
        if is_truthy(&test) {
217
272
            for expr in &result_forms {
218
68
                compile_for_effect(ctx, emit, &mut local, expr)?;
219
            }
220
272
            return Ok(());
221
612
        }
222
612
        for expr in body {
223
408
            compile_for_effect(ctx, emit, &mut local, expr)?;
224
        }
225
612
        let new_values: Vec<(&str, Expr)> = stepped
226
612
            .iter()
227
612
            .filter_map(|(name, step)| {
228
612
                step.as_ref()
229
612
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
230
612
            })
231
612
            .collect::<Result<_>>()?;
232
612
        for (name, val) in new_values {
233
612
            local
234
612
                .lookup_mut(name)
235
612
                .expect("DO variable must exist")
236
612
                .set_value(val);
237
612
        }
238
    }
239
272
}
240

            
241
/// The runtime type a DO loop yields, mirroring `compile_do_runtime_for_stack`
242
/// (which returns `compile_for_stack` of the LAST result form, or `Bool`/nil
243
/// when there is none). A result form the body grows via
244
/// `(setf acc (cons V acc))` is a list whose element comes from the cons car
245
/// (`infer_result_pair_element`); any other form — a counter, a do var — takes
246
/// its own resolved runtime type. Hard-coding `PairRef` mistyped a counting
247
/// loop `(do … (end n) (setf n (+ n 1)))` as a pair, so `(= (count …) 0)`
248
/// rejected it as "= expects numeric arguments, got pair".
249
4180
pub(super) fn runtime_result_type(
250
4180
    result_forms: &[Expr],
251
4180
    body: &[Expr],
252
4180
    stepped: &[(String, Option<Expr>)],
253
4180
    infer_env: &SymbolTable,
254
4180
) -> WasmType {
255
4180
    let Some(last) = result_forms.last() else {
256
        return WasmType::Bool;
257
    };
258
4180
    if let Some(elem) = infer_result_pair_element(last, body, stepped, infer_env) {
259
2949
        return WasmType::PairRef(elem);
260
1231
    }
261
    // Resolve the result form against the runtime-typed loop env, then type it
262
    // the way codegen's `compile_for_stack` would: a runtime value reports its
263
    // own `wasm_type`; a const numeric literal goes through `classify_stack_type`
264
    // (an integer counter → I32, matching the let-promoted local codegen emits).
265
1231
    let resolved = eval_value(&mut infer_env.clone(), last).unwrap_or(Expr::Nil);
266
1231
    resolved
267
1231
        .wasm_type()
268
1231
        .or_else(|| classify_stack_type(&resolved))
269
1231
        .unwrap_or(WasmType::PairRef(PairElement::I32))
270
4180
}
271

            
272
4042
pub(super) fn do_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
273
4042
    if args.len() < 2 {
274
68
        return Err(Error::Compile(
275
68
            "DO requires a variable list and an end clause".to_string(),
276
68
        ));
277
3974
    }
278
3974
    let vars = parse_do_vars("DO", &args[0])?;
279
3974
    let (end_test, result_forms) = parse_end_clause("DO", &args[1])?;
280
3974
    let end_test = end_test.clone();
281
3974
    let result_forms: Vec<Expr> = result_forms.to_vec();
282
3974
    let body = &args[2..];
283

            
284
3974
    let init_values: Vec<(String, Expr, Option<Expr>)> = vars
285
3974
        .into_iter()
286
4654
        .map(|v| {
287
4654
            let val = match v.init {
288
4654
                Some(expr) => eval_value(symbols, &expr)?,
289
                None => Expr::Nil,
290
            };
291
4654
            Ok((v.name, val, v.step))
292
4654
        })
293
3974
        .collect::<Result<_>>()?;
294

            
295
4654
    let needs_runtime = init_values.iter().any(|(_, v, _)| v.is_wasm_runtime());
296

            
297
3974
    let mut local = symbols.clone();
298
3974
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
299
4654
    for (name, val, step) in &init_values {
300
4654
        local.define(Symbol::new(name, SymbolKind::Variable).with_value(val.clone()));
301
4654
        stepped.push((name.clone(), step.clone()));
302
4654
    }
303

            
304
    // Compute the static return type. When a result-form names a single
305
    // accumulator that the body builds via `(setf acc (cons V acc))`,
306
    // we peek at the CONS car's type so the static type matches the
307
    // PairElement the codegen path actually emits. Without this, the
308
    // hardcoded PairRef(I32) disagrees with the codegen (which
309
    // allocates the accumulator's element from the car's WasmType),
310
    // and consumer dolists end up downcasting to the wrong element
311
    // shape — a runtime cast failure.
312
    // Resolve the accumulator's element type against an env where each DO var
313
    // is bound as its RUNTIME type, not its const init. The codegen loop var is
314
    // runtime (an integer init `(i 0 …)` makes `i` a runtime Index/I32), so a
315
    // `(cons i acc)` cell is an I32 cell; resolving `i` to the const `0` would
316
    // type it as Ratio (the numeric-literal cell slot) and a consumer dolist
317
    // would downcast to the wrong element → a runtime cast trap.
318
3974
    let mut infer_env = symbols.clone();
319
4654
    for (name, val, step) in &init_values {
320
4654
        let ty = infer_wasm_type(val, step.as_ref(), &infer_env);
321
4654
        infer_env.define(Symbol::new(name, SymbolKind::Variable).with_value(Expr::WasmRuntime(ty)));
322
4654
    }
323
3974
    let result_ty = runtime_result_type(&result_forms, body, &stepped, &infer_env);
324

            
325
3974
    if needs_runtime {
326
        return Ok(Expr::WasmRuntime(result_ty));
327
    } else {
328
3974
        let test_result = eval_value(&mut local, &end_test)?;
329
3974
        if test_result.is_wasm_runtime() {
330
3226
            return Ok(Expr::WasmRuntime(result_ty));
331
748
        }
332
    }
333

            
334
748
    if !static_loop_terminates(&local, &end_test, &stepped, false) {
335
        return Ok(Expr::WasmRuntime(result_ty));
336
748
    }
337

            
338
    loop {
339
2856
        let test = eval_value(&mut local, &end_test)?;
340
2856
        if is_truthy(&test) {
341
748
            return if result_forms.is_empty() {
342
                Ok(Expr::Nil)
343
            } else {
344
748
                eval_body(&mut local, &result_forms)
345
            };
346
2108
        }
347
2108
        for expr in body {
348
            eval_value(&mut local, expr)?;
349
        }
350
2108
        let new_values: Vec<(&str, Expr)> = stepped
351
2108
            .iter()
352
3808
            .filter_map(|(name, step)| {
353
3808
                step.as_ref()
354
3808
                    .map(|s| eval_value(&mut local, s).map(|v| (name.as_str(), v)))
355
3808
            })
356
2108
            .collect::<Result<_>>()?;
357
3808
        for (name, val) in new_values {
358
3808
            local
359
3808
                .lookup_mut(name)
360
3808
                .expect("DO variable must exist")
361
3808
                .set_value(val);
362
3808
        }
363
    }
364
4042
}