1
// Skipped under Miri: these tests compile+run wasm via wasmtime, whose
2
// Cranelift backend refuses to run under Miri.
3
#![cfg(not(miri))]
4

            
5
use nms::interpreter::Interpreter;
6
use scripting::nomiscript::{Fraction, Value};
7

            
8
#[test]
9
1
fn test_eval_defun_sum() {
10
1
    let mut interp = Interpreter::new(false).unwrap();
11
1
    let results = interp
12
1
        .eval("(defun sum (a b c) \"Sums A, B, C\" (+ a b c))\n(sum 1 2 3)")
13
1
        .unwrap();
14
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(6))]);
15
1
}
16

            
17
#[test]
18
1
fn test_eval_defun_add() {
19
1
    let mut interp = Interpreter::new(false).unwrap();
20
1
    let results = interp
21
1
        .eval("(defun add (a b) (+ a b))\n(add 10 20)")
22
1
        .unwrap();
23
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(30))]);
24
1
}
25

            
26
#[test]
27
1
fn test_eval_defun_only() {
28
1
    let mut interp = Interpreter::new(false).unwrap();
29
1
    let results = interp.eval("(defun foo (x) (+ x 1))").unwrap();
30
1
    assert_eq!(results, vec![Value::Symbol("FOO".to_string())]);
31
1
}
32

            
33
// --- Recursion depth guards (Gap B residue, #59) ---
34
// Both the eval-time inline walk (`SymbolTable::enter_inline`) and the codegen
35
// inline walk (`CompileContext::push_inlining_frame`) bound nested defun
36
// inlining so an unbounded / non-terminating recursion becomes a structured
37
// compile error instead of a native compiler-stack overflow (SIGABRT).
38

            
39
#[test]
40
1
fn test_recursive_runtime_arg_defun_nested_in_native_does_not_overflow() {
41
    // `(loopx i)` never reduces (passes its arg straight through), so the
42
    // eval-time inline walk must recognize the runtime arg and bail to a
43
    // runtime placeholder rather than recurse the compiler stack forever.
44
    // Under ADR-0028 the surrounding `(+ 1 idx)` is now valid Index arithmetic
45
    // (`transaction-tag-count` is a count; the old "requires ratio values, not
46
    // indices" rejection is gone), so the form compiles to finite self-
47
    // recursive wasm and the non-termination surfaces as a RUNTIME trap — not a
48
    // compiler SIGABRT. The invariant: we reach this assertion with an `Err` at
49
    // all, proving the compiler terminated.
50
1
    let mut interp = Interpreter::new(false).unwrap();
51
1
    let err = interp
52
1
        .eval("(defun loopx (x) (loopx x)) (+ 1 (loopx (transaction-tag-count 0)))")
53
1
        .expect_err("non-terminating recursive runtime-arg call must error, not overflow");
54
1
    assert!(!err.to_string().is_empty());
55
1
}
56

            
57
#[test]
58
1
fn test_recursive_runtime_arg_defun_in_effect_position_does_not_overflow() {
59
    // Same non-terminating recursion but in EFFECT position (a non-tail
60
    // statement whose value is discarded). The effect-position codegen has its
61
    // own defun-inline path, which must also route to the monomorph path /
62
    // depth-guard rather than recurse the compiler stack. It compiles, so the
63
    // infinite recursion surfaces at RUNTIME (a wasm trap), not as a compiler
64
    // SIGABRT.
65
1
    let mut interp = Interpreter::new(false).unwrap();
66
1
    let err = interp
67
1
        .eval("(defun loopx (x) (loopx x)) (loopx (transaction-tag-count 0)) 1")
68
1
        .expect_err("non-terminating recursion in effect position must not overflow the compiler");
69
    // A clean structured/runtime error string, not a process abort.
70
1
    assert!(!err.to_string().is_empty());
71
1
}
72

            
73
#[test]
74
1
fn test_deep_const_recursion_errors_cleanly_not_overflow() {
75
    // Deep const-folded recursion drives the CODEGEN inline walk
76
    // (`compile_lambda_call` → `push_inlining_frame`). Past the depth ceiling
77
    // it must be a structured error, not a native stack overflow.
78
1
    let mut interp = Interpreter::new(false).unwrap();
79
1
    let err = interp
80
1
        .eval("(defun cnt (n) (if (<= n 0) 0 (cnt (- n 1)))) (cnt 200)")
81
1
        .expect_err("over-deep const recursion must error, not overflow");
82
1
    assert!(
83
1
        err.to_string().contains("inlining exceeded depth"),
84
        "expected inlining-depth error, got: {err}"
85
    );
86
1
}
87

            
88
#[test]
89
1
fn test_recursive_runtime_arg_defun_nested_routes_to_monomorph() {
90
    // The positive side of #59: a LEGITIMATE recursive defun over a runtime
91
    // ratio arg, nested as a native argument, must route to the codegen
92
    // monomorph runtime-call path (not overflow, not error). `dn` counts a
93
    // runtime ratio down to 0; `(+ 1 (dn …))` compiles and the recursion runs
94
    // at runtime. The eval-time placeholder for the re-entrant call is what
95
    // hands the real lowering to the monomorph emit.
96
1
    let mut interp = Interpreter::new(false).unwrap();
97
1
    assert_eq!(
98
1
        interp
99
1
            .eval(
100
1
                "(defun dn (n) (if (<= n (/ 1 2)) (/ 0 1) (dn (- n 1)))) \
101
1
                 (+ (/ 1 1) (dn (transaction-post-date 0)))"
102
            )
103
1
            .unwrap(),
104
1
        vec![Value::Number(Fraction::from_integer(1))]
105
    );
106
1
}
107

            
108
#[test]
109
1
fn test_shallow_const_recursion_still_folds() {
110
    // Recursion well within the depth ceiling must keep const-folding.
111
1
    let mut interp = Interpreter::new(false).unwrap();
112
1
    assert_eq!(
113
1
        interp
114
1
            .eval("(defun fact (n) (if (<= n 1) 1 (* n (fact (- n 1))))) (fact 10)")
115
1
            .unwrap(),
116
1
        vec![Value::Number(Fraction::from_integer(3_628_800))]
117
    );
118
1
    assert_eq!(
119
1
        interp
120
1
            .eval("(defun cnt (n) (if (<= n 0) 0 (cnt (- n 1)))) (cnt 20)")
121
1
            .unwrap(),
122
1
        vec![Value::Number(Fraction::from_integer(0))]
123
    );
124
1
}
125

            
126
#[test]
127
1
fn test_eval_defun_arity_error() {
128
1
    let mut interp = Interpreter::new(false).unwrap();
129
1
    let result = interp.eval("(defun add (a b) (+ a b))\n(add 1)");
130
1
    assert!(result.is_err());
131
1
}
132

            
133
#[test]
134
1
fn test_eval_defun_persistence() {
135
1
    let mut interp = Interpreter::new(false).unwrap();
136
1
    interp.eval("(defun add (a b) (+ a b))").unwrap();
137
1
    let results = interp.eval("(add 3 4)").unwrap();
138
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(7))]);
139
1
}
140

            
141
#[test]
142
1
fn test_eval_funcall_native() {
143
1
    let mut interp = Interpreter::new(false).unwrap();
144
1
    let results = interp.eval("(funcall + 1 2 3)").unwrap();
145
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(6))]);
146
1
}
147

            
148
#[test]
149
1
fn test_eval_funcall_user() {
150
1
    let mut interp = Interpreter::new(false).unwrap();
151
1
    let results = interp
152
1
        .eval("(defun add (a b) (+ a b))\n(funcall add 10 20)")
153
1
        .unwrap();
154
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(30))]);
155
1
}
156

            
157
#[test]
158
1
fn test_eval_apply_quoted_list() {
159
1
    let mut interp = Interpreter::new(false).unwrap();
160
1
    let results = interp.eval("(apply + '(1 2 3))").unwrap();
161
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(6))]);
162
1
}
163

            
164
#[test]
165
1
fn test_eval_apply_mixed_args() {
166
1
    let mut interp = Interpreter::new(false).unwrap();
167
1
    let results = interp.eval("(apply + 1 2 '(3 4))").unwrap();
168
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(10))]);
169
1
}
170

            
171
#[test]
172
1
fn test_eval_compile() {
173
1
    let mut interp = Interpreter::new(false).unwrap();
174
1
    let results = interp
175
1
        .eval("(defun add (a b) (+ a b))\n(compile 'add)")
176
1
        .unwrap();
177
1
    assert_eq!(results, vec![Value::Symbol("ADD".to_string())]);
178
1
}
179

            
180
#[test]
181
1
fn test_eval_eval() {
182
1
    let mut interp = Interpreter::new(false).unwrap();
183
1
    let results = interp.eval("(eval '(+ 1 2))").unwrap();
184
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(3))]);
185
1
}
186

            
187
#[test]
188
1
fn test_eval_eval_defun() {
189
1
    let mut interp = Interpreter::new(false).unwrap();
190
1
    let results = interp.eval("(eval '(defun x (a) (+ 1 a)))\n(x 5)").unwrap();
191
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(6))]);
192
1
}
193

            
194
#[test]
195
1
fn test_eval_eval_self_evaluating() {
196
1
    let mut interp = Interpreter::new(false).unwrap();
197
1
    assert_eq!(
198
1
        interp.eval("(eval 42)").unwrap(),
199
1
        vec![Value::Number(Fraction::from_integer(42))]
200
    );
201
1
    assert_eq!(
202
1
        interp.eval("(eval \"hello\")").unwrap(),
203
1
        vec![Value::String("hello".to_string())]
204
    );
205
1
    assert_eq!(interp.eval("(eval nil)").unwrap(), vec![Value::Nil]);
206
1
}
207

            
208
#[test]
209
1
fn test_eval_eval_symbol() {
210
1
    let mut interp = Interpreter::new(false).unwrap();
211
1
    let results = interp.eval("(eval 'revision)").unwrap();
212
1
    match &results[0] {
213
7
        Value::String(s) => assert!(s.chars().all(|c| c.is_ascii_hexdigit())),
214
        _ => panic!("expected string, got {:?}", results[0]),
215
    }
216
1
}
217

            
218
#[test]
219
1
fn test_eval_lambda_literal() {
220
1
    let mut interp = Interpreter::new(false).unwrap();
221
1
    let results = interp.eval("(lambda (x) (* x 2))").unwrap();
222
1
    assert_eq!(results, vec![Value::String("<closure>".to_string())]);
223
1
}
224

            
225
#[test]
226
1
fn test_eval_lambda_captures_renders_as_closure() {
227
1
    let mut interp = Interpreter::new(false).unwrap();
228
1
    let results = interp.eval("(let* ((y 3)) (lambda (x) (+ x y)))").unwrap();
229
1
    assert_eq!(results, vec![Value::String("<closure>".to_string())]);
230
1
}
231

            
232
#[test]
233
1
fn test_eval_defun_then_call() {
234
1
    let mut interp = Interpreter::new(false).unwrap();
235
1
    let results = interp
236
1
        .eval("(defun double (x) (* x 2)) (double 5)")
237
1
        .unwrap();
238
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(10))]);
239
1
}
240

            
241
#[test]
242
1
fn test_eval_eval_defun_dynamic() {
243
1
    let mut interp = Interpreter::new(false).unwrap();
244
1
    let results = interp
245
1
        .eval("(eval '(defun sum (a) (+ a 1))) (sum 5)")
246
1
        .unwrap();
247
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(6))]);
248
1
}
249

            
250
#[test]
251
1
fn test_eval_funcall_lambda() {
252
1
    let mut interp = Interpreter::new(false).unwrap();
253
1
    let results = interp.eval("(funcall (lambda (x y) (+ x y)) 3 4)").unwrap();
254
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(7))]);
255
1
}
256

            
257
#[test]
258
1
fn test_eval_function_form() {
259
1
    let mut interp = Interpreter::new(false).unwrap();
260
1
    let results = interp
261
1
        .eval("(defun f (x) (* x 2)) (funcall (function f) 5)")
262
1
        .unwrap();
263
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(10))]);
264
1
}
265

            
266
#[test]
267
1
fn test_funcall_let_bound_lambda() {
268
1
    let mut interp = Interpreter::new(false).unwrap();
269
1
    let results = interp
270
1
        .eval("(let ((f (lambda (x) (* x 2)))) (funcall f 5))")
271
1
        .unwrap();
272
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(10))]);
273
1
}
274

            
275
#[test]
276
1
fn test_funcall_let_star_bound_lambda() {
277
1
    let mut interp = Interpreter::new(false).unwrap();
278
1
    let results = interp
279
1
        .eval("(let* ((f (lambda (x) (* x 2)))) (funcall f 5))")
280
1
        .unwrap();
281
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(10))]);
282
1
}
283

            
284
#[test]
285
1
fn test_bare_operator_error() {
286
1
    let mut interp = Interpreter::new(false).unwrap();
287
1
    let err = interp.eval("+").unwrap_err();
288
1
    assert!(
289
1
        err.to_string().contains("is a function"),
290
        "expected function error, got: {err}"
291
    );
292
1
}
293

            
294
#[test]
295
1
fn test_bare_special_form_error() {
296
1
    let mut interp = Interpreter::new(false).unwrap();
297
1
    let err = interp.eval("quote").unwrap_err();
298
1
    assert!(
299
1
        err.to_string().contains("is a special form"),
300
        "expected special form error, got: {err}"
301
    );
302
1
}
303

            
304
#[test]
305
1
fn test_bare_native_function_error() {
306
1
    let mut interp = Interpreter::new(false).unwrap();
307
1
    let err = interp.eval("debug").unwrap_err();
308
1
    assert!(
309
1
        err.to_string().contains("is a function"),
310
        "expected function error, got: {err}"
311
    );
312
1
}
313

            
314
#[test]
315
1
fn test_labels_simple() {
316
1
    let mut interp = Interpreter::new(false).unwrap();
317
1
    let results = interp
318
1
        .eval("(labels ((double (x) (* x 2))) (double 5))")
319
1
        .unwrap();
320
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(10))]);
321
1
}
322

            
323
#[test]
324
1
fn test_labels_factorial() {
325
1
    let mut interp = Interpreter::new(false).unwrap();
326
1
    let results = interp
327
1
        .eval(
328
1
            "(defun fact (n)
329
1
               (labels ((go (i acc)
330
1
                          (if (<= i 1) acc (go (- i 1) (* acc i)))))
331
1
                 (go n 1)))
332
1
             (fact 5)",
333
        )
334
1
        .unwrap();
335
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(120))]);
336
1
}
337

            
338
#[test]
339
1
fn test_labels_mutual_recursion() {
340
1
    let mut interp = Interpreter::new(false).unwrap();
341
1
    let results = interp
342
1
        .eval(
343
1
            "(labels ((is-even (n) (if (= n 0) t (is-odd (- n 1))))
344
1
                      (is-odd (n) (if (= n 0) nil (is-even (- n 1)))))
345
1
               (is-even 4))",
346
        )
347
1
        .unwrap();
348
1
    assert_eq!(results, vec![Value::Bool(true)]);
349
1
}
350

            
351
#[test]
352
1
fn test_labels_no_body_error() {
353
1
    let mut interp = Interpreter::new(false).unwrap();
354
1
    let err = interp.eval("(labels ((f (x) x)))").unwrap_err();
355
1
    assert!(
356
1
        err.to_string().contains("LABELS requires"),
357
        "expected labels error, got: {err}"
358
    );
359
1
}
360

            
361
#[test]
362
1
fn test_labels_scope() {
363
1
    let mut interp = Interpreter::new(false).unwrap();
364
1
    interp
365
1
        .eval("(labels ((local-fn (x) (+ x 1))) (local-fn 5))")
366
1
        .unwrap();
367
1
    let err = interp.eval("(local-fn 5)").unwrap_err();
368
1
    assert!(
369
1
        err.to_string().contains("undefined")
370
1
            || err.to_string().contains("Undefined")
371
            || err.to_string().contains("not defined"),
372
        "expected undefined error, got: {err}"
373
    );
374
1
}
375

            
376
#[test]
377
1
fn test_1_plus() {
378
1
    let mut interp = Interpreter::new(false).unwrap();
379
1
    let results = interp.eval("(1+ 5)").unwrap();
380
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(6))]);
381
1
}
382

            
383
#[test]
384
1
fn test_1_minus() {
385
1
    let mut interp = Interpreter::new(false).unwrap();
386
1
    let results = interp.eval("(1- 5)").unwrap();
387
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(4))]);
388
1
}