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_add() {
10
1
    let mut interp = Interpreter::new(false).unwrap();
11
1
    let results = interp.eval("(+ 1 2 3 4 5 6 7)").unwrap();
12
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(28))]);
13
1
}
14

            
15
#[test]
16
1
fn test_eval_add_identity() {
17
1
    let mut interp = Interpreter::new(false).unwrap();
18
1
    let results = interp.eval("(+)").unwrap();
19
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(0))]);
20
1
}
21

            
22
#[test]
23
1
fn test_eval_add_fractions() {
24
1
    let mut interp = Interpreter::new(false).unwrap();
25
1
    let results = interp.eval("(+ 0.5 0.25)").unwrap();
26
1
    assert_eq!(results, vec![Value::Number(Fraction::new(3, 4))]);
27
1
}
28

            
29
#[test]
30
1
fn test_eval_sub_negation() {
31
1
    let mut interp = Interpreter::new(false).unwrap();
32
1
    assert_eq!(
33
1
        interp.eval("(- 5)").unwrap(),
34
1
        vec![Value::Number(Fraction::from_integer(-5))]
35
    );
36
1
}
37

            
38
#[test]
39
1
fn test_eval_sub_binary() {
40
1
    let mut interp = Interpreter::new(false).unwrap();
41
1
    assert_eq!(
42
1
        interp.eval("(- 10 3)").unwrap(),
43
1
        vec![Value::Number(Fraction::from_integer(7))]
44
    );
45
1
}
46

            
47
#[test]
48
1
fn test_eval_sub_variadic() {
49
1
    let mut interp = Interpreter::new(false).unwrap();
50
1
    assert_eq!(
51
1
        interp.eval("(- 100 10 20 30)").unwrap(),
52
1
        vec![Value::Number(Fraction::from_integer(40))]
53
    );
54
1
}
55

            
56
#[test]
57
1
fn test_eval_sub_no_args_error() {
58
1
    let mut interp = Interpreter::new(false).unwrap();
59
1
    assert!(interp.eval("(-)").is_err());
60
1
}
61

            
62
#[test]
63
1
fn test_eval_mul() {
64
1
    let mut interp = Interpreter::new(false).unwrap();
65
1
    assert_eq!(
66
1
        interp.eval("(* 2 3 4)").unwrap(),
67
1
        vec![Value::Number(Fraction::from_integer(24))]
68
    );
69
1
}
70

            
71
#[test]
72
1
fn test_eval_mul_identity() {
73
1
    let mut interp = Interpreter::new(false).unwrap();
74
1
    assert_eq!(
75
1
        interp.eval("(*)").unwrap(),
76
1
        vec![Value::Number(Fraction::from_integer(1))]
77
    );
78
1
}
79

            
80
#[test]
81
1
fn test_eval_mul_fractions() {
82
1
    let mut interp = Interpreter::new(false).unwrap();
83
1
    assert_eq!(
84
1
        interp.eval("(* 0.5 0.5)").unwrap(),
85
1
        vec![Value::Number(Fraction::new(1, 4))]
86
    );
87
1
}
88

            
89
#[test]
90
1
fn test_eval_div_reciprocal() {
91
    // ADR-0028: an all-integer (Index) reciprocal divides as an index — `1/4`
92
    // truncates toward zero to `0`. A fractional reciprocal needs a Scalar
93
    // operand (e.g. `(/ 4.0)`).
94
1
    let mut interp = Interpreter::new(false).unwrap();
95
1
    assert_eq!(
96
1
        interp.eval("(/ 4)").unwrap(),
97
1
        vec![Value::Number(Fraction::from_integer(0))]
98
    );
99
1
}
100

            
101
#[test]
102
1
fn test_eval_div_reciprocal_scalar() {
103
    // The Scalar reciprocal: a fractional operand (denom ≠ 1) keeps rational
104
    // division — `(/ 4/3)` → `3/4`.
105
1
    let mut interp = Interpreter::new(false).unwrap();
106
1
    assert_eq!(
107
1
        interp.eval("(/ 4/3)").unwrap(),
108
1
        vec![Value::Number(Fraction::new(3, 4))]
109
    );
110
1
}
111

            
112
#[test]
113
1
fn test_eval_div_binary() {
114
    // ADR-0028: all-integer (Index) division truncates toward zero (`10/3 → 3`),
115
    // matching `i32.div_s`. A `10/3` Scalar needs a fractional literal.
116
1
    let mut interp = Interpreter::new(false).unwrap();
117
1
    assert_eq!(
118
1
        interp.eval("(/ 10 3)").unwrap(),
119
1
        vec![Value::Number(Fraction::from_integer(3))]
120
    );
121
1
}
122

            
123
#[test]
124
1
fn test_eval_div_variadic() {
125
1
    let mut interp = Interpreter::new(false).unwrap();
126
1
    assert_eq!(
127
1
        interp.eval("(/ 120 2 3 4)").unwrap(),
128
1
        vec![Value::Number(Fraction::from_integer(5))]
129
    );
130
1
}
131

            
132
#[test]
133
1
fn test_eval_div_by_zero_error() {
134
1
    let mut interp = Interpreter::new(false).unwrap();
135
1
    assert!(interp.eval("(/ 1 0)").is_err());
136
1
}
137

            
138
#[test]
139
1
fn test_eval_div_no_args_error() {
140
1
    let mut interp = Interpreter::new(false).unwrap();
141
1
    assert!(interp.eval("(/)").is_err());
142
1
}
143

            
144
#[test]
145
1
fn test_eval_div_reciprocal_zero_error() {
146
1
    let mut interp = Interpreter::new(false).unwrap();
147
1
    assert!(interp.eval("(/ 0)").is_err());
148
1
}
149

            
150
#[test]
151
1
fn test_eval_mod() {
152
1
    let mut interp = Interpreter::new(false).unwrap();
153
1
    assert_eq!(
154
1
        interp.eval("(mod 10 3)").unwrap(),
155
1
        vec![Value::Number(Fraction::from_integer(1))]
156
    );
157
1
}
158

            
159
#[test]
160
1
fn test_eval_mod_negative() {
161
    // ADR-0028: integer MOD lowers to `i32.rem_s` (remainder takes the sign of
162
    // the dividend), so `(mod -1 5) → -1`, not the Euclidean `4`. The negative
163
    // result decodes as a signed `Number` (the `i64.extend_i32_s` fix), not its
164
    // unsigned 2^32 image.
165
1
    let mut interp = Interpreter::new(false).unwrap();
166
1
    assert_eq!(
167
1
        interp.eval("(mod -1 5)").unwrap(),
168
1
        vec![Value::Number(Fraction::from_integer(-1))]
169
    );
170
1
}
171

            
172
#[test]
173
1
fn test_eval_mod_arity_error() {
174
1
    let mut interp = Interpreter::new(false).unwrap();
175
1
    assert!(interp.eval("(mod 10)").is_err());
176
1
    assert!(interp.eval("(mod 10 3 2)").is_err());
177
1
}