use crate::ast::value::Value; use crate::opcodes::default_fn; use rust_decimal::{prelude::ToPrimitive, Decimal, MathematicalOps}; use super::error::VMError; use super::vm::VM; impl<'a> VM<'a> { /// Dispatch a built-in (default) function call by ID pub(super) fn dispatch_builtin( &mut self, dest: usize, fn_id: u8, arg_regs: &[usize], ) -> Result<(), VMError> { match fn_id { default_fn::RAND => self.builtin_rand(dest, arg_regs), default_fn::ABS => self.builtin_abs(dest, arg_regs), default_fn::MIN => self.builtin_min(dest, arg_regs), default_fn::MAX => self.builtin_max(dest, arg_regs), default_fn::FLOOR => self.builtin_floor(dest, arg_regs), default_fn::CEIL => self.builtin_ceil(dest, arg_regs), default_fn::ROUND => self.builtin_round(dest, arg_regs), default_fn::SQRT => self.builtin_sqrt(dest, arg_regs), default_fn::LEN => self.builtin_len(dest, arg_regs), default_fn::TO_STRING => self.builtin_to_string(dest, arg_regs), default_fn::TO_NUMBER => self.builtin_to_number(dest, arg_regs), _ => { let name = default_fn::name(fn_id) .map(|s| s.to_string()) .unwrap_or_else(|| format!("unknown({})", fn_id)); Err(VMError::RuntimeError(format!( "Unknown default function: {}", name ))) } } } fn builtin_rand(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { use rand::RngExt; if arg_regs.len() < 2 { return Err(VMError::RuntimeError( "rand() requires two arguments (min, max)".to_string(), )); } match (&self.registers[arg_regs[0]], &self.registers[arg_regs[1]]) { (Value::Number(min), Value::Number(max)) => { let min_i64 = min.to_i64().ok_or_else(|| { VMError::RuntimeError("rand() min must be an integer".to_string()) })?; let max_i64 = max.to_i64().ok_or_else(|| { VMError::RuntimeError("rand() max must be an integer".to_string()) })?; if min_i64 > max_i64 { return Err(VMError::RuntimeError( "rand() min must be <= max".to_string(), )); } let mut rng = rand::rng(); let result = rng.random_range(min_i64..=max_i64); self.registers[dest] = Value::Number(Decimal::from(result)); Ok(()) } _ => Err(VMError::RuntimeError( "rand() requires number arguments".to_string(), )), } } fn builtin_abs(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("abs", arg_regs, 1)?; let n = extract_number("abs", &self.registers[arg_regs[0]])?; self.registers[dest] = Value::Number(n.abs()); Ok(()) } fn builtin_min(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { if arg_regs.is_empty() { return Err(VMError::RuntimeError( "min() requires at least one argument".to_string(), )); } let mut result = extract_number("min", &self.registers[arg_regs[0]])?; for ® in &arg_regs[1..] { let n = extract_number("min", &self.registers[reg])?; if n < result { result = n; } } self.registers[dest] = Value::Number(result); Ok(()) } fn builtin_max(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { if arg_regs.is_empty() { return Err(VMError::RuntimeError( "max() requires at least one argument".to_string(), )); } let mut result = extract_number("max", &self.registers[arg_regs[0]])?; for ® in &arg_regs[1..] { let n = extract_number("max", &self.registers[reg])?; if n > result { result = n; } } self.registers[dest] = Value::Number(result); Ok(()) } fn builtin_floor(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("floor", arg_regs, 1)?; let n = extract_number("floor", &self.registers[arg_regs[0]])?; self.registers[dest] = Value::Number(n.floor()); Ok(()) } fn builtin_ceil(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("ceil", arg_regs, 1)?; let n = extract_number("ceil", &self.registers[arg_regs[0]])?; self.registers[dest] = Value::Number(n.ceil()); Ok(()) } fn builtin_round(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("round", arg_regs, 1)?; let n = extract_number("round", &self.registers[arg_regs[0]])?; // Optional second argument: decimal places (default 0) let places = if arg_regs.len() > 1 { extract_number("round", &self.registers[arg_regs[1]])? .to_u32() .unwrap_or(0) } else { 0 }; self.registers[dest] = Value::Number(n.round_dp(places)); Ok(()) } fn builtin_sqrt(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("sqrt", arg_regs, 1)?; let n = extract_number("sqrt", &self.registers[arg_regs[0]])?; if n.is_sign_negative() { return Err(VMError::RuntimeError( "sqrt() argument must be non-negative".to_string(), )); } let result = n.sqrt().ok_or_else(|| { VMError::RuntimeError("sqrt() failed to compute".to_string()) })?; self.registers[dest] = Value::Number(result); Ok(()) } fn builtin_len(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("len", arg_regs, 1)?; let len = match &self.registers[arg_regs[0]] { Value::String(s) => Decimal::from(s.len()), Value::StringList(l) => Decimal::from(l.len()), Value::NumberList(l) => Decimal::from(l.len()), Value::Object(m) => Decimal::from(m.len()), other => { return Err(VMError::RuntimeError(format!( "len() not supported for type {}", other.type_name() ))); } }; self.registers[dest] = Value::Number(len); Ok(()) } fn builtin_to_string(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("toString", arg_regs, 1)?; let s = super::vm::value_to_string(&self.registers[arg_regs[0]]); self.registers[dest] = Value::String(s.into_owned().into()); Ok(()) } fn builtin_to_number(&mut self, dest: usize, arg_regs: &[usize]) -> Result<(), VMError> { require_args("toNumber", arg_regs, 1)?; let result = match &self.registers[arg_regs[0]] { Value::Number(n) => *n, Value::String(s) => s.parse::().map_err(|_| { VMError::RuntimeError(format!("toNumber() cannot parse '{}'", s)) })?, Value::Boolean(b) => { if *b { Decimal::from(1) } else { Decimal::from(0) } } other => { return Err(VMError::RuntimeError(format!( "toNumber() not supported for type {}", other.type_name() ))); } }; self.registers[dest] = Value::Number(result); Ok(()) } } /// Helper: check minimum argument count fn require_args(name: &str, arg_regs: &[usize], min: usize) -> Result<(), VMError> { if arg_regs.len() < min { Err(VMError::RuntimeError(format!( "{}() requires at least {} argument(s)", name, min ))) } else { Ok(()) } } /// Helper: extract a Decimal from a Value or return an error fn extract_number(name: &str, val: &Value) -> Result { match val { Value::Number(n) => Ok(*n), other => Err(VMError::RuntimeError(format!( "{}() requires a number argument, got {}", name, other.type_name() ))), } }