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::super::control::is_truthy;
17
use super::common::{
18
    DoLoop, infer_wasm_type, parse_do_vars, parse_end_clause, static_loop_terminates,
19
};
20
use super::runtime::{
21
    compile_do_runtime, compile_do_runtime_for_effect, compile_do_runtime_for_stack,
22
};
23

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

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

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

            
81
544
    if !needs_runtime {
82
408
        let test_result = eval_value(&mut local, &end_test)?;
83
408
        needs_runtime = test_result.is_wasm_runtime();
84
136
    }
85

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
276
206
    if needs_runtime {
277
2
        return Ok(Expr::WasmRuntime(result_ty));
278
204
    }
279

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

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