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