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        \\ Logical operations
41        'not [[false] [true] if] def
42        "( x -- bool ) Logical negation of truthiness" doc
43
44        \\ Arithmetic operations
45        'negate [-1 *] def
46        "( n -- -n ) Negate a number" doc
47
48        \\ List processing primitives
49        'length [
50            dup nil?
51            [drop 0]
52            [tail length 1 +]
53            if
54        ] def
55        "( list -- n ) Calculate list length recursively" doc
56
57        'list-ref [
58            dup 0 =
59            [drop head]
60            [1 - swap tail swap list-ref]
61            if
62        ] def
63        "( list index -- element ) Get nth element (0-indexed)" doc
64
65        'append [
66            swap dup nil?
67            [drop]
68            [
69                dup head
70                swap tail
71                rot
72                append
73                cons
74            ]
75            if
76        ] def
77        "( list1 list2 -- list3 ) Concatenate two lists" doc
78
79        'null? [null =] def
80        "( x -- bool ) Test if value is null" doc
81
82        'record? [type "record" =] def
83        "( x -- bool ) Test if value is any record type" doc
84
85        \\ Conditional duplication from Forth
86        '?dup [
87            dup truthy? [dup] [] if
88        ] def
89        "( x -- x x | x ) Duplicate if truthy, otherwise leave unchanged" doc
90
91        \\ Variable operations (Forth-style)
92        '1+ [1 +] def
93        "( n -- n+1 ) Increment by 1" doc
94
95        '1- [1 -] def
96        "( n -- n-1 ) Decrement by 1" doc
97
98        '+! [dup @ rot + swap !] def
99        "( n var -- ) Add n to variable" doc
100
101        'on [true swap !] def
102        "( var -- ) Store true to variable" doc
103
104        'off [false swap !] def
105        "( var -- ) Store false to variable" doc
106
107        \\ List iteration
108        'each [
109            >r                      \\ Move fn to return stack: list | fn
110            dup nil?                \\ Check if list is empty: list bool | fn
111            [
112                drop r> drop        \\ Empty list: clean up list and fn
113            ]
114            [
115                dup head            \\ list -> list head | fn
116                r@                  \\ Get fn: list head fn | fn
117                exec                \\ Execute fn: list ... | fn (fn consumes head, may leave results)
118                tail                \\ Get tail: ... tail | fn
119                r> each             \\ Recurse: ... tail fn
120            ]
121            if
122        ] def
123        "( list [fn] -- ) Execute fn on each element of list (fn consumes argument, may leave results)" doc
124
125        \\ Short-circuiting logical operations
126        'and [
127            swap                          \\ Move first quotation to top
128            exec                          \\ Execute first quotation
129            dup                           \\ Always duplicate the result
130            [
131                drop                      \\ Drop the duplicate, keep original
132                exec                      \\ Execute second quotation
133            ]
134            [
135                swap drop                 \\ If falsy, drop second quotation, keep falsy result
136            ]
137            if
138        ] def
139        "( [cond1] [cond2] -- result ) Short-circuit AND: executes cond2 only if cond1 is truthy" doc
140
141        'or [
142            swap                          \\ Move first quotation to top
143            exec                          \\ Execute first quotation
144            dup                           \\ Always duplicate the result
145            [
146                swap drop                 \\ If truthy, drop second quotation, keep result
147            ]
148            [
149                drop                      \\ Drop the duplicate
150                exec                      \\ If falsy, execute second quotation
151            ]
152            if
153        ] def
154        "( [cond1] [cond2] -- result ) Short-circuit OR: executes cond2 only if cond1 is falsy" doc
155
156        \\ Control flow primitives
157        'while [
158            >r >r                         \\ move body and condition to return stack
159            r@ exec                       \\ execute condition (copy from R-stack)
160            [
161                r> r> dup rot swap >r >r  \\ get body and move body and condition back to return stack
162                exec                      \\ execute body
163                r> r> while               \\ recursive call
164            ]
165            [ r> r> drop drop ]
166            if
167        ] def
168        "( [condition] [body] -- ) Loop: executes body while condition returns truthy" doc
169
170        \\ Date/time operations
171        \\ Date record type with calendar components
172        \\ The 'now' primitive (written in Rust) returns instances of this record type
173        ["year" "month" "day" "hour" "minute" "second" "offset"] "date" make-record-type drop
174    "#;
175
176    // RUST CONCEPT: Execute the prelude code directly
177    // This uses the normal execution path - no special handling needed
178    execute_string(prelude_code, interp)?;
179
180    // RUST CONCEPT: Conditional compilation for feature-specific prelude
181    // Complex number constants (only when complex_numbers feature is enabled)
182    #[cfg(feature = "complex_numbers")]
183    {
184        let complex_prelude = r#"
185            \\ Mathematical constants (complex numbers)
186            'i 0+1i def
187            "Imaginary unit constant (0+1i)" doc
188        "#;
189        execute_string(complex_prelude, interp)?;
190    }
191
192    // RUST CONCEPT: Conditional compilation for platform-specific prelude
193    // Hardware convenience wrappers for micro:bit
194    #[cfg(target_os = "none")]
195    {
196        let hardware_prelude = r#"
197            \\ Hardware convenience wrappers (micro:bit only)
198            'button-a? [0 button-read] def
199            "( -- bool ) Read button A state (true = pressed)" doc
200
201            'button-b? [1 button-read] def
202            "( -- bool ) Read button B state (true = pressed)" doc
203        "#;
204        execute_string(hardware_prelude, interp)?;
205    }
206
207    Ok(())
208}
209
210// RUST CONCEPT: Testing the prelude
211#[cfg(test)]
212mod tests {
213    use super::*;
214    use crate::value::Value;
215
216    // RUST CONCEPT: Test helper function
217    fn setup_interpreter_with_prelude() -> Interpreter {
218        // RUST CONCEPT: Manual prelude loading for testing
219        // Interpreter::new() now automatically loads builtins and prelude
220        let mut interp = Interpreter::new();
221        load_prelude(&mut interp).unwrap();
222        interp
223    }
224
225    #[test]
226    fn test_prelude_dup() {
227        let mut interp = setup_interpreter_with_prelude();
228
229        // Test: 42 dup should give us 42 42
230        execute_string("42 dup", &mut interp).unwrap();
231
232        let top = interp.pop().unwrap();
233        let second = interp.pop().unwrap();
234
235        assert!(matches!(top, Value::Int32(42)));
236        assert!(matches!(second, Value::Int32(42)));
237    }
238
239    #[test]
240    fn test_prelude_swap() {
241        let mut interp = setup_interpreter_with_prelude();
242
243        // Test: 1 2 swap should give us 2 1
244        execute_string("1 2 swap", &mut interp).unwrap();
245
246        let top = interp.pop().unwrap();
247        let second = interp.pop().unwrap();
248
249        assert!(matches!(top, Value::Int32(1)));
250        assert!(matches!(second, Value::Int32(2)));
251    }
252
253    #[test]
254    fn test_prelude_over() {
255        let mut interp = setup_interpreter_with_prelude();
256
257        // Test: 1 2 over should give us 1 2 1
258        execute_string("1 2 over", &mut interp).unwrap();
259
260        let top = interp.pop().unwrap();
261        let second = interp.pop().unwrap();
262        let third = interp.pop().unwrap();
263
264        assert!(matches!(top, Value::Int32(1)));
265        assert!(matches!(second, Value::Int32(2)));
266        assert!(matches!(third, Value::Int32(1)));
267    }
268
269    #[test]
270    fn test_prelude_rot() {
271        let mut interp = setup_interpreter_with_prelude();
272
273        // Test: 1 2 3 rot should give us 2 3 1
274        execute_string("1 2 3 rot", &mut interp).unwrap();
275
276        let top = interp.pop().unwrap();
277        let second = interp.pop().unwrap();
278        let third = interp.pop().unwrap();
279
280        assert!(matches!(top, Value::Int32(1)));
281        assert!(matches!(second, Value::Int32(3)));
282        assert!(matches!(third, Value::Int32(2)));
283    }
284
285    #[test]
286    fn test_prelude_nip() {
287        let mut interp = setup_interpreter_with_prelude();
288
289        // Test: 1 2 3 nip should give us 1 3 (removes second item)
290        execute_string("1 2 3 nip", &mut interp).unwrap();
291
292        let top = interp.pop().unwrap();
293        let second = interp.pop().unwrap();
294
295        assert!(matches!(top, Value::Int32(3)));
296        assert!(matches!(second, Value::Int32(1)));
297
298        // Stack should be empty now
299        assert!(interp.pop().is_err());
300    }
301
302    #[test]
303    fn test_prelude_tuck() {
304        let mut interp = setup_interpreter_with_prelude();
305
306        // Test: 5 6 tuck should give us 6 5 6 (insert copy of top below second)
307        execute_string("5 6 tuck", &mut interp).unwrap();
308
309        let top = interp.pop().unwrap();
310        let second = interp.pop().unwrap();
311        let third = interp.pop().unwrap();
312
313        assert!(matches!(top, Value::Int32(6)));
314        assert!(matches!(second, Value::Int32(5)));
315        assert!(matches!(third, Value::Int32(6)));
316
317        // Stack should be empty now
318        assert!(interp.pop().is_err());
319    }
320
321    #[test]
322    fn test_prelude_length() {
323        let mut interp = setup_interpreter_with_prelude();
324
325        // Test: [1 2 3 4 5] length should give us 5
326        execute_string("[1 2 3 4 5] length", &mut interp).unwrap();
327
328        let result = interp.pop().unwrap();
329        assert!(matches!(result, Value::Int32(5)));
330
331        // Test: [] length should give us 0
332        execute_string("[] length", &mut interp).unwrap();
333
334        let result = interp.pop().unwrap();
335        assert!(matches!(result, Value::Int32(0)));
336    }
337
338    #[test]
339    fn test_prelude_null_predicate() {
340        let mut interp = setup_interpreter_with_prelude();
341
342        // Test: null null? should give us true
343        execute_string("null null?", &mut interp).unwrap();
344
345        let result = interp.pop().unwrap();
346        assert!(matches!(result, Value::Boolean(true)));
347
348        // Test: 42 null? should give us false
349        execute_string("42 null?", &mut interp).unwrap();
350
351        let result = interp.pop().unwrap();
352        assert!(matches!(result, Value::Boolean(false)));
353
354        // Test: [] null? should give us false (empty list is not null)
355        execute_string("[] null?", &mut interp).unwrap();
356
357        let result = interp.pop().unwrap();
358        assert!(matches!(result, Value::Boolean(false)));
359    }
360
361    #[test]
362    fn test_prelude_while_loop_counter() {
363        let mut interp = setup_interpreter_with_prelude();
364
365        // Test: while loop that counts from 1 and leaves 5 on the stack
366        // Start with 1, condition checks if counter < 5, body increments counter
367        // Final result should leave 5 on the stack when condition becomes false
368        let code = r#"
369            1
370            [ dup 5 < ]
371            [ 1 + ]
372            while
373        "#;
374
375        execute_string(code, &mut interp).unwrap();
376
377        // Should have 5 on stack (started at 1, incremented while < 5)
378        let result = interp.pop().unwrap();
379        assert!(matches!(result, Value::Int32(5)));
380
381        // Stack should be empty now
382        assert!(interp.pop().is_err());
383    }
384
385    #[test]
386    fn test_prelude_while_sum_accumulator() {
387        let mut interp = setup_interpreter_with_prelude();
388
389        // Test: while loop that sums numbers 1+2+3+4+5 = 15
390        // Stack: [counter sum]
391        let code = r#"
392            1 0
393            [ over 5 <= ]
394            [ over + swap 1 + swap ]
395            while
396            nip
397        "#;
398
399        execute_string(code, &mut interp).unwrap();
400
401        // Should have 15 on stack (sum of 1+2+3+4+5)
402        let result = interp.pop().unwrap();
403        assert!(matches!(result, Value::Int32(15)));
404
405        // Stack should be empty now
406        assert!(interp.pop().is_err());
407    }
408
409    #[test]
410    fn test_prelude_while_empty_body() {
411        let mut interp = setup_interpreter_with_prelude();
412
413        // Test: while loop with false condition - body should never execute
414        let code = r#"
415            42
416            [ 0 ]
417            [ 99 ]
418            while
419        "#;
420
421        execute_string(code, &mut interp).unwrap();
422
423        // Should still have 42 on stack (body never executed)
424        let result = interp.pop().unwrap();
425        assert!(matches!(result, Value::Int32(42)));
426
427        // Stack should be empty now
428        assert!(interp.pop().is_err());
429    }
430
431    #[test]
432    fn test_prelude_question_dup() {
433        let mut interp = setup_interpreter_with_prelude();
434
435        // Test: truthy value should be duplicated
436        execute_string("42 ?dup", &mut interp).unwrap();
437
438        let top = interp.pop().unwrap();
439        let second = interp.pop().unwrap();
440        assert!(matches!(top, Value::Int32(42)));
441        assert!(matches!(second, Value::Int32(42)));
442
443        // Test: falsy value (0) should not be duplicated
444        execute_string("0 ?dup", &mut interp).unwrap();
445
446        let result = interp.pop().unwrap();
447        assert!(matches!(result, Value::Int32(0)));
448        // Should be empty now - no duplication occurred
449        assert!(interp.pop().is_err());
450
451        // Test: false should not be duplicated
452        execute_string("false ?dup", &mut interp).unwrap();
453
454        let result = interp.pop().unwrap();
455        assert!(matches!(result, Value::Boolean(false)));
456        assert!(interp.pop().is_err());
457    }
458
459    #[test]
460    fn test_prelude_and_short_circuit() {
461        let mut interp = setup_interpreter_with_prelude();
462
463        // Test: true and true -> returns second value
464        execute_string("[5] [10] and", &mut interp).unwrap();
465
466        let result = interp.pop().unwrap();
467        assert!(matches!(result, Value::Int32(10)));
468
469        // Test: false and anything -> returns false, doesn't execute second
470        execute_string("[0] [99] and", &mut interp).unwrap();
471
472        let result = interp.pop().unwrap();
473        assert!(matches!(result, Value::Int32(0)));
474
475        // Test: short-circuiting - second quotation should not execute
476        // This pushes a marker, then does false and [marker-remover]
477        // If short-circuiting works, marker should still be there
478        execute_string("999 [0] [drop] and", &mut interp).unwrap();
479
480        let and_result = interp.pop().unwrap();
481        let marker = interp.pop().unwrap();
482        assert!(matches!(and_result, Value::Int32(0)));
483        assert!(matches!(marker, Value::Int32(999))); // Marker should still be there
484    }
485
486    #[test]
487    fn test_prelude_or_short_circuit() {
488        let mut interp = setup_interpreter_with_prelude();
489
490        // Test: false or true -> returns second value
491        execute_string("[0] [42] or", &mut interp).unwrap();
492
493        let result = interp.pop().unwrap();
494        assert!(matches!(result, Value::Int32(42)));
495
496        // Test: true or anything -> returns first value, doesn't execute second
497        execute_string("[5] [99] or", &mut interp).unwrap();
498
499        let result = interp.pop().unwrap();
500        assert!(matches!(result, Value::Int32(5)));
501
502        // Test: short-circuiting - second quotation should not execute
503        execute_string("888 [7] [drop] or", &mut interp).unwrap();
504
505        let or_result = interp.pop().unwrap();
506        let marker = interp.pop().unwrap();
507        assert!(matches!(or_result, Value::Int32(7)));
508        assert!(matches!(marker, Value::Int32(888))); // Marker should still be there
509    }
510
511    #[test]
512    fn test_prelude_and_or_chaining() {
513        let mut interp = setup_interpreter_with_prelude();
514
515        // Test: chaining and operations - all true
516        execute_string("[1] [2] and [3] and", &mut interp).unwrap();
517
518        let result = interp.pop().unwrap();
519        assert!(matches!(result, Value::Int32(3)));
520
521        // Test: chaining or operations - first true
522        execute_string("[1] [2] or [3] or", &mut interp).unwrap();
523
524        let result = interp.pop().unwrap();
525        assert!(matches!(result, Value::Int32(1)));
526
527        // Test: mixed and/or
528        execute_string("[0] [5] or [10] and", &mut interp).unwrap();
529
530        let result = interp.pop().unwrap();
531        assert!(matches!(result, Value::Int32(10)));
532    }
533
534    #[test]
535    #[cfg(feature = "complex_numbers")]
536    fn test_prelude_imaginary_constant() {
537        use num_bigint::BigInt;
538        let mut interp = setup_interpreter_with_prelude();
539
540        // Test: 'i' should be defined as 0+1i (GaussianInt)
541        execute_string("i", &mut interp).unwrap();
542
543        let result = interp.pop().unwrap();
544        assert!(matches!(result, Value::GaussianInt(ref re, ref im)
545            if re == &BigInt::from(0) && im == &BigInt::from(1)));
546
547        // Test: using 'i' in arithmetic: i + i = 0+2i
548        execute_string("i i +", &mut interp).unwrap();
549
550        let result = interp.pop().unwrap();
551        assert!(matches!(result, Value::GaussianInt(ref re, ref im)
552            if re == &BigInt::from(0) && im == &BigInt::from(2)));
553    }
554
555    #[test]
556    fn test_prelude_each_basic() {
557        let mut interp = setup_interpreter_with_prelude();
558
559        // Test: [1 2 3] [drop] each should consume all elements
560        execute_string("[10 20 30] [drop] each", &mut interp).unwrap();
561
562        // Stack should be empty - each consumes the list and function consumes elements
563        assert!(interp.pop().is_err());
564    }
565
566    #[test]
567    fn test_prelude_each_empty_list() {
568        let mut interp = setup_interpreter_with_prelude();
569
570        // Test: empty list should not execute function
571        execute_string("42 [] [drop] each", &mut interp).unwrap();
572
573        // The 42 should still be on stack (function never executed)
574        let result = interp.pop().unwrap();
575        assert!(matches!(result, Value::Int32(42)));
576
577        // Stack should be empty now
578        assert!(interp.pop().is_err());
579    }
580
581    #[test]
582    fn test_prelude_each_with_print() {
583        let mut interp = setup_interpreter_with_prelude();
584
585        // Test: [1 2 3] [.] each should print 1, 2, 3 (side effects only)
586        // Since [.] consumes its argument and returns nothing, stack should be clean
587        execute_string("[1 2 3] [.] each", &mut interp).unwrap();
588
589        // Stack should be empty - pure side effects
590        assert!(interp.pop().is_err());
591    }
592
593    #[test]
594    fn test_prelude_not() {
595        let mut interp = setup_interpreter_with_prelude();
596
597        // Test: not on true -> false
598        execute_string("true not", &mut interp).unwrap();
599        let result = interp.pop().unwrap();
600        assert!(matches!(result, Value::Boolean(false)));
601
602        // Test: not on false -> true
603        execute_string("false not", &mut interp).unwrap();
604        let result = interp.pop().unwrap();
605        assert!(matches!(result, Value::Boolean(true)));
606
607        // Test: not on truthy value (non-zero number) -> false
608        execute_string("42 not", &mut interp).unwrap();
609        let result = interp.pop().unwrap();
610        assert!(matches!(result, Value::Boolean(false)));
611
612        // Test: not on falsy value (zero) -> true
613        execute_string("0 not", &mut interp).unwrap();
614        let result = interp.pop().unwrap();
615        assert!(matches!(result, Value::Boolean(true)));
616
617        // Test: not on falsy value (null) -> true
618        execute_string("null not", &mut interp).unwrap();
619        let result = interp.pop().unwrap();
620        assert!(matches!(result, Value::Boolean(true)));
621
622        // Test: not on truthy value (non-empty list) -> false
623        execute_string("[1 2 3] not", &mut interp).unwrap();
624        let result = interp.pop().unwrap();
625        assert!(matches!(result, Value::Boolean(false)));
626
627        // Test: double negation: not not -> identity for truthy values
628        execute_string("5 not not", &mut interp).unwrap();
629        let result = interp.pop().unwrap();
630        assert!(matches!(result, Value::Boolean(true)));
631    }
632
633    #[test]
634    fn test_prelude_list_ref() {
635        let mut interp = setup_interpreter_with_prelude();
636
637        // Test: Get first element (index 0)
638        execute_string("[10 20 30 40] 0 list-ref", &mut interp).unwrap();
639        let result = interp.pop().unwrap();
640        assert!(matches!(result, Value::Int32(10)));
641
642        // Test: Get second element (index 1)
643        execute_string("[10 20 30 40] 1 list-ref", &mut interp).unwrap();
644        let result = interp.pop().unwrap();
645        assert!(matches!(result, Value::Int32(20)));
646
647        // Test: Get last element (index 3)
648        execute_string("[10 20 30 40] 3 list-ref", &mut interp).unwrap();
649        let result = interp.pop().unwrap();
650        assert!(matches!(result, Value::Int32(40)));
651
652        // Test: Single element list
653        execute_string("[42] 0 list-ref", &mut interp).unwrap();
654        let result = interp.pop().unwrap();
655        assert!(matches!(result, Value::Int32(42)));
656
657        // Test: Nested list access
658        execute_string("[[1 2] [3 4] [5 6]] 2 list-ref 0 list-ref", &mut interp).unwrap();
659        let result = interp.pop().unwrap();
660        assert!(matches!(result, Value::Int32(5)));
661    }
662
663    #[test]
664    fn test_prelude_append() {
665        let mut interp = setup_interpreter_with_prelude();
666
667        // Test: Append two non-empty lists
668        execute_string("[1 2] [3 4] append", &mut interp).unwrap();
669        execute_string("dup length", &mut interp).unwrap();
670        let len = interp.pop().unwrap();
671        assert!(matches!(len, Value::Int32(4)));
672        // Verify contents: should be [1 2 3 4]
673        execute_string("0 list-ref", &mut interp).unwrap();
674        assert!(matches!(interp.pop().unwrap(), Value::Int32(1)));
675
676        // Test: Append empty list to non-empty list
677        execute_string("[] [1 2 3] append", &mut interp).unwrap();
678        execute_string("length", &mut interp).unwrap();
679        let len = interp.pop().unwrap();
680        assert!(matches!(len, Value::Int32(3)));
681
682        // Test: Append non-empty list to empty list
683        execute_string("[1 2 3] [] append", &mut interp).unwrap();
684        execute_string("length", &mut interp).unwrap();
685        let len = interp.pop().unwrap();
686        assert!(matches!(len, Value::Int32(3)));
687
688        // Test: Append two empty lists
689        execute_string("[] [] append", &mut interp).unwrap();
690        let result = interp.pop().unwrap();
691        assert!(matches!(result, Value::Nil));
692
693        // Test: Append single element lists
694        execute_string("[10] [20] append", &mut interp).unwrap();
695        execute_string("dup 0 list-ref swap 1 list-ref", &mut interp).unwrap();
696        let second = interp.pop().unwrap();
697        let first = interp.pop().unwrap();
698        assert!(matches!(first, Value::Int32(10)));
699        assert!(matches!(second, Value::Int32(20)));
700
701        // Test: Chained append
702        execute_string("[1] [2 3] append [4 5] append", &mut interp).unwrap();
703        execute_string("length", &mut interp).unwrap();
704        let len = interp.pop().unwrap();
705        assert!(matches!(len, Value::Int32(5)));
706    }
707
708    #[test]
709    fn test_prelude_negate() {
710        let mut interp = setup_interpreter_with_prelude();
711
712        // Test: negate positive integer
713        execute_string("5 negate", &mut interp).unwrap();
714        let result = interp.pop().unwrap();
715        assert!(matches!(result, Value::Int32(-5)));
716
717        // Test: negate negative integer
718        execute_string("-3 negate", &mut interp).unwrap();
719        let result = interp.pop().unwrap();
720        assert!(matches!(result, Value::Int32(3)));
721
722        // Test: negate zero
723        execute_string("0 negate", &mut interp).unwrap();
724        let result = interp.pop().unwrap();
725        assert!(matches!(result, Value::Int32(0)));
726
727        // Test: double negation
728        execute_string("7 negate negate", &mut interp).unwrap();
729        let result = interp.pop().unwrap();
730        assert!(matches!(result, Value::Int32(7)));
731
732        // Test: negate with float
733        execute_string("3.14 negate", &mut interp).unwrap();
734        let result = interp.pop().unwrap();
735        match result {
736            Value::Number(f) => assert!((f - (-3.14)).abs() < 0.0001),
737            _ => panic!("Expected Number, got {:?}", result),
738        }
739
740        // Test: negate with rational
741        execute_string("3/4 negate", &mut interp).unwrap();
742        let result = interp.pop().unwrap();
743        // Result should be -3/4 as a Rational
744        match result {
745            Value::Rational(r) => {
746                assert_eq!(r.numer(), &num_bigint::BigInt::from(-3));
747                assert_eq!(r.denom(), &num_bigint::BigInt::from(4));
748            }
749            _ => panic!("Expected Rational, got {:?}", result),
750        }
751    }
752}