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 = std::ptr::null_mut();
305            let stack = push_float(stack, 3.5);
306
307            let (stack, result) = pop(stack);
308            assert_eq!(result, Value::Float(3.5));
309            assert!(stack.is_null());
310        }
311    }
312
313    #[test]
314    fn test_f_add() {
315        unsafe {
316            let stack = std::ptr::null_mut();
317            let stack = push(stack, Value::Float(1.5));
318            let stack = push(stack, Value::Float(2.5));
319
320            let stack = f_add(stack);
321
322            let (stack, result) = pop(stack);
323            assert_eq!(result, Value::Float(4.0));
324            assert!(stack.is_null());
325        }
326    }
327
328    #[test]
329    fn test_f_subtract() {
330        unsafe {
331            let stack = std::ptr::null_mut();
332            let stack = push(stack, Value::Float(5.0));
333            let stack = push(stack, Value::Float(2.0));
334
335            let stack = f_subtract(stack);
336
337            let (stack, result) = pop(stack);
338            assert_eq!(result, Value::Float(3.0));
339            assert!(stack.is_null());
340        }
341    }
342
343    #[test]
344    fn test_f_multiply() {
345        unsafe {
346            let stack = std::ptr::null_mut();
347            let stack = push(stack, Value::Float(3.0));
348            let stack = push(stack, Value::Float(4.0));
349
350            let stack = f_multiply(stack);
351
352            let (stack, result) = pop(stack);
353            assert_eq!(result, Value::Float(12.0));
354            assert!(stack.is_null());
355        }
356    }
357
358    #[test]
359    fn test_f_divide() {
360        unsafe {
361            let stack = std::ptr::null_mut();
362            let stack = push(stack, Value::Float(10.0));
363            let stack = push(stack, Value::Float(4.0));
364
365            let stack = f_divide(stack);
366
367            let (stack, result) = pop(stack);
368            assert_eq!(result, Value::Float(2.5));
369            assert!(stack.is_null());
370        }
371    }
372
373    #[test]
374    fn test_f_divide_by_zero() {
375        unsafe {
376            let stack = std::ptr::null_mut();
377            let stack = push(stack, Value::Float(1.0));
378            let stack = push(stack, Value::Float(0.0));
379
380            let stack = f_divide(stack);
381
382            let (stack, result) = pop(stack);
383            match result {
384                Value::Float(f) => assert!(f.is_infinite()),
385                _ => panic!("Expected Float"),
386            }
387            assert!(stack.is_null());
388        }
389    }
390
391    #[test]
392    fn test_f_eq_true() {
393        unsafe {
394            let stack = std::ptr::null_mut();
395            let stack = push(stack, Value::Float(3.5));
396            let stack = push(stack, Value::Float(3.5));
397
398            let stack = f_eq(stack);
399
400            let (stack, result) = pop(stack);
401            assert_eq!(result, Value::Int(1));
402            assert!(stack.is_null());
403        }
404    }
405
406    #[test]
407    fn test_f_eq_false() {
408        unsafe {
409            let stack = std::ptr::null_mut();
410            let stack = push(stack, Value::Float(3.5));
411            let stack = push(stack, Value::Float(2.5));
412
413            let stack = f_eq(stack);
414
415            let (stack, result) = pop(stack);
416            assert_eq!(result, Value::Int(0));
417            assert!(stack.is_null());
418        }
419    }
420
421    #[test]
422    fn test_f_lt() {
423        unsafe {
424            let stack = std::ptr::null_mut();
425            let stack = push(stack, Value::Float(1.5));
426            let stack = push(stack, Value::Float(2.5));
427
428            let stack = f_lt(stack);
429
430            let (stack, result) = pop(stack);
431            assert_eq!(result, Value::Int(1)); // 1.5 < 2.5
432            assert!(stack.is_null());
433        }
434    }
435
436    #[test]
437    fn test_f_gt() {
438        unsafe {
439            let stack = std::ptr::null_mut();
440            let stack = push(stack, Value::Float(2.5));
441            let stack = push(stack, Value::Float(1.5));
442
443            let stack = f_gt(stack);
444
445            let (stack, result) = pop(stack);
446            assert_eq!(result, Value::Int(1)); // 2.5 > 1.5
447            assert!(stack.is_null());
448        }
449    }
450
451    #[test]
452    fn test_f_lte() {
453        unsafe {
454            let stack = std::ptr::null_mut();
455            let stack = push(stack, Value::Float(2.5));
456            let stack = push(stack, Value::Float(2.5));
457
458            let stack = f_lte(stack);
459
460            let (stack, result) = pop(stack);
461            assert_eq!(result, Value::Int(1)); // 2.5 <= 2.5
462            assert!(stack.is_null());
463        }
464    }
465
466    #[test]
467    fn test_f_gte() {
468        unsafe {
469            let stack = std::ptr::null_mut();
470            let stack = push(stack, Value::Float(2.5));
471            let stack = push(stack, Value::Float(2.5));
472
473            let stack = f_gte(stack);
474
475            let (stack, result) = pop(stack);
476            assert_eq!(result, Value::Int(1)); // 2.5 >= 2.5
477            assert!(stack.is_null());
478        }
479    }
480
481    #[test]
482    fn test_f_neq() {
483        unsafe {
484            let stack = std::ptr::null_mut();
485            let stack = push(stack, Value::Float(1.0));
486            let stack = push(stack, Value::Float(2.0));
487
488            let stack = f_neq(stack);
489
490            let (stack, result) = pop(stack);
491            assert_eq!(result, Value::Int(1)); // 1.0 <> 2.0
492            assert!(stack.is_null());
493        }
494    }
495
496    #[test]
497    fn test_int_to_float() {
498        unsafe {
499            let stack = std::ptr::null_mut();
500            let stack = push(stack, Value::Int(42));
501
502            let stack = int_to_float(stack);
503
504            let (stack, result) = pop(stack);
505            assert_eq!(result, Value::Float(42.0));
506            assert!(stack.is_null());
507        }
508    }
509
510    #[test]
511    fn test_float_to_int() {
512        unsafe {
513            let stack = std::ptr::null_mut();
514            let stack = push(stack, Value::Float(3.7));
515
516            let stack = float_to_int(stack);
517
518            let (stack, result) = pop(stack);
519            assert_eq!(result, Value::Int(3)); // Truncates toward zero
520            assert!(stack.is_null());
521        }
522    }
523
524    #[test]
525    fn test_float_to_int_negative() {
526        unsafe {
527            let stack = std::ptr::null_mut();
528            let stack = push(stack, Value::Float(-3.7));
529
530            let stack = float_to_int(stack);
531
532            let (stack, result) = pop(stack);
533            assert_eq!(result, Value::Int(-3)); // Truncates toward zero
534            assert!(stack.is_null());
535        }
536    }
537
538    #[test]
539    fn test_float_to_int_overflow_positive() {
540        unsafe {
541            let stack = std::ptr::null_mut();
542            let stack = push(stack, Value::Float(1e20)); // Much larger than i64::MAX
543
544            let stack = float_to_int(stack);
545
546            let (stack, result) = pop(stack);
547            assert_eq!(result, Value::Int(i64::MAX)); // Clamped to max
548            assert!(stack.is_null());
549        }
550    }
551
552    #[test]
553    fn test_float_to_int_overflow_negative() {
554        unsafe {
555            let stack = std::ptr::null_mut();
556            let stack = push(stack, Value::Float(-1e20)); // Much smaller than i64::MIN
557
558            let stack = float_to_int(stack);
559
560            let (stack, result) = pop(stack);
561            assert_eq!(result, Value::Int(i64::MIN)); // Clamped to min
562            assert!(stack.is_null());
563        }
564    }
565
566    #[test]
567    fn test_float_to_int_nan() {
568        unsafe {
569            let stack = std::ptr::null_mut();
570            let stack = push(stack, Value::Float(f64::NAN));
571
572            let stack = float_to_int(stack);
573
574            let (stack, result) = pop(stack);
575            assert_eq!(result, Value::Int(0)); // NaN becomes 0
576            assert!(stack.is_null());
577        }
578    }
579
580    #[test]
581    fn test_float_to_int_infinity() {
582        unsafe {
583            let stack = std::ptr::null_mut();
584            let stack = push(stack, Value::Float(f64::INFINITY));
585
586            let stack = float_to_int(stack);
587
588            let (stack, result) = pop(stack);
589            assert_eq!(result, Value::Int(i64::MAX)); // +Inf becomes MAX
590            assert!(stack.is_null());
591        }
592    }
593
594    #[test]
595    fn test_float_to_string() {
596        unsafe {
597            let stack = std::ptr::null_mut();
598            let stack = push(stack, Value::Float(3.5));
599
600            let stack = float_to_string(stack);
601
602            let (stack, result) = pop(stack);
603            match result {
604                Value::String(s) => assert_eq!(s.as_str(), "3.5"),
605                _ => panic!("Expected String"),
606            }
607            assert!(stack.is_null());
608        }
609    }
610
611    #[test]
612    fn test_float_to_string_whole_number() {
613        unsafe {
614            let stack = std::ptr::null_mut();
615            let stack = push(stack, Value::Float(42.0));
616
617            let stack = float_to_string(stack);
618
619            let (stack, result) = pop(stack);
620            match result {
621                Value::String(s) => assert_eq!(s.as_str(), "42"),
622                _ => panic!("Expected String"),
623            }
624            assert!(stack.is_null());
625        }
626    }
627
628    #[test]
629    fn test_nan_propagation() {
630        unsafe {
631            let stack = std::ptr::null_mut();
632            let stack = push(stack, Value::Float(f64::NAN));
633            let stack = push(stack, Value::Float(1.0));
634
635            let stack = f_add(stack);
636
637            let (stack, result) = pop(stack);
638            match result {
639                Value::Float(f) => assert!(f.is_nan()),
640                _ => panic!("Expected Float"),
641            }
642            assert!(stack.is_null());
643        }
644    }
645
646    #[test]
647    fn test_infinity() {
648        unsafe {
649            let stack = std::ptr::null_mut();
650            let stack = push(stack, Value::Float(f64::INFINITY));
651            let stack = push(stack, Value::Float(1.0));
652
653            let stack = f_add(stack);
654
655            let (stack, result) = pop(stack);
656            match result {
657                Value::Float(f) => assert!(f.is_infinite()),
658                _ => panic!("Expected Float"),
659            }
660            assert!(stack.is_null());
661        }
662    }
663}