1
//! `DO*` (sequential-step variant) compile + eval handlers.
2
//!
3
//! DO* evaluates inits sequentially — each binding is visible to
4
//! later inits in the same form. Steps similarly apply one var at a
5
//! time. The sequential flag rides through `DoLoop.sequential` so the
6
//! shared `runtime::compile_do_runtime_loop` does the right thing.
7

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

            
15
use super::super::binding::eval_body;
16
use super::common::{
17
    DoLoop, infer_wasm_type, parse_do_vars, parse_end_clause, static_loop_terminates,
18
};
19
use super::runtime::{
20
    compile_do_runtime, compile_do_runtime_for_effect, compile_do_runtime_for_stack,
21
};
22

            
23
pub(super) fn compile_do_star_for_stack(
24
    ctx: &mut CompileContext,
25
    emit: &mut FunctionEmitter,
26
    symbols: &mut SymbolTable,
27
    args: &[Expr],
28
) -> Result<WasmType> {
29
    if args.len() < 2 {
30
        return Err(Error::Compile(
31
            "DO* requires a variable list and an end clause".to_string(),
32
        ));
33
    }
34
    let vars = parse_do_vars("DO*", &args[0])?;
35
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
36
    let end_test = end_test.clone();
37
    let result_forms: Vec<Expr> = result_forms.to_vec();
38
    let body = &args[2..];
39
    let dl = DoLoop {
40
        vars: &vars,
41
        end_test: &end_test,
42
        result_forms: &result_forms,
43
        body,
44
        sequential: true,
45
    };
46
    compile_do_runtime_for_stack(ctx, emit, symbols, &dl)
47
}
48

            
49
747
pub(super) fn compile_do_star(
50
747
    ctx: &mut CompileContext,
51
747
    emit: &mut FunctionEmitter,
52
747
    symbols: &mut SymbolTable,
53
747
    args: &[Expr],
54
747
) -> Result<()> {
55
747
    if args.len() < 2 {
56
83
        return Err(Error::Compile(
57
83
            "DO* requires a variable list and an end clause".to_string(),
58
83
        ));
59
664
    }
60
664
    let vars = parse_do_vars("DO*", &args[0])?;
61
664
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
62
664
    let end_test = end_test.clone();
63
664
    let result_forms: Vec<Expr> = result_forms.to_vec();
64
664
    let body = &args[2..];
65

            
66
    // DO* evaluates inits sequentially — each binding is visible to later inits
67
664
    let mut local = symbols.clone();
68
664
    let mut needs_runtime = false;
69
1245
    for v in &vars {
70
1245
        let val = match &v.init {
71
1245
            Some(expr) => eval_value(&mut local, expr)?,
72
            None => Expr::Nil,
73
        };
74
1245
        if val.is_wasm_runtime() {
75
249
            needs_runtime = true;
76
996
        }
77
1245
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
78
    }
79

            
80
664
    if !needs_runtime {
81
498
        let test_result = eval_value(&mut local, &end_test)?;
82
498
        needs_runtime = test_result.is_wasm_runtime();
83
166
    }
84

            
85
664
    if needs_runtime {
86
332
        let dl = DoLoop {
87
332
            vars: &vars,
88
332
            end_test: &end_test,
89
332
            result_forms: &result_forms,
90
332
            body,
91
332
            sequential: true,
92
332
        };
93
332
        return compile_do_runtime(ctx, emit, symbols, &dl);
94
332
    }
95

            
96
332
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
97
664
    for v in &vars {
98
664
        stepped.push((v.name.clone(), v.step.clone()));
99
664
    }
100

            
101
332
    if !static_loop_terminates(&local, &end_test, &stepped, true) {
102
166
        let dl = DoLoop {
103
166
            vars: &vars,
104
166
            end_test: &end_test,
105
166
            result_forms: &result_forms,
106
166
            body,
107
166
            sequential: true,
108
166
        };
109
166
        return compile_do_runtime(ctx, emit, symbols, &dl);
110
166
    }
111

            
112
    loop {
113
415
        let test = eval_value(&mut local, &end_test)?;
114
415
        if !test.is_nil() {
115
166
            return if result_forms.is_empty() {
116
                compile_nil(ctx, emit);
117
                Ok(())
118
            } else {
119
166
                compile_body(ctx, emit, &mut local, &result_forms)
120
            };
121
249
        }
122
249
        for expr in body {
123
            compile_for_effect(ctx, emit, &mut local, expr)?;
124
        }
125
498
        for (name, step) in &stepped {
126
498
            if let Some(s) = step {
127
498
                let val = eval_value(&mut local, s)?;
128
498
                local
129
498
                    .lookup_mut(name)
130
498
                    .expect("DO* variable must exist")
131
498
                    .set_value(val);
132
            }
133
        }
134
    }
135
747
}
136

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

            
154
83
    let mut local = symbols.clone();
155
83
    let mut needs_runtime = false;
156
83
    for v in &vars {
157
83
        let val = match &v.init {
158
83
            Some(expr) => eval_value(&mut local, expr)?,
159
            None => Expr::Nil,
160
        };
161
83
        if val.is_wasm_runtime() {
162
            needs_runtime = true;
163
83
        }
164
83
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
165
    }
166

            
167
83
    if !needs_runtime {
168
83
        let test_result = eval_value(&mut local, &end_test)?;
169
83
        needs_runtime = test_result.is_wasm_runtime();
170
    }
171

            
172
83
    if needs_runtime {
173
        let dl = DoLoop {
174
            vars: &vars,
175
            end_test: &end_test,
176
            result_forms: &result_forms,
177
            body,
178
            sequential: true,
179
        };
180
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
181
83
    }
182

            
183
83
    let stepped: Vec<(String, Option<Expr>)> = vars
184
83
        .iter()
185
83
        .map(|v| (v.name.clone(), v.step.clone()))
186
83
        .collect();
187

            
188
83
    if !static_loop_terminates(&local, &end_test, &stepped, true) {
189
        let dl = DoLoop {
190
            vars: &vars,
191
            end_test: &end_test,
192
            result_forms: &result_forms,
193
            body,
194
            sequential: true,
195
        };
196
        return compile_do_runtime_for_effect(ctx, emit, symbols, &dl);
197
83
    }
198

            
199
    loop {
200
332
        let test = eval_value(&mut local, &end_test)?;
201
332
        if !test.is_nil() {
202
83
            for expr in &result_forms {
203
83
                compile_for_effect(ctx, emit, &mut local, expr)?;
204
            }
205
83
            return Ok(());
206
249
        }
207
249
        for expr in body {
208
            compile_for_effect(ctx, emit, &mut local, expr)?;
209
        }
210
249
        for (name, step) in &stepped {
211
249
            if let Some(s) = step {
212
249
                let val = eval_value(&mut local, s)?;
213
249
                local
214
249
                    .lookup_mut(name)
215
249
                    .expect("DO* variable must exist")
216
249
                    .set_value(val);
217
            }
218
        }
219
    }
220
83
}
221

            
222
334
pub(super) fn do_star_form(symbols: &mut SymbolTable, args: &[Expr]) -> Result<Expr> {
223
334
    if args.len() < 2 {
224
83
        return Err(Error::Compile(
225
83
            "DO* requires a variable list and an end clause".to_string(),
226
83
        ));
227
251
    }
228
251
    let vars = parse_do_vars("DO*", &args[0])?;
229
251
    let (end_test, result_forms) = parse_end_clause("DO*", &args[1])?;
230
251
    let end_test = end_test.clone();
231
251
    let result_forms: Vec<Expr> = result_forms.to_vec();
232
251
    let body = &args[2..];
233

            
234
251
    let mut local = symbols.clone();
235
251
    let mut needs_runtime = false;
236
251
    let mut stepped: Vec<(String, Option<Expr>)> = Vec::new();
237
334
    for v in vars {
238
334
        let val = match v.init {
239
334
            Some(expr) => eval_value(&mut local, &expr)?,
240
            None => Expr::Nil,
241
        };
242
334
        if val.is_wasm_runtime() {
243
            needs_runtime = true;
244
334
        }
245
334
        local.define(Symbol::new(&v.name, SymbolKind::Variable).with_value(val));
246
334
        stepped.push((v.name, v.step));
247
    }
248

            
249
251
    if !needs_runtime {
250
251
        let test_result = eval_value(&mut local, &end_test)?;
251
251
        if test_result.is_wasm_runtime() {
252
2
            needs_runtime = true;
253
251
        }
254
    }
255

            
256
    // Match `do_form`: derive the result's PairElement from the
257
    // accumulator's body-setf `(setf <result> (cons V <result>))` so
258
    // the static type matches the PairRef shape the codegen path emits.
259
    // Each DO* var is resolved as its RUNTIME stack type, not its const
260
    // init — an integer init (`(i 0 …)`) makes `i` a runtime Index (I32),
261
    // so `(cons i …)` is an I32 cell; resolving `i` to the const `0` would
262
    // type it as Ratio (the numeric-literal cell slot) and a consumer
263
    // dolist would downcast to the wrong element → a runtime cast trap.
264
251
    let mut infer_env = symbols.clone();
265
334
    for (name, step) in &stepped {
266
334
        let init_val = local
267
334
            .lookup(name)
268
334
            .and_then(|s| s.value().cloned())
269
334
            .unwrap_or(Expr::Nil);
270
334
        let ty = infer_wasm_type(&init_val, step.as_ref(), &infer_env);
271
334
        infer_env.define(Symbol::new(name, SymbolKind::Variable).with_value(Expr::WasmRuntime(ty)));
272
    }
273
251
    let result_ty = super::do_form::runtime_result_type(&result_forms, body, &stepped, &infer_env);
274

            
275
251
    if needs_runtime {
276
2
        return Ok(Expr::WasmRuntime(result_ty));
277
249
    }
278

            
279
249
    if !static_loop_terminates(&local, &end_test, &stepped, true) {
280
        return Ok(Expr::WasmRuntime(result_ty));
281
249
    }
282

            
283
    loop {
284
996
        let test = eval_value(&mut local, &end_test)?;
285
996
        if !test.is_nil() {
286
249
            return if result_forms.is_empty() {
287
                Ok(Expr::Nil)
288
            } else {
289
249
                eval_body(&mut local, &result_forms)
290
            };
291
747
        }
292
747
        for expr in body {
293
            eval_value(&mut local, expr)?;
294
        }
295
996
        for (name, step) in &stepped {
296
996
            if let Some(s) = step {
297
996
                let val = eval_value(&mut local, s)?;
298
996
                local
299
996
                    .lookup_mut(name)
300
996
                    .expect("DO* variable must exist")
301
996
                    .set_value(val);
302
            }
303
        }
304
    }
305
334
}