Skip to main content

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