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_defmacro_when() {
10
1
    let mut interp = Interpreter::new(false).unwrap();
11
1
    let results = interp
12
1
        .eval("(defmacro when (test body) (list 'if test body))\n(when (> 3 2) \"yes\")")
13
1
        .unwrap();
14
1
    assert_eq!(results, vec![Value::String("yes".to_string())]);
15
1
}
16

            
17
#[test]
18
1
fn test_defmacro_when_false() {
19
1
    let mut interp = Interpreter::new(false).unwrap();
20
1
    let results = interp
21
1
        .eval("(defmacro when (test body) (list 'if test body))\n(when (> 2 3) \"yes\")")
22
1
        .unwrap();
23
1
    assert_eq!(results, vec![Value::Nil]);
24
1
}
25

            
26
#[test]
27
1
fn test_defmacro_unless() {
28
1
    let mut interp = Interpreter::new(false).unwrap();
29
1
    let results = interp
30
1
        .eval("(defmacro unless (test body) (list 'if test nil body))\n(unless #f \"yes\")")
31
1
        .unwrap();
32
1
    assert_eq!(results, vec![Value::String("yes".to_string())]);
33
1
}
34

            
35
#[test]
36
1
fn test_defmacro_constant() {
37
1
    let mut interp = Interpreter::new(false).unwrap();
38
1
    let results = interp
39
1
        .eval("(defmacro always-42 () 42)\n(always-42)")
40
1
        .unwrap();
41
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(42))]);
42
1
}
43

            
44
#[test]
45
1
fn test_defmacro_returns_name() {
46
1
    let mut interp = Interpreter::new(false).unwrap();
47
1
    let results = interp.eval("(defmacro my-m (x) x)").unwrap();
48
1
    assert_eq!(results, vec![Value::Symbol("MY-M".to_string())]);
49
1
}
50

            
51
#[test]
52
1
fn test_defmacro_arity_error() {
53
1
    let mut interp = Interpreter::new(false).unwrap();
54
1
    let result = interp.eval("(defmacro when (test body) (list 'if test body))\n(when (> 3 2))");
55
1
    assert!(result.is_err());
56
1
}
57

            
58
#[test]
59
1
fn test_defmacro_persistence() {
60
1
    let mut interp = Interpreter::new(false).unwrap();
61
1
    interp
62
1
        .eval("(defmacro when (test body) (list 'if test body))")
63
1
        .unwrap();
64
1
    let results = interp.eval("(when #t 42)").unwrap();
65
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(42))]);
66
1
}
67

            
68
#[test]
69
1
fn test_defmacro_swap_args() {
70
1
    let mut interp = Interpreter::new(false).unwrap();
71
1
    let results = interp
72
1
        .eval("(defmacro swap-sub (a b) (list '- b a))\n(swap-sub 1 10)")
73
1
        .unwrap();
74
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(9))]);
75
1
}
76

            
77
#[test]
78
1
fn test_defmacro_nested_expansion() {
79
1
    let mut interp = Interpreter::new(false).unwrap();
80
1
    let results = interp
81
1
        .eval("(defmacro when (test body) (list 'if test body))\n(when (when #t #t) 42)")
82
1
        .unwrap();
83
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(42))]);
84
1
}
85

            
86
#[test]
87
1
fn test_defmacro_chained() {
88
1
    let mut interp = Interpreter::new(false).unwrap();
89
1
    let results = interp
90
1
        .eval("(defmacro when (test body) (list 'if test body))\n(defmacro my-when (test body) (list 'when test body))\n(my-when (> 3 2) \"yes\")")
91
1
        .unwrap();
92
1
    assert_eq!(results, vec![Value::String("yes".to_string())]);
93
1
}
94

            
95
#[test]
96
1
fn test_defmacro_bare_value_error() {
97
1
    let mut interp = Interpreter::new(false).unwrap();
98
1
    interp
99
1
        .eval("(defmacro when (test body) (list 'if test body))")
100
1
        .unwrap();
101
1
    let err = interp.eval("when").unwrap_err();
102
1
    assert!(
103
1
        err.to_string().contains("macro"),
104
        "expected macro error, got: {err}"
105
    );
106
1
}
107

            
108
// MACROEXPAND-1
109

            
110
#[test]
111
1
fn test_macroexpand_1_macro() {
112
1
    let mut interp = Interpreter::new(false).unwrap();
113
1
    let results = interp
114
1
        .eval("(defmacro when (test body) (list 'if test body))\n(macroexpand-1 '(when #t 42))")
115
1
        .unwrap();
116
1
    assert_eq!(results, vec![Value::String("('IF #T 42)".to_string())]);
117
1
}
118

            
119
#[test]
120
1
fn test_macroexpand_1_non_macro() {
121
1
    let mut interp = Interpreter::new(false).unwrap();
122
1
    let results = interp.eval("(macroexpand-1 '(+ 1 2))").unwrap();
123
1
    assert_eq!(results, vec![Value::String("(+ 1 2)".to_string())]);
124
1
}
125

            
126
// MACROEXPAND
127

            
128
#[test]
129
1
fn test_macroexpand_repeated() {
130
1
    let mut interp = Interpreter::new(false).unwrap();
131
1
    let results = interp
132
1
        .eval("(defmacro when (test body) (list 'if test body))\n(defmacro my-when (test body) (list 'when test body))\n(macroexpand '(my-when #t 42))")
133
1
        .unwrap();
134
1
    assert_eq!(results, vec![Value::String("('IF #T 42)".to_string())]);
135
1
}
136

            
137
// QUASIQUOTE
138

            
139
#[test]
140
1
fn test_quasiquote_when() {
141
1
    let mut interp = Interpreter::new(false).unwrap();
142
1
    let results = interp
143
1
        .eval("(defmacro when (test body) `(if ,test ,body))\n(when (> 3 2) \"yes\")")
144
1
        .unwrap();
145
1
    assert_eq!(results, vec![Value::String("yes".to_string())]);
146
1
}
147

            
148
#[test]
149
1
fn test_quasiquote_unless() {
150
1
    let mut interp = Interpreter::new(false).unwrap();
151
1
    let results = interp
152
1
        .eval("(defmacro unless (test body) `(if ,test nil ,body))\n(unless #f \"yes\")")
153
1
        .unwrap();
154
1
    assert_eq!(results, vec![Value::String("yes".to_string())]);
155
1
}
156

            
157
#[test]
158
1
fn test_quasiquote_unquote_splicing() {
159
1
    let mut interp = Interpreter::new(false).unwrap();
160
1
    let results = interp
161
1
        .eval("(defmacro listify (&rest xs) `(list ,@xs))\n(listify 1 2 3)")
162
1
        .unwrap();
163
1
    assert_eq!(results, vec![Value::String("(1 2 3)".to_string())]);
164
1
}
165

            
166
#[test]
167
1
fn test_quasiquote_literal() {
168
1
    let mut interp = Interpreter::new(false).unwrap();
169
1
    let results = interp.eval("`42").unwrap();
170
1
    assert_eq!(results, vec![Value::Number(Fraction::from_integer(42))]);
171
1
}
172

            
173
#[test]
174
1
fn test_quasiquote_symbol() {
175
1
    let mut interp = Interpreter::new(false).unwrap();
176
1
    let results = interp.eval("`foo").unwrap();
177
1
    assert_eq!(results, vec![Value::Symbol("FOO".to_string())]);
178
1
}
179

            
180
#[test]
181
1
fn test_unquote_outside_error() {
182
1
    let mut interp = Interpreter::new(false).unwrap();
183
1
    assert!(interp.eval("(defvar *x* 1) ,*x*").is_err());
184
1
}
185

            
186
// DESCRIBE with macros
187

            
188
#[test]
189
1
fn test_describe_macro() {
190
1
    let mut interp = Interpreter::new(false).unwrap();
191
1
    let results = interp
192
1
        .eval("(defmacro when (test body) (list 'if test body))\n(describe when)")
193
1
        .unwrap();
194
1
    match &results[0] {
195
1
        Value::String(s) => {
196
1
            assert!(s.contains("WHEN is a macro"), "got: {s}");
197
        }
198
        _ => panic!("expected string, got {:?}", results[0]),
199
    }
200
1
}