uni_core/
prelude.rs

1// RUST CONCEPT: Uni Prelude Module
2// This module contains Uni's prelude definitions - the standard words loaded at startup
3// Following the Forth tradition, we define higher-level operations in terms of primitives
4
5use crate::evaluator::execute_string;
6use crate::interpreter::Interpreter;
7use crate::value::RuntimeError;
8
9// RUST CONCEPT: Prelude initialization
10// This function loads all prelude definitions into the interpreter
11pub fn load_prelude(interp: &mut Interpreter) -> Result<(), RuntimeError> {
12    // RUST CONCEPT: Define prelude words using actual Uni code
13    // This is much more natural than building def commands from string pairs
14    // Each line is real Uni code that defines a word
15    // RUST CONCEPT: Multi-line string for multiple definitions
16    // Each definition is actual Uni code that uses def naturally
17    let prelude_code = r#"
18        \\ Stack manipulation words
19        'swap [1 roll] def
20        "( a b -- b a ) Swap top two stack items" doc
21
22        'dup [0 pick] def
23        "( a -- a a ) Duplicate top stack item" doc
24
25        'over [1 pick] def
26        "( a b -- a b a ) Copy second stack item to top" doc
27
28        'rot [2 roll] def
29        "( a b c -- b c a ) Rotate third item to top" doc
30
31        'nip [swap drop] def
32        "( a b -- b ) Remove second stack item" doc
33
34        'tuck [swap over] def
35        "( a b -- b a b ) Copy top below second item" doc
36
37        'nil? [[] =] def
38        "( x -- bool ) Test if value is empty list" doc
39
40        \\ List processing primitives
41        'length [
42            dup nil?
43            [drop 0]
44            [tail length 1 +]
45            if
46        ] def
47        "( list -- n ) Calculate list length recursively" doc
48
49        'null? [null =] def
50        "( x -- bool ) Test if value is null" doc
51
52        'record? [type "record" =] def
53        "( x -- bool ) Test if value is any record type" doc
54
55        \\ Conditional duplication from Forth
56        '?dup [
57            dup truthy? [dup] [] if
58        ] def
59        "( x -- x x | x ) Duplicate if truthy, otherwise leave unchanged" doc
60
61        \\ Short-circuiting logical operations
62        'and [
63            swap                          \\ Move first quotation to top
64            exec                          \\ Execute first quotation
65            dup                           \\ Always duplicate the result
66            [
67                drop                      \\ Drop the duplicate, keep original
68                exec                      \\ Execute second quotation
69            ]
70            [
71                swap drop                 \\ If falsy, drop second quotation, keep falsy result
72            ]
73            if
74        ] def
75        "( [cond1] [cond2] -- result ) Short-circuit AND: executes cond2 only if cond1 is truthy" doc
76
77        'or [
78            swap                          \\ Move first quotation to top
79            exec                          \\ Execute first quotation
80            dup                           \\ Always duplicate the result
81            [
82                swap drop                 \\ If truthy, drop second quotation, keep result
83            ]
84            [
85                drop                      \\ Drop the duplicate
86                exec                      \\ If falsy, execute second quotation
87            ]
88            if
89        ] def
90        "( [cond1] [cond2] -- result ) Short-circuit OR: executes cond2 only if cond1 is falsy" doc
91
92        \\ Control flow primitives
93        'while [
94            >r >r                         \\ move body and condition to return stack
95            r@ exec                       \\ execute condition (copy from R-stack)
96            [
97                r> r> dup rot swap >r >r  \\ get body and move body and condition back to return stack
98                exec                      \\ execute body
99                r> r> while               \\ recursive call
100            ]
101            [ r> r> drop drop ]
102            if
103        ] def
104        "( [condition] [body] -- ) Loop: executes body while condition returns truthy" doc
105    "#;
106
107    // RUST CONCEPT: Execute the prelude code directly
108    // This uses the normal execution path - no special handling needed
109    execute_string(prelude_code, interp)?;
110
111    // RUST CONCEPT: Conditional compilation for feature-specific prelude
112    // Complex number constants (only when complex_numbers feature is enabled)
113    #[cfg(feature = "complex_numbers")]
114    {
115        let complex_prelude = r#"
116            \\ Mathematical constants (complex numbers)
117            'i 0+1i def
118            "Imaginary unit constant (0+1i)" doc
119        "#;
120        execute_string(complex_prelude, interp)?;
121    }
122
123    // RUST CONCEPT: Conditional compilation for platform-specific prelude
124    // Hardware convenience wrappers for micro:bit
125    #[cfg(target_os = "none")]
126    {
127        let hardware_prelude = r#"
128            \\ Hardware convenience wrappers (micro:bit only)
129            'button-a? [0 button-read] def
130            "( -- bool ) Read button A state (true = pressed)" doc
131
132            'button-b? [1 button-read] def
133            "( -- bool ) Read button B state (true = pressed)" doc
134        "#;
135        execute_string(hardware_prelude, interp)?;
136    }
137
138    Ok(())
139}
140
141// RUST CONCEPT: Testing the prelude
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::value::Value;
146
147    // RUST CONCEPT: Test helper function
148    fn setup_interpreter_with_prelude() -> Interpreter {
149        // RUST CONCEPT: Manual prelude loading for testing
150        // Interpreter::new() now automatically loads builtins and prelude
151        let mut interp = Interpreter::new();
152        load_prelude(&mut interp).unwrap();
153        interp
154    }
155
156    #[test]
157    fn test_prelude_dup() {
158        let mut interp = setup_interpreter_with_prelude();
159
160        // Test: 42 dup should give us 42 42
161        execute_string("42 dup", &mut interp).unwrap();
162
163        let top = interp.pop().unwrap();
164        let second = interp.pop().unwrap();
165
166        assert!(matches!(top, Value::Int32(42)));
167        assert!(matches!(second, Value::Int32(42)));
168    }
169
170    #[test]
171    fn test_prelude_swap() {
172        let mut interp = setup_interpreter_with_prelude();
173
174        // Test: 1 2 swap should give us 2 1
175        execute_string("1 2 swap", &mut interp).unwrap();
176
177        let top = interp.pop().unwrap();
178        let second = interp.pop().unwrap();
179
180        assert!(matches!(top, Value::Int32(1)));
181        assert!(matches!(second, Value::Int32(2)));
182    }
183
184    #[test]
185    fn test_prelude_over() {
186        let mut interp = setup_interpreter_with_prelude();
187
188        // Test: 1 2 over should give us 1 2 1
189        execute_string("1 2 over", &mut interp).unwrap();
190
191        let top = interp.pop().unwrap();
192        let second = interp.pop().unwrap();
193        let third = interp.pop().unwrap();
194
195        assert!(matches!(top, Value::Int32(1)));
196        assert!(matches!(second, Value::Int32(2)));
197        assert!(matches!(third, Value::Int32(1)));
198    }
199
200    #[test]
201    fn test_prelude_rot() {
202        let mut interp = setup_interpreter_with_prelude();
203
204        // Test: 1 2 3 rot should give us 2 3 1
205        execute_string("1 2 3 rot", &mut interp).unwrap();
206
207        let top = interp.pop().unwrap();
208        let second = interp.pop().unwrap();
209        let third = interp.pop().unwrap();
210
211        assert!(matches!(top, Value::Int32(1)));
212        assert!(matches!(second, Value::Int32(3)));
213        assert!(matches!(third, Value::Int32(2)));
214    }
215
216    #[test]
217    fn test_prelude_nip() {
218        let mut interp = setup_interpreter_with_prelude();
219
220        // Test: 1 2 3 nip should give us 1 3 (removes second item)
221        execute_string("1 2 3 nip", &mut interp).unwrap();
222
223        let top = interp.pop().unwrap();
224        let second = interp.pop().unwrap();
225
226        assert!(matches!(top, Value::Int32(3)));
227        assert!(matches!(second, Value::Int32(1)));
228
229        // Stack should be empty now
230        assert!(interp.pop().is_err());
231    }
232
233    #[test]
234    fn test_prelude_tuck() {
235        let mut interp = setup_interpreter_with_prelude();
236
237        // Test: 5 6 tuck should give us 6 5 6 (insert copy of top below second)
238        execute_string("5 6 tuck", &mut interp).unwrap();
239
240        let top = interp.pop().unwrap();
241        let second = interp.pop().unwrap();
242        let third = interp.pop().unwrap();
243
244        assert!(matches!(top, Value::Int32(6)));
245        assert!(matches!(second, Value::Int32(5)));
246        assert!(matches!(third, Value::Int32(6)));
247
248        // Stack should be empty now
249        assert!(interp.pop().is_err());
250    }
251
252    #[test]
253    fn test_prelude_length() {
254        let mut interp = setup_interpreter_with_prelude();
255
256        // Test: [1 2 3 4 5] length should give us 5
257        execute_string("[1 2 3 4 5] length", &mut interp).unwrap();
258
259        let result = interp.pop().unwrap();
260        assert!(matches!(result, Value::Int32(5)));
261
262        // Test: [] length should give us 0
263        execute_string("[] length", &mut interp).unwrap();
264
265        let result = interp.pop().unwrap();
266        assert!(matches!(result, Value::Int32(0)));
267    }
268
269    #[test]
270    fn test_prelude_null_predicate() {
271        let mut interp = setup_interpreter_with_prelude();
272
273        // Test: null null? should give us true
274        execute_string("null null?", &mut interp).unwrap();
275
276        let result = interp.pop().unwrap();
277        assert!(matches!(result, Value::Boolean(true)));
278
279        // Test: 42 null? should give us false
280        execute_string("42 null?", &mut interp).unwrap();
281
282        let result = interp.pop().unwrap();
283        assert!(matches!(result, Value::Boolean(false)));
284
285        // Test: [] null? should give us false (empty list is not null)
286        execute_string("[] null?", &mut interp).unwrap();
287
288        let result = interp.pop().unwrap();
289        assert!(matches!(result, Value::Boolean(false)));
290    }
291
292    #[test]
293    fn test_prelude_while_loop_counter() {
294        let mut interp = setup_interpreter_with_prelude();
295
296        // Test: while loop that counts from 1 and leaves 5 on the stack
297        // Start with 1, condition checks if counter < 5, body increments counter
298        // Final result should leave 5 on the stack when condition becomes false
299        let code = r#"
300            1
301            [ dup 5 < ]
302            [ 1 + ]
303            while
304        "#;
305
306        execute_string(code, &mut interp).unwrap();
307
308        // Should have 5 on stack (started at 1, incremented while < 5)
309        let result = interp.pop().unwrap();
310        assert!(matches!(result, Value::Int32(5)));
311
312        // Stack should be empty now
313        assert!(interp.pop().is_err());
314    }
315
316    #[test]
317    fn test_prelude_while_sum_accumulator() {
318        let mut interp = setup_interpreter_with_prelude();
319
320        // Test: while loop that sums numbers 1+2+3+4+5 = 15
321        // Stack: [counter sum]
322        let code = r#"
323            1 0
324            [ over 5 <= ]
325            [ over + swap 1 + swap ]
326            while
327            nip
328        "#;
329
330        execute_string(code, &mut interp).unwrap();
331
332        // Should have 15 on stack (sum of 1+2+3+4+5)
333        let result = interp.pop().unwrap();
334        assert!(matches!(result, Value::Int32(15)));
335
336        // Stack should be empty now
337        assert!(interp.pop().is_err());
338    }
339
340    #[test]
341    fn test_prelude_while_empty_body() {
342        let mut interp = setup_interpreter_with_prelude();
343
344        // Test: while loop with false condition - body should never execute
345        let code = r#"
346            42
347            [ 0 ]
348            [ 99 ]
349            while
350        "#;
351
352        execute_string(code, &mut interp).unwrap();
353
354        // Should still have 42 on stack (body never executed)
355        let result = interp.pop().unwrap();
356        assert!(matches!(result, Value::Int32(42)));
357
358        // Stack should be empty now
359        assert!(interp.pop().is_err());
360    }
361
362    #[test]
363    fn test_prelude_question_dup() {
364        let mut interp = setup_interpreter_with_prelude();
365
366        // Test: truthy value should be duplicated
367        execute_string("42 ?dup", &mut interp).unwrap();
368
369        let top = interp.pop().unwrap();
370        let second = interp.pop().unwrap();
371        assert!(matches!(top, Value::Int32(42)));
372        assert!(matches!(second, Value::Int32(42)));
373
374        // Test: falsy value (0) should not be duplicated
375        execute_string("0 ?dup", &mut interp).unwrap();
376
377        let result = interp.pop().unwrap();
378        assert!(matches!(result, Value::Int32(0)));
379        // Should be empty now - no duplication occurred
380        assert!(interp.pop().is_err());
381
382        // Test: false should not be duplicated
383        execute_string("false ?dup", &mut interp).unwrap();
384
385        let result = interp.pop().unwrap();
386        assert!(matches!(result, Value::Boolean(false)));
387        assert!(interp.pop().is_err());
388    }
389
390    #[test]
391    fn test_prelude_and_short_circuit() {
392        let mut interp = setup_interpreter_with_prelude();
393
394        // Test: true and true -> returns second value
395        execute_string("[5] [10] and", &mut interp).unwrap();
396
397        let result = interp.pop().unwrap();
398        assert!(matches!(result, Value::Int32(10)));
399
400        // Test: false and anything -> returns false, doesn't execute second
401        execute_string("[0] [99] and", &mut interp).unwrap();
402
403        let result = interp.pop().unwrap();
404        assert!(matches!(result, Value::Int32(0)));
405
406        // Test: short-circuiting - second quotation should not execute
407        // This pushes a marker, then does false and [marker-remover]
408        // If short-circuiting works, marker should still be there
409        execute_string("999 [0] [drop] and", &mut interp).unwrap();
410
411        let and_result = interp.pop().unwrap();
412        let marker = interp.pop().unwrap();
413        assert!(matches!(and_result, Value::Int32(0)));
414        assert!(matches!(marker, Value::Int32(999))); // Marker should still be there
415    }
416
417    #[test]
418    fn test_prelude_or_short_circuit() {
419        let mut interp = setup_interpreter_with_prelude();
420
421        // Test: false or true -> returns second value
422        execute_string("[0] [42] or", &mut interp).unwrap();
423
424        let result = interp.pop().unwrap();
425        assert!(matches!(result, Value::Int32(42)));
426
427        // Test: true or anything -> returns first value, doesn't execute second
428        execute_string("[5] [99] or", &mut interp).unwrap();
429
430        let result = interp.pop().unwrap();
431        assert!(matches!(result, Value::Int32(5)));
432
433        // Test: short-circuiting - second quotation should not execute
434        execute_string("888 [7] [drop] or", &mut interp).unwrap();
435
436        let or_result = interp.pop().unwrap();
437        let marker = interp.pop().unwrap();
438        assert!(matches!(or_result, Value::Int32(7)));
439        assert!(matches!(marker, Value::Int32(888))); // Marker should still be there
440    }
441
442    #[test]
443    fn test_prelude_and_or_chaining() {
444        let mut interp = setup_interpreter_with_prelude();
445
446        // Test: chaining and operations - all true
447        execute_string("[1] [2] and [3] and", &mut interp).unwrap();
448
449        let result = interp.pop().unwrap();
450        assert!(matches!(result, Value::Int32(3)));
451
452        // Test: chaining or operations - first true
453        execute_string("[1] [2] or [3] or", &mut interp).unwrap();
454
455        let result = interp.pop().unwrap();
456        assert!(matches!(result, Value::Int32(1)));
457
458        // Test: mixed and/or
459        execute_string("[0] [5] or [10] and", &mut interp).unwrap();
460
461        let result = interp.pop().unwrap();
462        assert!(matches!(result, Value::Int32(10)));
463    }
464
465    #[test]
466    #[cfg(feature = "complex_numbers")]
467    fn test_prelude_imaginary_constant() {
468        use num_bigint::BigInt;
469        let mut interp = setup_interpreter_with_prelude();
470
471        // Test: 'i' should be defined as 0+1i (GaussianInt)
472        execute_string("i", &mut interp).unwrap();
473
474        let result = interp.pop().unwrap();
475        assert!(matches!(result, Value::GaussianInt(ref re, ref im)
476            if re == &BigInt::from(0) && im == &BigInt::from(1)));
477
478        // Test: using 'i' in arithmetic: i + i = 0+2i
479        execute_string("i i +", &mut interp).unwrap();
480
481        let result = interp.pop().unwrap();
482        assert!(matches!(result, Value::GaussianInt(ref re, ref im)
483            if re == &BigInt::from(0) && im == &BigInt::from(2)));
484    }
485}