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

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

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

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

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

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

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

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

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

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

            
173
71
    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
71
    }
183

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

            
189
71
    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
71
    }
199

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

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

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

            
250
215
    if !needs_runtime {
251
215
        let test_result = eval_value(&mut local, &end_test)?;
252
215
        if test_result.is_wasm_runtime() {
253
2
            needs_runtime = true;
254
215
        }
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
215
    let mut infer_env = symbols.clone();
266
286
    for (name, step) in &stepped {
267
286
        let init_val = local
268
286
            .lookup(name)
269
286
            .and_then(|s| s.value().cloned())
270
286
            .unwrap_or(Expr::Nil);
271
286
        let ty = infer_wasm_type(&init_val, step.as_ref(), &infer_env);
272
286
        infer_env.define(Symbol::new(name, SymbolKind::Variable).with_value(Expr::WasmRuntime(ty)));
273
    }
274
215
    let result_ty = super::do_form::runtime_result_type(&result_forms, body, &stepped, &infer_env);
275

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

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

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