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