seq_runtime/
arithmetic.rs

1//! Arithmetic operations for Seq
2//!
3//! These functions are exported with C ABI for LLVM codegen to call.
4//!
5//! # Safety Contract
6//!
7//! **IMPORTANT:** These functions are designed to be called ONLY by compiler-generated code,
8//! not by end users or arbitrary C code. The compiler's type checker is responsible for:
9//!
10//! - Ensuring stack has correct number of values
11//! - Ensuring values are the correct types (Int for arithmetic, Int for comparisons)
12//! - Preventing division by zero at compile time when possible
13//!
14//! # Overflow Behavior
15//!
16//! All arithmetic operations use **wrapping semantics** for predictable, defined behavior:
17//! - `add`: i64::MAX + 1 wraps to i64::MIN
18//! - `subtract`: i64::MIN - 1 wraps to i64::MAX
19//! - `multiply`: overflow wraps around
20//! - `divide`: i64::MIN / -1 wraps to i64::MIN (special case)
21//!
22//! This matches the behavior of Forth and Factor, providing consistency for low-level code.
23
24use crate::error::set_runtime_error;
25use crate::stack::DISC_INT;
26use crate::stack::{Stack, peek_sv, pop, pop_two, push};
27use crate::value::Value;
28
29/// Push an integer literal onto the stack (for compiler-generated code)
30///
31/// Stack effect: ( -- n )
32///
33/// # Safety
34/// Always safe to call
35#[unsafe(no_mangle)]
36pub unsafe extern "C" fn patch_seq_push_int(stack: Stack, value: i64) -> Stack {
37    unsafe { push(stack, Value::Int(value)) }
38}
39
40/// Push a boolean literal onto the stack (for compiler-generated code)
41///
42/// Stack effect: ( -- bool )
43///
44/// # Safety
45/// Always safe to call
46#[unsafe(no_mangle)]
47pub unsafe extern "C" fn patch_seq_push_bool(stack: Stack, value: bool) -> Stack {
48    unsafe { push(stack, Value::Bool(value)) }
49}
50
51/// Add two integers
52///
53/// Stack effect: ( a b -- a+b )
54///
55/// # Safety
56/// Stack must have two Int values on top
57#[unsafe(no_mangle)]
58pub unsafe extern "C" fn patch_seq_add(stack: Stack) -> Stack {
59    let (rest, a, b) = unsafe { pop_two(stack, "add") };
60    match (a, b) {
61        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
62            push(rest, Value::Int(a_val.wrapping_add(b_val)))
63        },
64        _ => panic!("add: expected two integers on stack"),
65    }
66}
67
68/// Subtract two integers (a - b)
69///
70/// Stack effect: ( a b -- a-b )
71///
72/// # Safety
73/// Stack must have two Int values on top
74#[unsafe(no_mangle)]
75pub unsafe extern "C" fn patch_seq_subtract(stack: Stack) -> Stack {
76    let (rest, a, b) = unsafe { pop_two(stack, "subtract") };
77    match (a, b) {
78        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
79            push(rest, Value::Int(a_val.wrapping_sub(b_val)))
80        },
81        _ => panic!("subtract: expected two integers on stack"),
82    }
83}
84
85/// Multiply two integers
86///
87/// Stack effect: ( a b -- a*b )
88///
89/// # Safety
90/// Stack must have two Int values on top
91#[unsafe(no_mangle)]
92pub unsafe extern "C" fn patch_seq_multiply(stack: Stack) -> Stack {
93    let (rest, a, b) = unsafe { pop_two(stack, "multiply") };
94    match (a, b) {
95        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
96            push(rest, Value::Int(a_val.wrapping_mul(b_val)))
97        },
98        _ => panic!("multiply: expected two integers on stack"),
99    }
100}
101
102/// Divide two integers (a / b)
103///
104/// Stack effect: ( a b -- a/b )
105///
106/// # Error Handling
107/// - Division by zero: Sets runtime error and returns 0
108/// - Type mismatch: Sets runtime error and returns 0
109///
110/// # Safety
111/// Stack must have at least two values on top
112#[unsafe(no_mangle)]
113pub unsafe extern "C" fn patch_seq_divide(stack: Stack) -> Stack {
114    let (rest, a, b) = unsafe { pop_two(stack, "divide") };
115    match (a, b) {
116        (Value::Int(a_val), Value::Int(0)) => {
117            // Division by zero - set error and return 0
118            set_runtime_error(format!(
119                "divide: division by zero (attempted {} / 0)",
120                a_val
121            ));
122            unsafe { push(rest, Value::Int(0)) }
123        }
124        (Value::Int(a_val), Value::Int(b_val)) => {
125            // Use wrapping_div to handle i64::MIN / -1 overflow edge case
126            unsafe { push(rest, Value::Int(a_val.wrapping_div(b_val))) }
127        }
128        _ => {
129            // Type error - should not happen with type-checked code
130            set_runtime_error("divide: expected two integers on stack");
131            unsafe { push(rest, Value::Int(0)) }
132        }
133    }
134}
135
136/// Modulo (remainder) of two integers (a % b)
137///
138/// Stack effect: ( a b -- a%b )
139///
140/// # Error Handling
141/// - Division by zero: Sets runtime error and returns 0
142/// - Type mismatch: Sets runtime error and returns 0
143///
144/// # Safety
145/// Stack must have at least two values on top
146#[unsafe(no_mangle)]
147pub unsafe extern "C" fn patch_seq_modulo(stack: Stack) -> Stack {
148    let (rest, a, b) = unsafe { pop_two(stack, "modulo") };
149    match (a, b) {
150        (Value::Int(a_val), Value::Int(0)) => {
151            // Division by zero - set error and return 0
152            set_runtime_error(format!(
153                "modulo: division by zero (attempted {} % 0)",
154                a_val
155            ));
156            unsafe { push(rest, Value::Int(0)) }
157        }
158        (Value::Int(a_val), Value::Int(b_val)) => {
159            // Use wrapping_rem to handle i64::MIN % -1 overflow edge case
160            unsafe { push(rest, Value::Int(a_val.wrapping_rem(b_val))) }
161        }
162        _ => {
163            // Type error - should not happen with type-checked code
164            set_runtime_error("modulo: expected two integers on stack");
165            unsafe { push(rest, Value::Int(0)) }
166        }
167    }
168}
169
170/// Integer equality: =
171///
172/// Returns 1 if equal, 0 if not (Forth-style boolean)
173/// Stack effect: ( a b -- flag )
174///
175/// # Safety
176/// Stack must have two Int values on top
177#[unsafe(no_mangle)]
178pub unsafe extern "C" fn patch_seq_eq(stack: Stack) -> Stack {
179    let (rest, a, b) = unsafe { pop_two(stack, "=") };
180    match (a, b) {
181        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
182            push(rest, Value::Bool(a_val == b_val))
183        },
184        _ => panic!("=: expected two integers on stack"),
185    }
186}
187
188/// Less than: <
189///
190/// Returns 1 if a < b, 0 otherwise (Forth-style boolean)
191/// Stack effect: ( a b -- flag )
192///
193/// # Safety
194/// Stack must have two Int values on top
195#[unsafe(no_mangle)]
196pub unsafe extern "C" fn patch_seq_lt(stack: Stack) -> Stack {
197    let (rest, a, b) = unsafe { pop_two(stack, "<") };
198    match (a, b) {
199        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Bool(a_val < b_val)) },
200        _ => panic!("<: expected two integers on stack"),
201    }
202}
203
204/// Greater than: >
205///
206/// Returns 1 if a > b, 0 otherwise (Forth-style boolean)
207/// Stack effect: ( a b -- flag )
208///
209/// # Safety
210/// Stack must have two Int values on top
211#[unsafe(no_mangle)]
212pub unsafe extern "C" fn patch_seq_gt(stack: Stack) -> Stack {
213    let (rest, a, b) = unsafe { pop_two(stack, ">") };
214    match (a, b) {
215        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Bool(a_val > b_val)) },
216        _ => panic!(">: expected two integers on stack"),
217    }
218}
219
220/// Less than or equal: <=
221///
222/// Returns 1 if a <= b, 0 otherwise (Forth-style boolean)
223/// Stack effect: ( a b -- flag )
224///
225/// # Safety
226/// Stack must have two Int values on top
227#[unsafe(no_mangle)]
228pub unsafe extern "C" fn patch_seq_lte(stack: Stack) -> Stack {
229    let (rest, a, b) = unsafe { pop_two(stack, "<=") };
230    match (a, b) {
231        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
232            push(rest, Value::Bool(a_val <= b_val))
233        },
234        _ => panic!("<=: expected two integers on stack"),
235    }
236}
237
238/// Greater than or equal: >=
239///
240/// Returns 1 if a >= b, 0 otherwise (Forth-style boolean)
241/// Stack effect: ( a b -- flag )
242///
243/// # Safety
244/// Stack must have two Int values on top
245#[unsafe(no_mangle)]
246pub unsafe extern "C" fn patch_seq_gte(stack: Stack) -> Stack {
247    let (rest, a, b) = unsafe { pop_two(stack, ">=") };
248    match (a, b) {
249        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
250            push(rest, Value::Bool(a_val >= b_val))
251        },
252        _ => panic!(">=: expected two integers on stack"),
253    }
254}
255
256/// Not equal: <>
257///
258/// Returns 1 if a != b, 0 otherwise (Forth-style boolean)
259/// Stack effect: ( a b -- flag )
260///
261/// # Safety
262/// Stack must have two Int values on top
263#[unsafe(no_mangle)]
264pub unsafe extern "C" fn patch_seq_neq(stack: Stack) -> Stack {
265    let (rest, a, b) = unsafe { pop_two(stack, "<>") };
266    match (a, b) {
267        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
268            push(rest, Value::Bool(a_val != b_val))
269        },
270        _ => panic!("<>: expected two integers on stack"),
271    }
272}
273
274/// Logical AND operation (Forth-style: multiply for boolean values)
275///
276/// Stack effect: ( a b -- result )
277/// where 0 is false, non-zero is true
278/// Returns 1 if both are true (non-zero), 0 otherwise
279///
280/// # Safety
281/// Stack must have at least two Int values
282#[unsafe(no_mangle)]
283pub unsafe extern "C" fn patch_seq_and(stack: Stack) -> Stack {
284    let (rest, a, b) = unsafe { pop_two(stack, "and") };
285    match (a, b) {
286        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
287            push(
288                rest,
289                Value::Int(if a_val != 0 && b_val != 0 { 1 } else { 0 }),
290            )
291        },
292        _ => panic!("and: expected two integers on stack"),
293    }
294}
295
296/// Logical OR operation (Forth-style)
297///
298/// Stack effect: ( a b -- result )
299/// where 0 is false, non-zero is true
300/// Returns 1 if either is true (non-zero), 0 otherwise
301///
302/// # Safety
303/// Stack must have at least two Int values
304#[unsafe(no_mangle)]
305pub unsafe extern "C" fn patch_seq_or(stack: Stack) -> Stack {
306    let (rest, a, b) = unsafe { pop_two(stack, "or") };
307    match (a, b) {
308        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
309            push(
310                rest,
311                Value::Int(if a_val != 0 || b_val != 0 { 1 } else { 0 }),
312            )
313        },
314        _ => panic!("or: expected two integers on stack"),
315    }
316}
317
318/// Logical NOT operation
319///
320/// Stack effect: ( a -- result )
321/// where 0 is false, non-zero is true
322/// Returns 1 if false (0), 0 otherwise
323///
324/// # Safety
325/// Stack must have at least one Int value
326#[unsafe(no_mangle)]
327pub unsafe extern "C" fn patch_seq_not(stack: Stack) -> Stack {
328    assert!(!stack.is_null(), "not: stack is empty");
329    let (rest, a) = unsafe { pop(stack) };
330
331    match a {
332        Value::Int(a_val) => unsafe { push(rest, Value::Int(if a_val == 0 { 1 } else { 0 })) },
333        _ => panic!("not: expected integer on stack"),
334    }
335}
336
337// ============================================================================
338// Bitwise Operations
339// ============================================================================
340
341/// Bitwise AND
342///
343/// Stack effect: ( a b -- a&b )
344///
345/// # Safety
346/// Stack must have two Int values on top
347#[unsafe(no_mangle)]
348pub unsafe extern "C" fn patch_seq_band(stack: Stack) -> Stack {
349    let (rest, a, b) = unsafe { pop_two(stack, "band") };
350    match (a, b) {
351        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val & b_val)) },
352        _ => panic!("band: expected two integers on stack"),
353    }
354}
355
356/// Bitwise OR
357///
358/// Stack effect: ( a b -- a|b )
359///
360/// # Safety
361/// Stack must have two Int values on top
362#[unsafe(no_mangle)]
363pub unsafe extern "C" fn patch_seq_bor(stack: Stack) -> Stack {
364    let (rest, a, b) = unsafe { pop_two(stack, "bor") };
365    match (a, b) {
366        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val | b_val)) },
367        _ => panic!("bor: expected two integers on stack"),
368    }
369}
370
371/// Bitwise XOR
372///
373/// Stack effect: ( a b -- a^b )
374///
375/// # Safety
376/// Stack must have two Int values on top
377#[unsafe(no_mangle)]
378pub unsafe extern "C" fn patch_seq_bxor(stack: Stack) -> Stack {
379    let (rest, a, b) = unsafe { pop_two(stack, "bxor") };
380    match (a, b) {
381        (Value::Int(a_val), Value::Int(b_val)) => unsafe { push(rest, Value::Int(a_val ^ b_val)) },
382        _ => panic!("bxor: expected two integers on stack"),
383    }
384}
385
386/// Bitwise NOT (one's complement)
387///
388/// Stack effect: ( a -- !a )
389///
390/// # Safety
391/// Stack must have one Int value on top
392#[unsafe(no_mangle)]
393pub unsafe extern "C" fn patch_seq_bnot(stack: Stack) -> Stack {
394    assert!(!stack.is_null(), "bnot: stack is empty");
395    let (rest, a) = unsafe { pop(stack) };
396    match a {
397        Value::Int(a_val) => unsafe { push(rest, Value::Int(!a_val)) },
398        _ => panic!("bnot: expected integer on stack"),
399    }
400}
401
402/// Shift left
403///
404/// Stack effect: ( value count -- result )
405/// Shifts value left by count bits. Negative count or count >= 64 returns 0.
406///
407/// # Safety
408/// Stack must have two Int values on top
409#[unsafe(no_mangle)]
410pub unsafe extern "C" fn patch_seq_shl(stack: Stack) -> Stack {
411    let (rest, value, count) = unsafe { pop_two(stack, "shl") };
412    match (value, count) {
413        (Value::Int(v), Value::Int(c)) => {
414            // Use checked_shl to avoid undefined behavior for out-of-range shifts
415            // Negative counts become large u32 values, which correctly return None
416            let result = if c < 0 {
417                0
418            } else {
419                v.checked_shl(c as u32).unwrap_or(0)
420            };
421            unsafe { push(rest, Value::Int(result)) }
422        }
423        _ => panic!("shl: expected two integers on stack"),
424    }
425}
426
427/// Logical shift right (zero-fill)
428///
429/// Stack effect: ( value count -- result )
430/// Shifts value right by count bits, filling with zeros.
431/// Negative count or count >= 64 returns 0.
432///
433/// # Safety
434/// Stack must have two Int values on top
435#[unsafe(no_mangle)]
436pub unsafe extern "C" fn patch_seq_shr(stack: Stack) -> Stack {
437    let (rest, value, count) = unsafe { pop_two(stack, "shr") };
438    match (value, count) {
439        (Value::Int(v), Value::Int(c)) => {
440            // Use checked_shr to avoid undefined behavior for out-of-range shifts
441            // Cast to u64 for logical (zero-fill) shift behavior
442            let result = if c < 0 {
443                0
444            } else {
445                (v as u64).checked_shr(c as u32).unwrap_or(0) as i64
446            };
447            unsafe { push(rest, Value::Int(result)) }
448        }
449        _ => panic!("shr: expected two integers on stack"),
450    }
451}
452
453/// Population count (count number of 1 bits)
454///
455/// Stack effect: ( n -- count )
456///
457/// # Safety
458/// Stack must have one Int value on top
459#[unsafe(no_mangle)]
460pub unsafe extern "C" fn patch_seq_popcount(stack: Stack) -> Stack {
461    assert!(!stack.is_null(), "popcount: stack is empty");
462    let (rest, a) = unsafe { pop(stack) };
463    match a {
464        Value::Int(v) => unsafe { push(rest, Value::Int(v.count_ones() as i64)) },
465        _ => panic!("popcount: expected integer on stack"),
466    }
467}
468
469/// Count leading zeros
470///
471/// Stack effect: ( n -- count )
472///
473/// # Safety
474/// Stack must have one Int value on top
475#[unsafe(no_mangle)]
476pub unsafe extern "C" fn patch_seq_clz(stack: Stack) -> Stack {
477    assert!(!stack.is_null(), "clz: stack is empty");
478    let (rest, a) = unsafe { pop(stack) };
479    match a {
480        Value::Int(v) => unsafe { push(rest, Value::Int(v.leading_zeros() as i64)) },
481        _ => panic!("clz: expected integer on stack"),
482    }
483}
484
485/// Count trailing zeros
486///
487/// Stack effect: ( n -- count )
488///
489/// # Safety
490/// Stack must have one Int value on top
491#[unsafe(no_mangle)]
492pub unsafe extern "C" fn patch_seq_ctz(stack: Stack) -> Stack {
493    assert!(!stack.is_null(), "ctz: stack is empty");
494    let (rest, a) = unsafe { pop(stack) };
495    match a {
496        Value::Int(v) => unsafe { push(rest, Value::Int(v.trailing_zeros() as i64)) },
497        _ => panic!("ctz: expected integer on stack"),
498    }
499}
500
501/// Push the bit width of Int (64)
502///
503/// Stack effect: ( -- 64 )
504///
505/// # Safety
506/// Always safe to call
507#[unsafe(no_mangle)]
508pub unsafe extern "C" fn patch_seq_int_bits(stack: Stack) -> Stack {
509    unsafe { push(stack, Value::Int(64)) }
510}
511
512/// Helper for peeking at the top integer value without popping
513///
514/// Returns the integer value on top of the stack without modifying the stack.
515/// Used in conjunction with pop_stack for conditional branching.
516///
517/// **Why separate peek and pop?**
518/// In LLVM IR for conditionals, we need to:
519/// 1. Extract the integer value to test it (peek_int_value)
520/// 2. Branch based on that value (icmp + br)
521/// 3. Free the stack node in both branches (pop_stack)
522///
523/// A combined pop_int_value would leak memory since we'd need the value
524/// before branching but couldn't return the updated stack pointer through
525/// both branches. Separating these operations prevents memory leaks.
526///
527/// Stack effect: ( n -- n ) returns n
528///
529/// # Safety
530/// Stack must have an Int value on top
531#[unsafe(no_mangle)]
532pub unsafe extern "C" fn patch_seq_peek_int_value(stack: Stack) -> i64 {
533    assert!(!stack.is_null(), "peek_int_value: stack is empty");
534
535    // Stack points to next push location, so top is at stack - 1
536    let sv = unsafe { peek_sv(stack) };
537    if sv.slot0 == DISC_INT {
538        sv.slot1 as i64
539    } else {
540        panic!(
541            "peek_int_value: expected Int on stack, got discriminant {}",
542            sv.slot0
543        )
544    }
545}
546
547/// Peek at a bool value on top of stack without popping (for pattern matching)
548///
549/// # Safety
550/// Stack must have a Bool value on top
551#[unsafe(no_mangle)]
552pub unsafe extern "C" fn patch_seq_peek_bool_value(stack: Stack) -> bool {
553    use crate::stack::DISC_BOOL;
554    assert!(!stack.is_null(), "peek_bool_value: stack is empty");
555
556    // Stack points to next push location, so top is at stack - 1
557    let sv = unsafe { peek_sv(stack) };
558    if sv.slot0 == DISC_BOOL {
559        sv.slot1 != 0
560    } else {
561        panic!(
562            "peek_bool_value: expected Bool on stack, got discriminant {}",
563            sv.slot0
564        )
565    }
566}
567
568/// Helper for popping without extracting the value (for conditionals)
569///
570/// Pops the top stack node and returns the updated stack pointer.
571/// Used after peek_int_value to free the condition value's stack node.
572///
573/// Stack effect: ( n -- )
574///
575/// # Safety
576/// Stack must not be empty
577#[unsafe(no_mangle)]
578pub unsafe extern "C" fn patch_seq_pop_stack(stack: Stack) -> Stack {
579    assert!(!stack.is_null(), "pop_stack: stack is empty");
580    let (rest, _value) = unsafe { pop(stack) };
581    rest
582}
583
584// Public re-exports with short names for internal use
585pub use patch_seq_add as add;
586pub use patch_seq_and as and;
587pub use patch_seq_band as band;
588pub use patch_seq_bnot as bnot;
589pub use patch_seq_bor as bor;
590pub use patch_seq_bxor as bxor;
591pub use patch_seq_clz as clz;
592pub use patch_seq_ctz as ctz;
593pub use patch_seq_divide as divide;
594pub use patch_seq_eq as eq;
595pub use patch_seq_gt as gt;
596pub use patch_seq_gte as gte;
597pub use patch_seq_int_bits as int_bits;
598pub use patch_seq_lt as lt;
599pub use patch_seq_lte as lte;
600pub use patch_seq_multiply as multiply;
601pub use patch_seq_neq as neq;
602pub use patch_seq_not as not;
603pub use patch_seq_or as or;
604pub use patch_seq_popcount as popcount;
605pub use patch_seq_push_bool as push_bool;
606pub use patch_seq_push_int as push_int;
607pub use patch_seq_shl as shl;
608pub use patch_seq_shr as shr;
609pub use patch_seq_subtract as subtract;
610
611#[cfg(test)]
612mod tests {
613    use super::*;
614
615    #[test]
616    fn test_add() {
617        unsafe {
618            let stack = crate::stack::alloc_test_stack();
619            let stack = push_int(stack, 5);
620            let stack = push_int(stack, 3);
621            let stack = add(stack);
622
623            let (_stack, result) = pop(stack);
624            assert_eq!(result, Value::Int(8));
625        }
626    }
627
628    #[test]
629    fn test_subtract() {
630        unsafe {
631            let stack = crate::stack::alloc_test_stack();
632            let stack = push_int(stack, 10);
633            let stack = push_int(stack, 3);
634            let stack = subtract(stack);
635
636            let (_stack, result) = pop(stack);
637            assert_eq!(result, Value::Int(7));
638        }
639    }
640
641    #[test]
642    fn test_multiply() {
643        unsafe {
644            let stack = crate::stack::alloc_test_stack();
645            let stack = push_int(stack, 4);
646            let stack = push_int(stack, 5);
647            let stack = multiply(stack);
648
649            let (_stack, result) = pop(stack);
650            assert_eq!(result, Value::Int(20));
651        }
652    }
653
654    #[test]
655    fn test_divide() {
656        unsafe {
657            let stack = crate::stack::alloc_test_stack();
658            let stack = push_int(stack, 20);
659            let stack = push_int(stack, 4);
660            let stack = divide(stack);
661
662            let (_stack, result) = pop(stack);
663            assert_eq!(result, Value::Int(5));
664        }
665    }
666
667    #[test]
668    fn test_comparisons() {
669        unsafe {
670            // Test eq (returns true/false Bool)
671            let stack = crate::stack::alloc_test_stack();
672            let stack = push_int(stack, 5);
673            let stack = push_int(stack, 5);
674            let stack = eq(stack);
675            let (_stack, result) = pop(stack);
676            assert_eq!(result, Value::Bool(true));
677
678            // Test lt
679            let stack = push_int(stack, 3);
680            let stack = push_int(stack, 5);
681            let stack = lt(stack);
682            let (_stack, result) = pop(stack);
683            assert_eq!(result, Value::Bool(true));
684
685            // Test gt
686            let stack = push_int(stack, 7);
687            let stack = push_int(stack, 5);
688            let stack = gt(stack);
689            let (_stack, result) = pop(stack);
690            assert_eq!(result, Value::Bool(true));
691        }
692    }
693
694    // This test uses i64::MAX and i64::MIN which are outside the 44-bit NaN-boxing range
695    #[test]
696    fn test_overflow_wrapping() {
697        // Test that arithmetic uses wrapping semantics (defined overflow behavior)
698        unsafe {
699            // Test add overflow: i64::MAX + 1 should wrap
700            let stack = crate::stack::alloc_test_stack();
701            let stack = push_int(stack, i64::MAX);
702            let stack = push_int(stack, 1);
703            let stack = add(stack);
704            let (_stack, result) = pop(stack);
705            assert_eq!(result, Value::Int(i64::MIN)); // Wraps to minimum
706
707            // Test multiply overflow
708            let stack = push_int(stack, i64::MAX);
709            let stack = push_int(stack, 2);
710            let stack = multiply(stack);
711            let (_stack, result) = pop(stack);
712            // Result is well-defined (wrapping)
713            assert!(matches!(result, Value::Int(_)));
714
715            // Test subtract underflow
716            let stack = push_int(stack, i64::MIN);
717            let stack = push_int(stack, 1);
718            let stack = subtract(stack);
719            let (_stack, result) = pop(stack);
720            assert_eq!(result, Value::Int(i64::MAX)); // Wraps to maximum
721        }
722    }
723
724    #[test]
725    fn test_negative_division() {
726        unsafe {
727            // Test negative dividend
728            let stack = crate::stack::alloc_test_stack();
729            let stack = push_int(stack, -10);
730            let stack = push_int(stack, 3);
731            let stack = divide(stack);
732            let (_stack, result) = pop(stack);
733            assert_eq!(result, Value::Int(-3)); // Truncates toward zero
734
735            // Test negative divisor
736            let stack = push_int(stack, 10);
737            let stack = push_int(stack, -3);
738            let stack = divide(stack);
739            let (_stack, result) = pop(stack);
740            assert_eq!(result, Value::Int(-3));
741
742            // Test both negative
743            let stack = push_int(stack, -10);
744            let stack = push_int(stack, -3);
745            let stack = divide(stack);
746            let (_stack, result) = pop(stack);
747            assert_eq!(result, Value::Int(3));
748        }
749    }
750
751    // This test uses i64::MIN which is outside the 44-bit NaN-boxing range
752    #[test]
753    fn test_division_overflow_edge_case() {
754        // Critical edge case: i64::MIN / -1 would overflow
755        // Regular division panics, but wrapping_div wraps to i64::MIN
756        unsafe {
757            let stack = crate::stack::alloc_test_stack();
758            let stack = push_int(stack, i64::MIN);
759            let stack = push_int(stack, -1);
760            let stack = divide(stack);
761            let (_stack, result) = pop(stack);
762            // i64::MIN / -1 would be i64::MAX + 1, which wraps to i64::MIN
763            assert_eq!(result, Value::Int(i64::MIN));
764        }
765    }
766
767    #[test]
768    fn test_and_true_true() {
769        unsafe {
770            let stack = crate::stack::alloc_test_stack();
771            let stack = push_int(stack, 1);
772            let stack = push_int(stack, 1);
773            let stack = and(stack);
774            let (_stack, result) = pop(stack);
775            assert_eq!(result, Value::Int(1));
776        }
777    }
778
779    #[test]
780    fn test_and_true_false() {
781        unsafe {
782            let stack = crate::stack::alloc_test_stack();
783            let stack = push_int(stack, 1);
784            let stack = push_int(stack, 0);
785            let stack = and(stack);
786            let (_stack, result) = pop(stack);
787            assert_eq!(result, Value::Int(0));
788        }
789    }
790
791    #[test]
792    fn test_and_false_false() {
793        unsafe {
794            let stack = crate::stack::alloc_test_stack();
795            let stack = push_int(stack, 0);
796            let stack = push_int(stack, 0);
797            let stack = and(stack);
798            let (_stack, result) = pop(stack);
799            assert_eq!(result, Value::Int(0));
800        }
801    }
802
803    #[test]
804    fn test_or_true_true() {
805        unsafe {
806            let stack = crate::stack::alloc_test_stack();
807            let stack = push_int(stack, 1);
808            let stack = push_int(stack, 1);
809            let stack = or(stack);
810            let (_stack, result) = pop(stack);
811            assert_eq!(result, Value::Int(1));
812        }
813    }
814
815    #[test]
816    fn test_or_true_false() {
817        unsafe {
818            let stack = crate::stack::alloc_test_stack();
819            let stack = push_int(stack, 1);
820            let stack = push_int(stack, 0);
821            let stack = or(stack);
822            let (_stack, result) = pop(stack);
823            assert_eq!(result, Value::Int(1));
824        }
825    }
826
827    #[test]
828    fn test_or_false_false() {
829        unsafe {
830            let stack = crate::stack::alloc_test_stack();
831            let stack = push_int(stack, 0);
832            let stack = push_int(stack, 0);
833            let stack = or(stack);
834            let (_stack, result) = pop(stack);
835            assert_eq!(result, Value::Int(0));
836        }
837    }
838
839    #[test]
840    fn test_not_true() {
841        unsafe {
842            let stack = crate::stack::alloc_test_stack();
843            let stack = push_int(stack, 1);
844            let stack = not(stack);
845            let (_stack, result) = pop(stack);
846            assert_eq!(result, Value::Int(0));
847        }
848    }
849
850    #[test]
851    fn test_not_false() {
852        unsafe {
853            let stack = crate::stack::alloc_test_stack();
854            let stack = push_int(stack, 0);
855            let stack = not(stack);
856            let (_stack, result) = pop(stack);
857            assert_eq!(result, Value::Int(1));
858        }
859    }
860
861    #[test]
862    fn test_and_nonzero_values() {
863        // Forth-style: any non-zero is true
864        unsafe {
865            let stack = crate::stack::alloc_test_stack();
866            let stack = push_int(stack, 42);
867            let stack = push_int(stack, -5);
868            let stack = and(stack);
869            let (_stack, result) = pop(stack);
870            assert_eq!(result, Value::Int(1));
871        }
872    }
873
874    #[test]
875    fn test_divide_by_zero_sets_error() {
876        unsafe {
877            crate::error::clear_runtime_error();
878            let stack = crate::stack::alloc_test_stack();
879            let stack = push_int(stack, 42);
880            let stack = push_int(stack, 0);
881            let stack = divide(stack);
882
883            // Should have set an error
884            assert!(crate::error::has_runtime_error());
885            let error = crate::error::take_runtime_error().unwrap();
886            assert!(error.contains("division by zero"));
887
888            // Should have pushed 0 as sentinel
889            let (_stack, result) = pop(stack);
890            assert_eq!(result, Value::Int(0));
891        }
892    }
893}