seq_runtime/
float_ops.rs

1//! Float operations for Seq
2//!
3//! These functions are exported with C ABI for LLVM codegen to call.
4//! All float operations use the `f.` prefix to distinguish from integer operations.
5
6use crate::seqstring::global_string;
7use crate::stack::{Stack, pop, pop_two, push};
8use crate::value::Value;
9
10// =============================================================================
11// Push Float
12// =============================================================================
13
14/// Push a float value onto the stack
15///
16/// # Safety
17/// Stack pointer must be valid or null
18#[unsafe(no_mangle)]
19pub unsafe extern "C" fn patch_seq_push_float(stack: Stack, value: f64) -> Stack {
20    unsafe { push(stack, Value::Float(value)) }
21}
22
23// =============================================================================
24// Arithmetic Operations
25// =============================================================================
26
27/// Float addition: ( Float Float -- Float )
28///
29/// # Safety
30/// Stack must have two Float values on top
31#[unsafe(no_mangle)]
32pub unsafe extern "C" fn patch_seq_f_add(stack: Stack) -> Stack {
33    let (rest, a, b) = unsafe { pop_two(stack, "f.add") };
34    match (a, b) {
35        (Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x + y)) },
36        _ => panic!("f.add: expected two Floats on stack"),
37    }
38}
39
40/// Float subtraction: ( Float Float -- Float )
41///
42/// # Safety
43/// Stack must have two Float values on top
44#[unsafe(no_mangle)]
45pub unsafe extern "C" fn patch_seq_f_subtract(stack: Stack) -> Stack {
46    let (rest, a, b) = unsafe { pop_two(stack, "f.subtract") };
47    match (a, b) {
48        (Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x - y)) },
49        _ => panic!("f.subtract: expected two Floats on stack"),
50    }
51}
52
53/// Float multiplication: ( Float Float -- Float )
54///
55/// # Safety
56/// Stack must have two Float values on top
57#[unsafe(no_mangle)]
58pub unsafe extern "C" fn patch_seq_f_multiply(stack: Stack) -> Stack {
59    let (rest, a, b) = unsafe { pop_two(stack, "f.multiply") };
60    match (a, b) {
61        (Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x * y)) },
62        _ => panic!("f.multiply: expected two Floats on stack"),
63    }
64}
65
66/// Float division: ( Float Float -- Float )
67///
68/// Division by zero returns infinity (IEEE 754 behavior)
69///
70/// # Safety
71/// Stack must have two Float values on top
72#[unsafe(no_mangle)]
73pub unsafe extern "C" fn patch_seq_f_divide(stack: Stack) -> Stack {
74    let (rest, a, b) = unsafe { pop_two(stack, "f.divide") };
75    match (a, b) {
76        (Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x / y)) },
77        _ => panic!("f.divide: expected two Floats on stack"),
78    }
79}
80
81// =============================================================================
82// Comparison Operations (return Int 0 or 1)
83// =============================================================================
84
85/// Float equality: ( Float Float -- Int )
86///
87/// **Warning:** Direct float equality can be surprising due to IEEE 754
88/// rounding. For example, `0.1 0.2 f.add 0.3 f.=` may return 0.
89/// Consider using epsilon-based comparison for tolerances.
90///
91/// # Safety
92/// Stack must have two Float values on top
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn patch_seq_f_eq(stack: Stack) -> Stack {
95    let (rest, a, b) = unsafe { pop_two(stack, "f.=") };
96    match (a, b) {
97        (Value::Float(x), Value::Float(y)) => unsafe {
98            push(rest, Value::Int(if x == y { 1 } else { 0 }))
99        },
100        _ => panic!("f.=: expected two Floats on stack"),
101    }
102}
103
104/// Float less than: ( Float Float -- Int )
105///
106/// # Safety
107/// Stack must have two Float values on top
108#[unsafe(no_mangle)]
109pub unsafe extern "C" fn patch_seq_f_lt(stack: Stack) -> Stack {
110    let (rest, a, b) = unsafe { pop_two(stack, "f.<") };
111    match (a, b) {
112        (Value::Float(x), Value::Float(y)) => unsafe {
113            push(rest, Value::Int(if x < y { 1 } else { 0 }))
114        },
115        _ => panic!("f.<: expected two Floats on stack"),
116    }
117}
118
119/// Float greater than: ( Float Float -- Int )
120///
121/// # Safety
122/// Stack must have two Float values on top
123#[unsafe(no_mangle)]
124pub unsafe extern "C" fn patch_seq_f_gt(stack: Stack) -> Stack {
125    let (rest, a, b) = unsafe { pop_two(stack, "f.>") };
126    match (a, b) {
127        (Value::Float(x), Value::Float(y)) => unsafe {
128            push(rest, Value::Int(if x > y { 1 } else { 0 }))
129        },
130        _ => panic!("f.>: expected two Floats on stack"),
131    }
132}
133
134/// Float less than or equal: ( Float Float -- Int )
135///
136/// # Safety
137/// Stack must have two Float values on top
138#[unsafe(no_mangle)]
139pub unsafe extern "C" fn patch_seq_f_lte(stack: Stack) -> Stack {
140    let (rest, a, b) = unsafe { pop_two(stack, "f.<=") };
141    match (a, b) {
142        (Value::Float(x), Value::Float(y)) => unsafe {
143            push(rest, Value::Int(if x <= y { 1 } else { 0 }))
144        },
145        _ => panic!("f.<=: expected two Floats on stack"),
146    }
147}
148
149/// Float greater than or equal: ( Float Float -- Int )
150///
151/// # Safety
152/// Stack must have two Float values on top
153#[unsafe(no_mangle)]
154pub unsafe extern "C" fn patch_seq_f_gte(stack: Stack) -> Stack {
155    let (rest, a, b) = unsafe { pop_two(stack, "f.>=") };
156    match (a, b) {
157        (Value::Float(x), Value::Float(y)) => unsafe {
158            push(rest, Value::Int(if x >= y { 1 } else { 0 }))
159        },
160        _ => panic!("f.>=: expected two Floats on stack"),
161    }
162}
163
164/// Float not equal: ( Float Float -- Int )
165///
166/// # Safety
167/// Stack must have two Float values on top
168#[unsafe(no_mangle)]
169pub unsafe extern "C" fn patch_seq_f_neq(stack: Stack) -> Stack {
170    let (rest, a, b) = unsafe { pop_two(stack, "f.<>") };
171    match (a, b) {
172        (Value::Float(x), Value::Float(y)) => unsafe {
173            push(rest, Value::Int(if x != y { 1 } else { 0 }))
174        },
175        _ => panic!("f.<>: expected two Floats on stack"),
176    }
177}
178
179// =============================================================================
180// Type Conversions
181// =============================================================================
182
183/// Convert Int to Float: ( Int -- Float )
184///
185/// # Safety
186/// Stack must have an Int value on top
187#[unsafe(no_mangle)]
188pub unsafe extern "C" fn patch_seq_int_to_float(stack: Stack) -> Stack {
189    assert!(!stack.is_null(), "int->float: stack is empty");
190    let (stack, val) = unsafe { pop(stack) };
191
192    match val {
193        Value::Int(i) => unsafe { push(stack, Value::Float(i as f64)) },
194        _ => panic!("int->float: expected Int on stack"),
195    }
196}
197
198/// Convert Float to Int: ( Float -- Int )
199///
200/// Truncates toward zero. Values outside i64 range are clamped:
201/// - Values >= i64::MAX become i64::MAX
202/// - Values <= i64::MIN become i64::MIN
203/// - NaN becomes 0
204///
205/// # Safety
206/// Stack must have a Float value on top
207#[unsafe(no_mangle)]
208pub unsafe extern "C" fn patch_seq_float_to_int(stack: Stack) -> Stack {
209    assert!(!stack.is_null(), "float->int: stack is empty");
210    let (stack, val) = unsafe { pop(stack) };
211
212    match val {
213        Value::Float(f) => {
214            // Clamp to i64 range to avoid undefined behavior
215            let i = if f.is_nan() {
216                0
217            } else if f >= i64::MAX as f64 {
218                i64::MAX
219            } else if f <= i64::MIN as f64 {
220                i64::MIN
221            } else {
222                f as i64
223            };
224            unsafe { push(stack, Value::Int(i)) }
225        }
226        _ => panic!("float->int: expected Float on stack"),
227    }
228}
229
230/// Convert Float to String: ( Float -- String )
231///
232/// # Safety
233/// Stack must have a Float value on top
234#[unsafe(no_mangle)]
235pub unsafe extern "C" fn patch_seq_float_to_string(stack: Stack) -> Stack {
236    assert!(!stack.is_null(), "float->string: stack is empty");
237    let (stack, val) = unsafe { pop(stack) };
238
239    match val {
240        Value::Float(f) => {
241            let s = f.to_string();
242            unsafe { push(stack, Value::String(global_string(s))) }
243        }
244        _ => panic!("float->string: expected Float on stack"),
245    }
246}
247
248/// Convert String to Float: ( String -- Float Int )
249/// Returns the parsed float and 1 on success, or 0.0 and 0 on failure
250///
251/// # Safety
252/// Stack must have a String value on top
253#[unsafe(no_mangle)]
254pub unsafe extern "C" fn patch_seq_string_to_float(stack: Stack) -> Stack {
255    assert!(!stack.is_null(), "string->float: stack is empty");
256    let (stack, val) = unsafe { pop(stack) };
257
258    match val {
259        Value::String(s) => match s.as_str().parse::<f64>() {
260            Ok(f) => {
261                let stack = unsafe { push(stack, Value::Float(f)) };
262                unsafe { push(stack, Value::Int(1)) }
263            }
264            Err(_) => {
265                let stack = unsafe { push(stack, Value::Float(0.0)) };
266                unsafe { push(stack, Value::Int(0)) }
267            }
268        },
269        _ => panic!("string->float: expected String on stack"),
270    }
271}
272
273// =============================================================================
274// Public re-exports with short names
275// =============================================================================
276
277pub use patch_seq_f_add as f_add;
278pub use patch_seq_f_divide as f_divide;
279pub use patch_seq_f_eq as f_eq;
280pub use patch_seq_f_gt as f_gt;
281pub use patch_seq_f_gte as f_gte;
282pub use patch_seq_f_lt as f_lt;
283pub use patch_seq_f_lte as f_lte;
284pub use patch_seq_f_multiply as f_multiply;
285pub use patch_seq_f_neq as f_neq;
286pub use patch_seq_f_subtract as f_subtract;
287pub use patch_seq_float_to_int as float_to_int;
288pub use patch_seq_float_to_string as float_to_string;
289pub use patch_seq_int_to_float as int_to_float;
290pub use patch_seq_push_float as push_float;
291pub use patch_seq_string_to_float as string_to_float;
292
293// =============================================================================
294// Tests
295// =============================================================================
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300
301    #[test]
302    fn test_push_float() {
303        unsafe {
304            let stack = crate::stack::alloc_test_stack();
305            let stack = push_float(stack, 3.5);
306
307            let (_stack, result) = pop(stack);
308            assert_eq!(result, Value::Float(3.5));
309        }
310    }
311
312    #[test]
313    fn test_f_add() {
314        unsafe {
315            let stack = crate::stack::alloc_test_stack();
316            let stack = push(stack, Value::Float(1.5));
317            let stack = push(stack, Value::Float(2.5));
318
319            let stack = f_add(stack);
320
321            let (_stack, result) = pop(stack);
322            assert_eq!(result, Value::Float(4.0));
323        }
324    }
325
326    #[test]
327    fn test_f_subtract() {
328        unsafe {
329            let stack = crate::stack::alloc_test_stack();
330            let stack = push(stack, Value::Float(5.0));
331            let stack = push(stack, Value::Float(2.0));
332
333            let stack = f_subtract(stack);
334
335            let (_stack, result) = pop(stack);
336            assert_eq!(result, Value::Float(3.0));
337        }
338    }
339
340    #[test]
341    fn test_f_multiply() {
342        unsafe {
343            let stack = crate::stack::alloc_test_stack();
344            let stack = push(stack, Value::Float(3.0));
345            let stack = push(stack, Value::Float(4.0));
346
347            let stack = f_multiply(stack);
348
349            let (_stack, result) = pop(stack);
350            assert_eq!(result, Value::Float(12.0));
351        }
352    }
353
354    #[test]
355    fn test_f_divide() {
356        unsafe {
357            let stack = crate::stack::alloc_test_stack();
358            let stack = push(stack, Value::Float(10.0));
359            let stack = push(stack, Value::Float(4.0));
360
361            let stack = f_divide(stack);
362
363            let (_stack, result) = pop(stack);
364            assert_eq!(result, Value::Float(2.5));
365        }
366    }
367
368    #[test]
369    fn test_f_divide_by_zero() {
370        unsafe {
371            let stack = crate::stack::alloc_test_stack();
372            let stack = push(stack, Value::Float(1.0));
373            let stack = push(stack, Value::Float(0.0));
374
375            let stack = f_divide(stack);
376
377            let (_stack, result) = pop(stack);
378            match result {
379                Value::Float(f) => assert!(f.is_infinite()),
380                _ => panic!("Expected Float"),
381            }
382        }
383    }
384
385    #[test]
386    fn test_f_eq_true() {
387        unsafe {
388            let stack = crate::stack::alloc_test_stack();
389            let stack = push(stack, Value::Float(3.5));
390            let stack = push(stack, Value::Float(3.5));
391
392            let stack = f_eq(stack);
393
394            let (_stack, result) = pop(stack);
395            assert_eq!(result, Value::Int(1));
396        }
397    }
398
399    #[test]
400    fn test_f_eq_false() {
401        unsafe {
402            let stack = crate::stack::alloc_test_stack();
403            let stack = push(stack, Value::Float(3.5));
404            let stack = push(stack, Value::Float(2.5));
405
406            let stack = f_eq(stack);
407
408            let (_stack, result) = pop(stack);
409            assert_eq!(result, Value::Int(0));
410        }
411    }
412
413    #[test]
414    fn test_f_lt() {
415        unsafe {
416            let stack = crate::stack::alloc_test_stack();
417            let stack = push(stack, Value::Float(1.5));
418            let stack = push(stack, Value::Float(2.5));
419
420            let stack = f_lt(stack);
421
422            let (_stack, result) = pop(stack);
423            assert_eq!(result, Value::Int(1)); // 1.5 < 2.5
424        }
425    }
426
427    #[test]
428    fn test_f_gt() {
429        unsafe {
430            let stack = crate::stack::alloc_test_stack();
431            let stack = push(stack, Value::Float(2.5));
432            let stack = push(stack, Value::Float(1.5));
433
434            let stack = f_gt(stack);
435
436            let (_stack, result) = pop(stack);
437            assert_eq!(result, Value::Int(1)); // 2.5 > 1.5
438        }
439    }
440
441    #[test]
442    fn test_f_lte() {
443        unsafe {
444            let stack = crate::stack::alloc_test_stack();
445            let stack = push(stack, Value::Float(2.5));
446            let stack = push(stack, Value::Float(2.5));
447
448            let stack = f_lte(stack);
449
450            let (_stack, result) = pop(stack);
451            assert_eq!(result, Value::Int(1)); // 2.5 <= 2.5
452        }
453    }
454
455    #[test]
456    fn test_f_gte() {
457        unsafe {
458            let stack = crate::stack::alloc_test_stack();
459            let stack = push(stack, Value::Float(2.5));
460            let stack = push(stack, Value::Float(2.5));
461
462            let stack = f_gte(stack);
463
464            let (_stack, result) = pop(stack);
465            assert_eq!(result, Value::Int(1)); // 2.5 >= 2.5
466        }
467    }
468
469    #[test]
470    fn test_f_neq() {
471        unsafe {
472            let stack = crate::stack::alloc_test_stack();
473            let stack = push(stack, Value::Float(1.0));
474            let stack = push(stack, Value::Float(2.0));
475
476            let stack = f_neq(stack);
477
478            let (_stack, result) = pop(stack);
479            assert_eq!(result, Value::Int(1)); // 1.0 <> 2.0
480        }
481    }
482
483    #[test]
484    fn test_int_to_float() {
485        unsafe {
486            let stack = crate::stack::alloc_test_stack();
487            let stack = push(stack, Value::Int(42));
488
489            let stack = int_to_float(stack);
490
491            let (_stack, result) = pop(stack);
492            assert_eq!(result, Value::Float(42.0));
493        }
494    }
495
496    #[test]
497    fn test_float_to_int() {
498        unsafe {
499            let stack = crate::stack::alloc_test_stack();
500            let stack = push(stack, Value::Float(3.7));
501
502            let stack = float_to_int(stack);
503
504            let (_stack, result) = pop(stack);
505            assert_eq!(result, Value::Int(3)); // Truncates toward zero
506        }
507    }
508
509    #[test]
510    fn test_float_to_int_negative() {
511        unsafe {
512            let stack = crate::stack::alloc_test_stack();
513            let stack = push(stack, Value::Float(-3.7));
514
515            let stack = float_to_int(stack);
516
517            let (_stack, result) = pop(stack);
518            assert_eq!(result, Value::Int(-3)); // Truncates toward zero
519        }
520    }
521
522    #[test]
523    fn test_float_to_int_overflow_positive() {
524        unsafe {
525            let stack = crate::stack::alloc_test_stack();
526            let stack = push(stack, Value::Float(1e20)); // Much larger than i64::MAX
527
528            let stack = float_to_int(stack);
529
530            let (_stack, result) = pop(stack);
531            assert_eq!(result, Value::Int(i64::MAX)); // Clamped to max
532        }
533    }
534
535    #[test]
536    fn test_float_to_int_overflow_negative() {
537        unsafe {
538            let stack = crate::stack::alloc_test_stack();
539            let stack = push(stack, Value::Float(-1e20)); // Much smaller than i64::MIN
540
541            let stack = float_to_int(stack);
542
543            let (_stack, result) = pop(stack);
544            assert_eq!(result, Value::Int(i64::MIN)); // Clamped to min
545        }
546    }
547
548    #[test]
549    fn test_float_to_int_nan() {
550        unsafe {
551            let stack = crate::stack::alloc_test_stack();
552            let stack = push(stack, Value::Float(f64::NAN));
553
554            let stack = float_to_int(stack);
555
556            let (_stack, result) = pop(stack);
557            assert_eq!(result, Value::Int(0)); // NaN becomes 0
558        }
559    }
560
561    #[test]
562    fn test_float_to_int_infinity() {
563        unsafe {
564            let stack = crate::stack::alloc_test_stack();
565            let stack = push(stack, Value::Float(f64::INFINITY));
566
567            let stack = float_to_int(stack);
568
569            let (_stack, result) = pop(stack);
570            assert_eq!(result, Value::Int(i64::MAX)); // +Inf becomes MAX
571        }
572    }
573
574    #[test]
575    fn test_float_to_string() {
576        unsafe {
577            let stack = crate::stack::alloc_test_stack();
578            let stack = push(stack, Value::Float(3.5));
579
580            let stack = float_to_string(stack);
581
582            let (_stack, result) = pop(stack);
583            match result {
584                Value::String(s) => assert_eq!(s.as_str(), "3.5"),
585                _ => panic!("Expected String"),
586            }
587        }
588    }
589
590    #[test]
591    fn test_float_to_string_whole_number() {
592        unsafe {
593            let stack = crate::stack::alloc_test_stack();
594            let stack = push(stack, Value::Float(42.0));
595
596            let stack = float_to_string(stack);
597
598            let (_stack, result) = pop(stack);
599            match result {
600                Value::String(s) => assert_eq!(s.as_str(), "42"),
601                _ => panic!("Expected String"),
602            }
603        }
604    }
605
606    #[test]
607    fn test_nan_propagation() {
608        unsafe {
609            let stack = crate::stack::alloc_test_stack();
610            let stack = push(stack, Value::Float(f64::NAN));
611            let stack = push(stack, Value::Float(1.0));
612
613            let stack = f_add(stack);
614
615            let (_stack, result) = pop(stack);
616            match result {
617                Value::Float(f) => assert!(f.is_nan()),
618                _ => panic!("Expected Float"),
619            }
620        }
621    }
622
623    #[test]
624    fn test_infinity() {
625        unsafe {
626            let stack = crate::stack::alloc_test_stack();
627            let stack = push(stack, Value::Float(f64::INFINITY));
628            let stack = push(stack, Value::Float(1.0));
629
630            let stack = f_add(stack);
631
632            let (_stack, result) = pop(stack);
633            match result {
634                Value::Float(f) => assert!(f.is_infinite()),
635                _ => panic!("Expected Float"),
636            }
637        }
638    }
639}