use crate::seqstring::global_string;
use crate::stack::{Stack, pop, pop_two, push};
use crate::value::Value;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_push_float(stack: Stack, value: f64) -> Stack {
unsafe { push(stack, Value::Float(value)) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_add(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.add") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x + y)) },
_ => panic!("f.add: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_subtract(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.subtract") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x - y)) },
_ => panic!("f.subtract: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_multiply(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.multiply") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x * y)) },
_ => panic!("f.multiply: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_divide(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.divide") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe { push(rest, Value::Float(x / y)) },
_ => panic!("f.divide: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_eq(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.=") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe {
push(rest, Value::Int(if x == y { 1 } else { 0 }))
},
_ => panic!("f.=: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_lt(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.<") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe {
push(rest, Value::Int(if x < y { 1 } else { 0 }))
},
_ => panic!("f.<: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_gt(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.>") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe {
push(rest, Value::Int(if x > y { 1 } else { 0 }))
},
_ => panic!("f.>: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_lte(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.<=") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe {
push(rest, Value::Int(if x <= y { 1 } else { 0 }))
},
_ => panic!("f.<=: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_gte(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.>=") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe {
push(rest, Value::Int(if x >= y { 1 } else { 0 }))
},
_ => panic!("f.>=: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_f_neq(stack: Stack) -> Stack {
let (rest, a, b) = unsafe { pop_two(stack, "f.<>") };
match (a, b) {
(Value::Float(x), Value::Float(y)) => unsafe {
push(rest, Value::Int(if x != y { 1 } else { 0 }))
},
_ => panic!("f.<>: expected two Floats on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_int_to_float(stack: Stack) -> Stack {
assert!(!stack.is_null(), "int->float: stack is empty");
let (stack, val) = unsafe { pop(stack) };
match val {
Value::Int(i) => unsafe { push(stack, Value::Float(i as f64)) },
_ => panic!("int->float: expected Int on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_float_to_int(stack: Stack) -> Stack {
assert!(!stack.is_null(), "float->int: stack is empty");
let (stack, val) = unsafe { pop(stack) };
match val {
Value::Float(f) => {
const INT63_MAX: i64 = (1i64 << 62) - 1;
const INT63_MIN: i64 = -(1i64 << 62);
let i = if f.is_nan() {
0
} else if f >= INT63_MAX as f64 {
INT63_MAX
} else if f <= INT63_MIN as f64 {
INT63_MIN
} else {
f as i64
};
unsafe { push(stack, Value::Int(i)) }
}
_ => panic!("float->int: expected Float on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_float_to_string(stack: Stack) -> Stack {
assert!(!stack.is_null(), "float->string: stack is empty");
let (stack, val) = unsafe { pop(stack) };
match val {
Value::Float(f) => {
let s = f.to_string();
unsafe { push(stack, Value::String(global_string(s))) }
}
_ => panic!("float->string: expected Float on stack"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_string_to_float(stack: Stack) -> Stack {
assert!(!stack.is_null(), "string->float: stack is empty");
let (stack, val) = unsafe { pop(stack) };
match val {
Value::String(s) => match s.as_str().parse::<f64>() {
Ok(f) => {
let stack = unsafe { push(stack, Value::Float(f)) };
unsafe { push(stack, Value::Bool(true)) }
}
Err(_) => {
let stack = unsafe { push(stack, Value::Float(0.0)) };
unsafe { push(stack, Value::Bool(false)) }
}
},
_ => panic!("string->float: expected String on stack"),
}
}
pub use patch_seq_f_add as f_add;
pub use patch_seq_f_divide as f_divide;
pub use patch_seq_f_eq as f_eq;
pub use patch_seq_f_gt as f_gt;
pub use patch_seq_f_gte as f_gte;
pub use patch_seq_f_lt as f_lt;
pub use patch_seq_f_lte as f_lte;
pub use patch_seq_f_multiply as f_multiply;
pub use patch_seq_f_neq as f_neq;
pub use patch_seq_f_subtract as f_subtract;
pub use patch_seq_float_to_int as float_to_int;
pub use patch_seq_float_to_string as float_to_string;
pub use patch_seq_int_to_float as int_to_float;
pub use patch_seq_push_float as push_float;
pub use patch_seq_string_to_float as string_to_float;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_push_float() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push_float(stack, 3.5);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Float(3.5));
}
}
#[test]
fn test_f_add() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(1.5));
let stack = push(stack, Value::Float(2.5));
let stack = f_add(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Float(4.0));
}
}
#[test]
fn test_f_subtract() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(5.0));
let stack = push(stack, Value::Float(2.0));
let stack = f_subtract(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Float(3.0));
}
}
#[test]
fn test_f_multiply() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(3.0));
let stack = push(stack, Value::Float(4.0));
let stack = f_multiply(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Float(12.0));
}
}
#[test]
fn test_f_divide() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(10.0));
let stack = push(stack, Value::Float(4.0));
let stack = f_divide(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Float(2.5));
}
}
#[test]
fn test_f_divide_by_zero() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(1.0));
let stack = push(stack, Value::Float(0.0));
let stack = f_divide(stack);
let (_stack, result) = pop(stack);
match result {
Value::Float(f) => assert!(f.is_infinite()),
_ => panic!("Expected Float"),
}
}
}
#[test]
fn test_f_eq_true() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(3.5));
let stack = push(stack, Value::Float(3.5));
let stack = f_eq(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1));
}
}
#[test]
fn test_f_eq_false() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(3.5));
let stack = push(stack, Value::Float(2.5));
let stack = f_eq(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0));
}
}
#[test]
fn test_f_lt() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(1.5));
let stack = push(stack, Value::Float(2.5));
let stack = f_lt(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1)); }
}
#[test]
fn test_f_gt() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(2.5));
let stack = push(stack, Value::Float(1.5));
let stack = f_gt(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1)); }
}
#[test]
fn test_f_lte() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(2.5));
let stack = push(stack, Value::Float(2.5));
let stack = f_lte(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1)); }
}
#[test]
fn test_f_gte() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(2.5));
let stack = push(stack, Value::Float(2.5));
let stack = f_gte(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1)); }
}
#[test]
fn test_f_neq() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(1.0));
let stack = push(stack, Value::Float(2.0));
let stack = f_neq(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(1)); }
}
#[test]
fn test_int_to_float() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Int(42));
let stack = int_to_float(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Float(42.0));
}
}
#[test]
fn test_float_to_int() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(3.7));
let stack = float_to_int(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(3)); }
}
#[test]
fn test_float_to_int_negative() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(-3.7));
let stack = float_to_int(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(-3)); }
}
#[test]
fn test_float_to_int_nan() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(f64::NAN));
let stack = float_to_int(stack);
let (_stack, result) = pop(stack);
assert_eq!(result, Value::Int(0)); }
}
#[test]
fn test_float_to_int_clamps_to_63bit_max() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(1e20));
let stack = float_to_int(stack);
let (_stack, result) = pop(stack);
let int63_max = (1i64 << 62) - 1;
assert_eq!(result, Value::Int(int63_max));
}
}
#[test]
fn test_float_to_int_clamps_to_63bit_min() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(-1e20));
let stack = float_to_int(stack);
let (_stack, result) = pop(stack);
let int63_min = -(1i64 << 62);
assert_eq!(result, Value::Int(int63_min));
}
}
#[test]
fn test_float_to_int_infinity_clamps() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(f64::INFINITY));
let stack = float_to_int(stack);
let (_stack, result) = pop(stack);
let int63_max = (1i64 << 62) - 1;
assert_eq!(result, Value::Int(int63_max));
}
}
#[test]
fn test_float_to_string() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(3.5));
let stack = float_to_string(stack);
let (_stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "3.5"),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_float_to_string_whole_number() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(42.0));
let stack = float_to_string(stack);
let (_stack, result) = pop(stack);
match result {
Value::String(s) => assert_eq!(s.as_str(), "42"),
_ => panic!("Expected String"),
}
}
}
#[test]
fn test_nan_propagation() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(f64::NAN));
let stack = push(stack, Value::Float(1.0));
let stack = f_add(stack);
let (_stack, result) = pop(stack);
match result {
Value::Float(f) => assert!(f.is_nan()),
_ => panic!("Expected Float"),
}
}
}
#[test]
fn test_infinity() {
unsafe {
let stack = crate::stack::alloc_test_stack();
let stack = push(stack, Value::Float(f64::INFINITY));
let stack = push(stack, Value::Float(1.0));
let stack = f_add(stack);
let (_stack, result) = pop(stack);
match result {
Value::Float(f) => assert!(f.is_infinite()),
_ => panic!("Expected Float"),
}
}
}
}