use crate::ast::environment::Environment; use crate::ast::types::{Signature, StaticType, Value}; use std::rc::Rc; pub fn register(env: &Environment) { register_constants(env); register_arithmetic(env); register_comparison(env); register_logic(env); } fn register_constants(env: &Environment) { // True/False are keywords or literals in parser, but could be exposed as constants too if needed. // In Delphi RTL: CFalse, CTrue, CNaN // We register NaN as a value, not a function. env.register_constant("NaN", StaticType::Float, Value::Float(f64::NAN)); env.register_constant("true", StaticType::Bool, Value::Bool(true)); env.register_constant("false", StaticType::Bool, Value::Bool(false)); } fn register_arithmetic(env: &Environment) { // --- Add (+) --- let add_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, }, Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float, }, Signature { params: StaticType::Tuple(vec![StaticType::Text, StaticType::Text]), ret: StaticType::Text, }, Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::Int]), ret: StaticType::DateTime, }, ]); env.register_native("+", add_ty, true, |args| { if args.len() == 2 { match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Int(a + b), (Value::Float(a), Value::Float(b)) => Value::Float(a + b), (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 + b), (Value::Float(a), Value::Int(b)) => Value::Float(a + *b as f64), (Value::Text(a), Value::Text(b)) => { let mut res = a.to_string(); res.push_str(b); Value::Text(Rc::from(res)) } (Value::DateTime(ts), Value::Int(ms)) => Value::DateTime(ts + ms), _ => Value::Void, } } else { // Variadic sum let mut acc = 0.0; for arg in args { if let Value::Int(i) = arg { acc += i as f64; } else if let Value::Float(f) = arg { acc += f; } } if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) } } }); // --- Subtract (-) --- let sub_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, }, Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float, }, Signature { params: StaticType::Tuple(vec![StaticType::Int]), ret: StaticType::Int, }, // Negation Signature { params: StaticType::Tuple(vec![StaticType::Float]), ret: StaticType::Float, }, Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::DateTime]), ret: StaticType::Int, }, Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::Int]), ret: StaticType::DateTime, }, ]); env.register_native("-", sub_ty, true, |args| { if args.is_empty() { return Value::Void; } if args.len() == 1 { return match args[0] { Value::Int(i) => Value::Int(-i), Value::Float(f) => Value::Float(-f), _ => Value::Void, }; } if args.len() == 2 { return match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Int(a - b), (Value::Float(a), Value::Float(b)) => Value::Float(a - b), (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 - b), (Value::Float(a), Value::Int(b)) => Value::Float(a - *b as f64), (Value::DateTime(a), Value::DateTime(b)) => Value::Int(a - b), (Value::DateTime(a), Value::Int(b)) => Value::DateTime(a - b), _ => Value::Void, }; } // Variadic sub let mut acc = match args[0] { Value::Int(i) => i as f64, Value::Float(f) => f, _ => return Value::Void, }; for arg in &args[1..] { if let Value::Int(i) = arg { acc -= *i as f64; } else if let Value::Float(f) = arg { acc -= f; } } if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) } }); // --- Multiply (*) --- let mul_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, }, Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float, }, ]); env.register_native("*", mul_ty, true, |args| { if args.len() == 2 { match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Int(a * b), (Value::Float(a), Value::Float(b)) => Value::Float(a * b), (Value::Int(a), Value::Float(b)) => Value::Float(*a as f64 * b), (Value::Float(a), Value::Int(b)) => Value::Float(a * *b as f64), _ => Value::Void, } } else { let mut acc = 1.0; for arg in args { if let Value::Int(i) = arg { acc *= i as f64; } else if let Value::Float(f) = arg { acc *= f; } } if acc.fract() == 0.0 { Value::Int(acc as i64) } else { Value::Float(acc) } } }); // --- Divide (/) --- let div_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Float, }, Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Float, }, ]); env.register_native("/", div_ty, true, |args| { if args.len() != 2 { return Value::Void; } let a = match args[0] { Value::Int(i) => i as f64, Value::Float(f) => f, _ => return Value::Void, }; let b = match args[1] { Value::Int(i) => i as f64, Value::Float(f) => f, _ => return Value::Void, }; if b == 0.0 { Value::Float(f64::NAN) } else { Value::Float(a / b) } }); // --- Integer Divide (//) --- let int_div_ty = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, })); env.register_native("//", int_div_ty, true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => { if *b == 0 { Value::Void } else { Value::Int(a / b) } } // Also allow float truncation? Delphi RTL says IntDivide takes DataValue and returns DataValue. // Usually div is for integers. Let's stick to Int. _ => Value::Void, } }); // --- Modulus (%) --- let mod_ty = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, })); env.register_native("%", mod_ty, true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => { if *b == 0 { Value::Void } else { Value::Int(a % b) } } _ => Value::Void, } }); } fn register_comparison(env: &Environment) { let cmp_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Bool, }, Signature { params: StaticType::Tuple(vec![StaticType::Float, StaticType::Float]), ret: StaticType::Bool, }, Signature { params: StaticType::Tuple(vec![StaticType::DateTime, StaticType::DateTime]), ret: StaticType::Bool, }, ]); // --- Greater Than (>) --- env.register_native(">", cmp_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Bool(a > b), (Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b), (Value::Float(a), Value::Int(b)) => Value::Bool(*a > (*b as f64)), (Value::Float(a), Value::Float(b)) => Value::Bool(a > b), (Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a > b), _ => Value::Bool(false), } }); // --- Less Than (<) --- env.register_native("<", cmp_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Bool(a < b), (Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) < *b), (Value::Float(a), Value::Int(b)) => Value::Bool(*a < (*b as f64)), (Value::Float(a), Value::Float(b)) => Value::Bool(a < b), (Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a < b), _ => Value::Bool(false), } }); // --- Greater Or Equal (>=) --- env.register_native(">=", cmp_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Bool(a >= b), (Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) >= *b), (Value::Float(a), Value::Int(b)) => Value::Bool(*a >= (*b as f64)), (Value::Float(a), Value::Float(b)) => Value::Bool(a >= b), (Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a >= b), _ => Value::Bool(false), } }); // --- Less Or Equal (<=) --- env.register_native("<=", cmp_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Bool(a <= b), (Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) <= *b), (Value::Float(a), Value::Int(b)) => Value::Bool(*a <= (*b as f64)), (Value::Float(a), Value::Float(b)) => Value::Bool(a <= b), (Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a <= b), _ => Value::Bool(false), } }); // --- Equal (=) --- let eq_ty = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Any, StaticType::Any]), ret: StaticType::Bool, })); env.register_native("=", eq_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } // Simple equality check. // Note: Floating point equality is tricky, but we follow standard behavior for now. match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Bool(a == b), (Value::Float(a), Value::Float(b)) => Value::Bool((a - b).abs() < f64::EPSILON), (Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64 - b).abs() < f64::EPSILON), (Value::Float(a), Value::Int(b)) => Value::Bool((a - *b as f64).abs() < f64::EPSILON), (Value::Text(a), Value::Text(b)) => Value::Bool(a == b), (Value::Bool(a), Value::Bool(b)) => Value::Bool(a == b), (Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a == b), (Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a == b), (Value::Void, Value::Void) => Value::Bool(true), _ => Value::Bool(false), } }); // --- Not Equal (<>) --- env.register_native("<>", eq_ty, true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Bool(a != b), (Value::Float(a), Value::Float(b)) => Value::Bool((a - b).abs() >= f64::EPSILON), (Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64 - b).abs() >= f64::EPSILON), (Value::Float(a), Value::Int(b)) => Value::Bool((a - *b as f64).abs() >= f64::EPSILON), (Value::Text(a), Value::Text(b)) => Value::Bool(a != b), (Value::Bool(a), Value::Bool(b)) => Value::Bool(a != b), (Value::Keyword(a), Value::Keyword(b)) => Value::Bool(a != b), (Value::DateTime(a), Value::DateTime(b)) => Value::Bool(a != b), (Value::Void, Value::Void) => Value::Bool(false), _ => Value::Bool(true), } }); } fn register_logic(env: &Environment) { // --- Not (not) --- let not_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Bool]), ret: StaticType::Bool, }, Signature { params: StaticType::Tuple(vec![StaticType::Int]), ret: StaticType::Int, }, ]); env.register_native("not", not_ty, true, |args| { if args.len() != 1 { return Value::Void; } match &args[0] { Value::Bool(b) => Value::Bool(!b), Value::Int(i) => Value::Int(!i), // Bitwise NOT _ => Value::Void, } }); // --- And (and) --- let logic_op_ty = StaticType::FunctionOverloads(vec![ Signature { params: StaticType::Tuple(vec![StaticType::Bool, StaticType::Bool]), ret: StaticType::Bool, }, Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, }, ]); env.register_native("and", logic_op_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a && *b), (Value::Int(a), Value::Int(b)) => Value::Int(a & b), _ => Value::Void, } }); // --- Or (or) --- env.register_native("or", logic_op_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a || *b), (Value::Int(a), Value::Int(b)) => Value::Int(a | b), _ => Value::Void, } }); // --- Xor (xor) --- env.register_native("xor", logic_op_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Bool(a), Value::Bool(b)) => Value::Bool(*a ^ *b), (Value::Int(a), Value::Int(b)) => Value::Int(a ^ b), _ => Value::Void, } }); // --- Shift Left (<<) --- let shift_ty = StaticType::Function(Box::new(Signature { params: StaticType::Tuple(vec![StaticType::Int, StaticType::Int]), ret: StaticType::Int, })); env.register_native("<<", shift_ty.clone(), true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Int(a << b), _ => Value::Void, } }); // --- Shift Right (>>) --- env.register_native(">>", shift_ty, true, |args| { if args.len() != 2 { return Value::Void; } match (&args[0], &args[1]) { (Value::Int(a), Value::Int(b)) => Value::Int(a >> b), _ => Value::Void, } }); }