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::stack::{Stack, pop, push};
25use crate::value::Value;
26
27/// Push an integer literal onto the stack (for compiler-generated code)
28///
29/// Stack effect: ( -- n )
30///
31/// # Safety
32/// Always safe to call
33#[unsafe(no_mangle)]
34pub unsafe extern "C" fn patch_seq_push_int(stack: Stack, value: i64) -> Stack {
35    unsafe { push(stack, Value::Int(value)) }
36}
37
38/// Push a boolean literal onto the stack (for compiler-generated code)
39///
40/// Stack effect: ( -- bool )
41///
42/// # Safety
43/// Always safe to call
44#[unsafe(no_mangle)]
45pub unsafe extern "C" fn patch_seq_push_bool(stack: Stack, value: bool) -> Stack {
46    unsafe { push(stack, Value::Bool(value)) }
47}
48
49/// Add two integers
50///
51/// Stack effect: ( a b -- a+b )
52///
53/// # Safety
54/// Stack must have two Int values on top
55#[unsafe(no_mangle)]
56pub unsafe extern "C" fn patch_seq_add(stack: Stack) -> Stack {
57    assert!(!stack.is_null(), "add: stack is empty");
58    let (rest, b) = unsafe { pop(stack) };
59    assert!(!rest.is_null(), "add: stack has only one value");
60    let (rest, a) = unsafe { pop(rest) };
61
62    match (a, b) {
63        (Value::Int(a_val), Value::Int(b_val)) => {
64            let result = a_val.wrapping_add(b_val); // Wrapping for defined overflow behavior
65            unsafe { push(rest, Value::Int(result)) }
66        }
67        _ => panic!("add: expected two integers on stack"),
68    }
69}
70
71/// Subtract two integers (a - b)
72///
73/// Stack effect: ( a b -- a-b )
74///
75/// # Safety
76/// Stack must have two Int values on top
77#[unsafe(no_mangle)]
78pub unsafe extern "C" fn patch_seq_subtract(stack: Stack) -> Stack {
79    assert!(!stack.is_null(), "subtract: stack is empty");
80    let (rest, b) = unsafe { pop(stack) };
81    assert!(!rest.is_null(), "subtract: stack has only one value");
82    let (rest, a) = unsafe { pop(rest) };
83
84    match (a, b) {
85        (Value::Int(a_val), Value::Int(b_val)) => {
86            let result = a_val.wrapping_sub(b_val);
87            unsafe { push(rest, Value::Int(result)) }
88        }
89        _ => panic!("subtract: expected two integers on stack"),
90    }
91}
92
93/// Multiply two integers
94///
95/// Stack effect: ( a b -- a*b )
96///
97/// # Safety
98/// Stack must have two Int values on top
99#[unsafe(no_mangle)]
100pub unsafe extern "C" fn patch_seq_multiply(stack: Stack) -> Stack {
101    assert!(!stack.is_null(), "multiply: stack is empty");
102    let (rest, b) = unsafe { pop(stack) };
103    assert!(!rest.is_null(), "multiply: stack has only one value");
104    let (rest, a) = unsafe { pop(rest) };
105
106    match (a, b) {
107        (Value::Int(a_val), Value::Int(b_val)) => {
108            let result = a_val.wrapping_mul(b_val);
109            unsafe { push(rest, Value::Int(result)) }
110        }
111        _ => panic!("multiply: expected two integers on stack"),
112    }
113}
114
115/// Divide two integers (a / b)
116///
117/// Stack effect: ( a b -- a/b )
118///
119/// # Safety
120/// Stack must have two Int values on top, b must not be zero
121#[unsafe(no_mangle)]
122pub unsafe extern "C" fn patch_seq_divide(stack: Stack) -> Stack {
123    assert!(!stack.is_null(), "divide: stack is empty");
124    let (rest, b) = unsafe { pop(stack) };
125    assert!(!rest.is_null(), "divide: stack has only one value");
126    let (rest, a) = unsafe { pop(rest) };
127
128    match (a, b) {
129        (Value::Int(a_val), Value::Int(b_val)) => {
130            assert!(
131                b_val != 0,
132                "divide: division by zero (attempted {} / {})",
133                a_val,
134                b_val
135            );
136            // Use wrapping_div to handle i64::MIN / -1 overflow edge case
137            // (consistent with wrapping semantics for add/subtract/multiply)
138            let result = a_val.wrapping_div(b_val);
139            unsafe { push(rest, Value::Int(result)) }
140        }
141        _ => panic!("divide: expected two integers on stack"),
142    }
143}
144
145/// Integer equality: =
146///
147/// Returns 1 if equal, 0 if not (Forth-style boolean)
148/// Stack effect: ( a b -- flag )
149///
150/// # Safety
151/// Stack must have two Int values on top
152#[unsafe(no_mangle)]
153pub unsafe extern "C" fn patch_seq_eq(stack: Stack) -> Stack {
154    assert!(!stack.is_null(), "eq: stack is empty");
155    let (rest, b) = unsafe { pop(stack) };
156    assert!(!rest.is_null(), "eq: stack has only one value");
157    let (rest, a) = unsafe { pop(rest) };
158
159    match (a, b) {
160        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
161            push(rest, Value::Int(if a_val == b_val { 1 } else { 0 }))
162        },
163        _ => panic!("eq: expected two integers on stack"),
164    }
165}
166
167/// Less than: <
168///
169/// Returns 1 if a < b, 0 otherwise (Forth-style boolean)
170/// Stack effect: ( a b -- flag )
171///
172/// # Safety
173/// Stack must have two Int values on top
174#[unsafe(no_mangle)]
175pub unsafe extern "C" fn patch_seq_lt(stack: Stack) -> Stack {
176    assert!(!stack.is_null(), "lt: stack is empty");
177    let (rest, b) = unsafe { pop(stack) };
178    assert!(!rest.is_null(), "lt: stack has only one value");
179    let (rest, a) = unsafe { pop(rest) };
180
181    match (a, b) {
182        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
183            push(rest, Value::Int(if a_val < b_val { 1 } else { 0 }))
184        },
185        _ => panic!("lt: expected two integers on stack"),
186    }
187}
188
189/// Greater than: >
190///
191/// Returns 1 if a > b, 0 otherwise (Forth-style boolean)
192/// Stack effect: ( a b -- flag )
193///
194/// # Safety
195/// Stack must have two Int values on top
196#[unsafe(no_mangle)]
197pub unsafe extern "C" fn patch_seq_gt(stack: Stack) -> Stack {
198    assert!(!stack.is_null(), "gt: stack is empty");
199    let (rest, b) = unsafe { pop(stack) };
200    assert!(!rest.is_null(), "gt: stack has only one value");
201    let (rest, a) = unsafe { pop(rest) };
202
203    match (a, b) {
204        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
205            push(rest, Value::Int(if a_val > b_val { 1 } else { 0 }))
206        },
207        _ => panic!("gt: expected two integers on stack"),
208    }
209}
210
211/// Less than or equal: <=
212///
213/// Returns 1 if a <= b, 0 otherwise (Forth-style boolean)
214/// Stack effect: ( a b -- flag )
215///
216/// # Safety
217/// Stack must have two Int values on top
218#[unsafe(no_mangle)]
219pub unsafe extern "C" fn patch_seq_lte(stack: Stack) -> Stack {
220    assert!(!stack.is_null(), "lte: stack is empty");
221    let (rest, b) = unsafe { pop(stack) };
222    assert!(!rest.is_null(), "lte: stack has only one value");
223    let (rest, a) = unsafe { pop(rest) };
224
225    match (a, b) {
226        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
227            push(rest, Value::Int(if a_val <= b_val { 1 } else { 0 }))
228        },
229        _ => panic!("lte: expected two integers on stack"),
230    }
231}
232
233/// Greater than or equal: >=
234///
235/// Returns 1 if a >= b, 0 otherwise (Forth-style boolean)
236/// Stack effect: ( a b -- flag )
237///
238/// # Safety
239/// Stack must have two Int values on top
240#[unsafe(no_mangle)]
241pub unsafe extern "C" fn patch_seq_gte(stack: Stack) -> Stack {
242    assert!(!stack.is_null(), "gte: stack is empty");
243    let (rest, b) = unsafe { pop(stack) };
244    assert!(!rest.is_null(), "gte: stack has only one value");
245    let (rest, a) = unsafe { pop(rest) };
246
247    match (a, b) {
248        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
249            push(rest, Value::Int(if a_val >= b_val { 1 } else { 0 }))
250        },
251        _ => panic!("gte: 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    assert!(!stack.is_null(), "neq: stack is empty");
265    let (rest, b) = unsafe { pop(stack) };
266    assert!(!rest.is_null(), "neq: stack has only one value");
267    let (rest, a) = unsafe { pop(rest) };
268
269    match (a, b) {
270        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
271            push(rest, Value::Int(if a_val != b_val { 1 } else { 0 }))
272        },
273        _ => panic!("neq: expected two integers on stack"),
274    }
275}
276
277/// Logical AND operation (Forth-style: multiply for boolean values)
278///
279/// Stack effect: ( a b -- result )
280/// where 0 is false, non-zero is true
281/// Returns 1 if both are true (non-zero), 0 otherwise
282///
283/// # Safety
284/// Stack must have at least two Int values
285#[unsafe(no_mangle)]
286pub unsafe extern "C" fn patch_seq_and(stack: Stack) -> Stack {
287    assert!(!stack.is_null(), "and: stack is empty");
288    let (rest, b) = unsafe { pop(stack) };
289    assert!(!rest.is_null(), "and: stack has only one value");
290    let (rest, a) = unsafe { pop(rest) };
291
292    match (a, b) {
293        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
294            push(
295                rest,
296                Value::Int(if a_val != 0 && b_val != 0 { 1 } else { 0 }),
297            )
298        },
299        _ => panic!("and: expected two integers on stack"),
300    }
301}
302
303/// Logical OR operation (Forth-style)
304///
305/// Stack effect: ( a b -- result )
306/// where 0 is false, non-zero is true
307/// Returns 1 if either is true (non-zero), 0 otherwise
308///
309/// # Safety
310/// Stack must have at least two Int values
311#[unsafe(no_mangle)]
312pub unsafe extern "C" fn patch_seq_or(stack: Stack) -> Stack {
313    assert!(!stack.is_null(), "or: stack is empty");
314    let (rest, b) = unsafe { pop(stack) };
315    assert!(!rest.is_null(), "or: stack has only one value");
316    let (rest, a) = unsafe { pop(rest) };
317
318    match (a, b) {
319        (Value::Int(a_val), Value::Int(b_val)) => unsafe {
320            push(
321                rest,
322                Value::Int(if a_val != 0 || b_val != 0 { 1 } else { 0 }),
323            )
324        },
325        _ => panic!("or: expected two integers on stack"),
326    }
327}
328
329/// Logical NOT operation
330///
331/// Stack effect: ( a -- result )
332/// where 0 is false, non-zero is true
333/// Returns 1 if false (0), 0 otherwise
334///
335/// # Safety
336/// Stack must have at least one Int value
337#[unsafe(no_mangle)]
338pub unsafe extern "C" fn patch_seq_not(stack: Stack) -> Stack {
339    assert!(!stack.is_null(), "not: stack is empty");
340    let (rest, a) = unsafe { pop(stack) };
341
342    match a {
343        Value::Int(a_val) => unsafe { push(rest, Value::Int(if a_val == 0 { 1 } else { 0 })) },
344        _ => panic!("not: expected integer on stack"),
345    }
346}
347
348/// Helper for peeking at the top integer value without popping
349///
350/// Returns the integer value on top of the stack without modifying the stack.
351/// Used in conjunction with pop_stack for conditional branching.
352///
353/// **Why separate peek and pop?**
354/// In LLVM IR for conditionals, we need to:
355/// 1. Extract the integer value to test it (peek_int_value)
356/// 2. Branch based on that value (icmp + br)
357/// 3. Free the stack node in both branches (pop_stack)
358///
359/// A combined pop_int_value would leak memory since we'd need the value
360/// before branching but couldn't return the updated stack pointer through
361/// both branches. Separating these operations prevents memory leaks.
362///
363/// Stack effect: ( n -- n ) returns n
364///
365/// # Safety
366/// Stack must have an Int value on top
367#[unsafe(no_mangle)]
368pub unsafe extern "C" fn patch_seq_peek_int_value(stack: Stack) -> i64 {
369    assert!(!stack.is_null(), "peek_int_value: stack is empty");
370
371    let node = unsafe { &*stack };
372    match &node.value {
373        Value::Int(n) => *n,
374        _ => panic!(
375            "peek_int_value: expected Int on stack, got {:?}",
376            node.value
377        ),
378    }
379}
380
381/// Helper for popping without extracting the value (for conditionals)
382///
383/// Pops the top stack node and returns the updated stack pointer.
384/// Used after peek_int_value to free the condition value's stack node.
385///
386/// Stack effect: ( n -- )
387///
388/// # Safety
389/// Stack must not be empty
390#[unsafe(no_mangle)]
391pub unsafe extern "C" fn patch_seq_pop_stack(stack: Stack) -> Stack {
392    assert!(!stack.is_null(), "pop_stack: stack is empty");
393    let (rest, _value) = unsafe { pop(stack) };
394    rest
395}
396
397// Public re-exports with short names for internal use
398pub use patch_seq_add as add;
399pub use patch_seq_and as and;
400pub use patch_seq_divide as divide;
401pub use patch_seq_eq as eq;
402pub use patch_seq_gt as gt;
403pub use patch_seq_gte as gte;
404pub use patch_seq_lt as lt;
405pub use patch_seq_lte as lte;
406pub use patch_seq_multiply as multiply;
407pub use patch_seq_neq as neq;
408pub use patch_seq_not as not;
409pub use patch_seq_or as or;
410pub use patch_seq_push_bool as push_bool;
411pub use patch_seq_push_int as push_int;
412pub use patch_seq_subtract as subtract;
413
414#[cfg(test)]
415mod tests {
416    use super::*;
417
418    #[test]
419    fn test_add() {
420        unsafe {
421            let stack = std::ptr::null_mut();
422            let stack = push_int(stack, 5);
423            let stack = push_int(stack, 3);
424            let stack = add(stack);
425
426            let (stack, result) = pop(stack);
427            assert_eq!(result, Value::Int(8));
428            assert!(stack.is_null());
429        }
430    }
431
432    #[test]
433    fn test_subtract() {
434        unsafe {
435            let stack = std::ptr::null_mut();
436            let stack = push_int(stack, 10);
437            let stack = push_int(stack, 3);
438            let stack = subtract(stack);
439
440            let (stack, result) = pop(stack);
441            assert_eq!(result, Value::Int(7));
442            assert!(stack.is_null());
443        }
444    }
445
446    #[test]
447    fn test_multiply() {
448        unsafe {
449            let stack = std::ptr::null_mut();
450            let stack = push_int(stack, 4);
451            let stack = push_int(stack, 5);
452            let stack = multiply(stack);
453
454            let (stack, result) = pop(stack);
455            assert_eq!(result, Value::Int(20));
456            assert!(stack.is_null());
457        }
458    }
459
460    #[test]
461    fn test_divide() {
462        unsafe {
463            let stack = std::ptr::null_mut();
464            let stack = push_int(stack, 20);
465            let stack = push_int(stack, 4);
466            let stack = divide(stack);
467
468            let (stack, result) = pop(stack);
469            assert_eq!(result, Value::Int(5));
470            assert!(stack.is_null());
471        }
472    }
473
474    #[test]
475    fn test_comparisons() {
476        unsafe {
477            // Test eq (returns 1 for true, 0 for false - Forth style)
478            let stack = std::ptr::null_mut();
479            let stack = push_int(stack, 5);
480            let stack = push_int(stack, 5);
481            let stack = eq(stack);
482            let (stack, result) = pop(stack);
483            assert_eq!(result, Value::Int(1)); // 1 = true
484            assert!(stack.is_null());
485
486            // Test lt
487            let stack = push_int(stack, 3);
488            let stack = push_int(stack, 5);
489            let stack = lt(stack);
490            let (stack, result) = pop(stack);
491            assert_eq!(result, Value::Int(1)); // 1 = true
492            assert!(stack.is_null());
493
494            // Test gt
495            let stack = push_int(stack, 7);
496            let stack = push_int(stack, 5);
497            let stack = gt(stack);
498            let (stack, result) = pop(stack);
499            assert_eq!(result, Value::Int(1)); // 1 = true
500            assert!(stack.is_null());
501        }
502    }
503
504    #[test]
505    fn test_overflow_wrapping() {
506        // Test that arithmetic uses wrapping semantics (defined overflow behavior)
507        unsafe {
508            // Test add overflow: i64::MAX + 1 should wrap
509            let stack = std::ptr::null_mut();
510            let stack = push_int(stack, i64::MAX);
511            let stack = push_int(stack, 1);
512            let stack = add(stack);
513            let (stack, result) = pop(stack);
514            assert_eq!(result, Value::Int(i64::MIN)); // Wraps to minimum
515            assert!(stack.is_null());
516
517            // Test multiply overflow
518            let stack = push_int(stack, i64::MAX);
519            let stack = push_int(stack, 2);
520            let stack = multiply(stack);
521            let (stack, result) = pop(stack);
522            // Result is well-defined (wrapping)
523            assert!(matches!(result, Value::Int(_)));
524            assert!(stack.is_null());
525
526            // Test subtract underflow
527            let stack = push_int(stack, i64::MIN);
528            let stack = push_int(stack, 1);
529            let stack = subtract(stack);
530            let (stack, result) = pop(stack);
531            assert_eq!(result, Value::Int(i64::MAX)); // Wraps to maximum
532            assert!(stack.is_null());
533        }
534    }
535
536    #[test]
537    fn test_negative_division() {
538        unsafe {
539            // Test negative dividend
540            let stack = std::ptr::null_mut();
541            let stack = push_int(stack, -10);
542            let stack = push_int(stack, 3);
543            let stack = divide(stack);
544            let (stack, result) = pop(stack);
545            assert_eq!(result, Value::Int(-3)); // Truncates toward zero
546            assert!(stack.is_null());
547
548            // Test negative divisor
549            let stack = push_int(stack, 10);
550            let stack = push_int(stack, -3);
551            let stack = divide(stack);
552            let (stack, result) = pop(stack);
553            assert_eq!(result, Value::Int(-3));
554            assert!(stack.is_null());
555
556            // Test both negative
557            let stack = push_int(stack, -10);
558            let stack = push_int(stack, -3);
559            let stack = divide(stack);
560            let (stack, result) = pop(stack);
561            assert_eq!(result, Value::Int(3));
562            assert!(stack.is_null());
563        }
564    }
565
566    #[test]
567    fn test_division_overflow_edge_case() {
568        // Critical edge case: i64::MIN / -1 would overflow
569        // Regular division panics, but wrapping_div wraps to i64::MIN
570        unsafe {
571            let stack = std::ptr::null_mut();
572            let stack = push_int(stack, i64::MIN);
573            let stack = push_int(stack, -1);
574            let stack = divide(stack);
575            let (stack, result) = pop(stack);
576            // i64::MIN / -1 would be i64::MAX + 1, which wraps to i64::MIN
577            assert_eq!(result, Value::Int(i64::MIN));
578            assert!(stack.is_null());
579        }
580    }
581
582    #[test]
583    fn test_and_true_true() {
584        unsafe {
585            let stack = std::ptr::null_mut();
586            let stack = push_int(stack, 1);
587            let stack = push_int(stack, 1);
588            let stack = and(stack);
589            let (stack, result) = pop(stack);
590            assert_eq!(result, Value::Int(1));
591            assert!(stack.is_null());
592        }
593    }
594
595    #[test]
596    fn test_and_true_false() {
597        unsafe {
598            let stack = std::ptr::null_mut();
599            let stack = push_int(stack, 1);
600            let stack = push_int(stack, 0);
601            let stack = and(stack);
602            let (stack, result) = pop(stack);
603            assert_eq!(result, Value::Int(0));
604            assert!(stack.is_null());
605        }
606    }
607
608    #[test]
609    fn test_and_false_false() {
610        unsafe {
611            let stack = std::ptr::null_mut();
612            let stack = push_int(stack, 0);
613            let stack = push_int(stack, 0);
614            let stack = and(stack);
615            let (stack, result) = pop(stack);
616            assert_eq!(result, Value::Int(0));
617            assert!(stack.is_null());
618        }
619    }
620
621    #[test]
622    fn test_or_true_true() {
623        unsafe {
624            let stack = std::ptr::null_mut();
625            let stack = push_int(stack, 1);
626            let stack = push_int(stack, 1);
627            let stack = or(stack);
628            let (stack, result) = pop(stack);
629            assert_eq!(result, Value::Int(1));
630            assert!(stack.is_null());
631        }
632    }
633
634    #[test]
635    fn test_or_true_false() {
636        unsafe {
637            let stack = std::ptr::null_mut();
638            let stack = push_int(stack, 1);
639            let stack = push_int(stack, 0);
640            let stack = or(stack);
641            let (stack, result) = pop(stack);
642            assert_eq!(result, Value::Int(1));
643            assert!(stack.is_null());
644        }
645    }
646
647    #[test]
648    fn test_or_false_false() {
649        unsafe {
650            let stack = std::ptr::null_mut();
651            let stack = push_int(stack, 0);
652            let stack = push_int(stack, 0);
653            let stack = or(stack);
654            let (stack, result) = pop(stack);
655            assert_eq!(result, Value::Int(0));
656            assert!(stack.is_null());
657        }
658    }
659
660    #[test]
661    fn test_not_true() {
662        unsafe {
663            let stack = std::ptr::null_mut();
664            let stack = push_int(stack, 1);
665            let stack = not(stack);
666            let (stack, result) = pop(stack);
667            assert_eq!(result, Value::Int(0));
668            assert!(stack.is_null());
669        }
670    }
671
672    #[test]
673    fn test_not_false() {
674        unsafe {
675            let stack = std::ptr::null_mut();
676            let stack = push_int(stack, 0);
677            let stack = not(stack);
678            let (stack, result) = pop(stack);
679            assert_eq!(result, Value::Int(1));
680            assert!(stack.is_null());
681        }
682    }
683
684    #[test]
685    fn test_and_nonzero_values() {
686        // Forth-style: any non-zero is true
687        unsafe {
688            let stack = std::ptr::null_mut();
689            let stack = push_int(stack, 42);
690            let stack = push_int(stack, -5);
691            let stack = and(stack);
692            let (stack, result) = pop(stack);
693            assert_eq!(result, Value::Int(1));
694            assert!(stack.is_null());
695        }
696    }
697
698    // Note: Division by zero test omitted because panics cannot be caught across
699    // extern "C" boundaries. The runtime will panic with a helpful error message
700    // "divide: division by zero (attempted X / 0)" which is validated at compile time
701    // by the type checker in production code.
702}