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