use crate::interpreter::value::Value;
use anyhow::Result;
use std::collections::HashMap;
use rand::Rng;
pub type BuiltinFunction = (String, usize, Box<dyn Fn(&[Value]) -> Result<Value>>);
pub struct BuiltinRegistry {
functions: HashMap<String, BuiltinFunction>,
}
impl BuiltinRegistry {
pub fn new() -> Self {
Self {
functions: HashMap::new(),
}
}
pub fn register(&mut self, name: String, arity: usize, func: Box<dyn Fn(&[Value]) -> Result<Value>>) {
self.functions.insert(name.clone(), (name, arity, func));
}
pub fn get_function(&self, name: &str) -> Option<&BuiltinFunction> {
self.functions.get(name)
}
pub fn has_function(&self, name: &str) -> bool {
self.functions.contains_key(name)
}
pub fn function_names(&self) -> Vec<String> {
self.functions.keys().cloned().collect()
}
pub fn clear(&mut self) {
self.functions.clear();
}
}
pub struct Builtins;
impl Builtins {
pub fn register_builtins(registry: &mut BuiltinRegistry) {
registry.register(
"write".to_string(),
1,
Box::new(|args| Self::write(args)),
);
registry.register(
"writeln".to_string(),
1,
Box::new(|args| Self::writeln(args)),
);
registry.register(
"read".to_string(),
1,
Box::new(|args| Self::read(args)),
);
registry.register(
"readln".to_string(),
1,
Box::new(|args| Self::readln(args)),
);
registry.register(
"abs".to_string(),
1,
Box::new(|args| Self::abs(args)),
);
registry.register(
"sqrt".to_string(),
1,
Box::new(|args| Self::sqrt(args)),
);
registry.register(
"sin".to_string(),
1,
Box::new(|args| Self::sin(args)),
);
registry.register(
"cos".to_string(),
1,
Box::new(|args| Self::cos(args)),
);
registry.register(
"exp".to_string(),
1,
Box::new(|args| Self::exp(args)),
);
registry.register(
"ln".to_string(),
1,
Box::new(|args| Self::ln(args)),
);
registry.register(
"length".to_string(),
1,
Box::new(|args| Self::length(args)),
);
registry.register(
"copy".to_string(),
3,
Box::new(|args| Self::copy_str(args)),
);
registry.register(
"pos".to_string(),
2,
Box::new(|args| Self::pos(args)),
);
registry.register(
"str".to_string(),
1,
Box::new(|args| Self::str_fn(args)),
);
registry.register(
"ord".to_string(),
1,
Box::new(|args| Self::ord_fn(args)),
);
registry.register(
"chr".to_string(),
1,
Box::new(|args| Self::chr_fn(args)),
);
registry.register(
"low".to_string(),
1,
Box::new(|args| Self::low(args)),
);
registry.register(
"high".to_string(),
1,
Box::new(|args| Self::high(args)),
);
registry.register(
"__index__".to_string(),
2,
Box::new(|args| Self::__index__(args)),
);
registry.register(
"random".to_string(),
0,
Box::new(|args| Self::random(args)),
);
registry.register(
"randomize".to_string(),
0,
Box::new(|args| Self::randomize(args)),
);
registry.register(
"TypeName".to_string(),
1,
Box::new(|args| {
if args.is_empty() {
return Err(anyhow::anyhow!("TypeName expects one argument"));
}
Ok(Value::String(
crate::reflection::type_name(&args[0]).to_string(),
))
}),
);
}
fn write(args: &[Value]) -> Result<Value> {
match args[0] {
Value::String(ref s) => {
print!("{}", s);
Ok(Value::Nil)
},
Value::Integer(i) => {
print!("{}", i);
Ok(Value::Nil)
},
Value::Boolean(b) => {
print!("{}", b);
Ok(Value::Nil)
},
Value::Real(f) => {
print!("{}", f);
Ok(Value::Nil)
},
Value::Char(c) => {
print!("{}", c);
Ok(Value::Nil)
},
_ => Err(anyhow::anyhow!("write: invalid argument type")),
}
}
fn writeln(args: &[Value]) -> Result<Value> {
match args[0] {
Value::String(ref s) => {
println!("{}", s);
Ok(Value::Nil)
},
Value::Integer(i) => {
println!("{}", i);
Ok(Value::Nil)
},
Value::Boolean(b) => {
println!("{}", b);
Ok(Value::Nil)
},
Value::Real(f) => {
println!("{}", f);
Ok(Value::Nil)
},
Value::Char(c) => {
println!("{}", c);
Ok(Value::Nil)
},
_ => Err(anyhow::anyhow!("writeln: invalid argument type")),
}
}
fn read(_args: &[Value]) -> Result<Value> {
todo!("read: needs user input implementation")
}
fn readln(_args: &[Value]) -> Result<Value> {
todo!("readln: needs user input implementation")
}
fn abs(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Integer(i) => Ok(Value::Integer(i.abs())),
Value::Real(f) => Ok(Value::Real(f.abs())),
_ => Err(anyhow::anyhow!("abs: integer or float expected")),
}
}
fn sqrt(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Real(f) => Ok(Value::Real(f.sqrt())),
Value::Integer(i) => Ok(Value::Real((i as f64).sqrt())),
_ => Err(anyhow::anyhow!("sqrt: numeric expected")),
}
}
fn sin(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Real(f) => Ok(Value::Real(f.sin())),
Value::Integer(i) => Ok(Value::Real((i as f64).sin())),
_ => Err(anyhow::anyhow!("sin: numeric expected")),
}
}
fn cos(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Real(f) => Ok(Value::Real(f.cos())),
Value::Integer(i) => Ok(Value::Real((i as f64).cos())),
_ => Err(anyhow::anyhow!("cos: numeric expected")),
}
}
fn exp(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Real(f) => Ok(Value::Real(f.exp())),
Value::Integer(i) => Ok(Value::Real((i as f64).exp())),
_ => Err(anyhow::anyhow!("exp: numeric expected")),
}
}
fn ln(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Real(f) if f > 0.0 => Ok(Value::Real(f.ln())),
Value::Integer(i) if i > 0 => Ok(Value::Real((i as f64).ln())),
_ => Err(anyhow::anyhow!("ln: positive number expected")),
}
}
fn length(args: &[Value]) -> Result<Value> {
match &args[0] {
Value::String(s) => Ok(Value::Integer(s.len() as i64)),
Value::Array { elements: arr, .. } => Ok(Value::Integer(arr.len() as i64)),
_ => Err(anyhow::anyhow!("length: string or array expected")),
}
}
fn copy_str(args: &[Value]) -> Result<Value> {
match (&args[0], &args[1], &args[2]) {
(Value::String(s), Value::Integer(start), Value::Integer(count)) => {
let start = (*start as usize).saturating_sub(1);
let count = *count as usize;
if start >= s.len() {
Ok(Value::String("".to_string()))
} else {
let end = (start + count).min(s.len());
Ok(Value::String(s[start..end].to_string()))
}
},
_ => Err(anyhow::anyhow!("copy: string, integer, integer expected")),
}
}
fn pos(args: &[Value]) -> Result<Value> {
match (&args[0], &args[1]) {
(Value::String(sub), Value::String(s)) => {
let pos = s.find(sub).map(|p| p as i64 + 1).unwrap_or(0);
Ok(Value::Integer(pos))
},
_ => Err(anyhow::anyhow!("pos: string, string expected")),
}
}
fn str_fn(args: &[Value]) -> Result<Value> {
match &args[0] {
Value::Integer(i) => Ok(Value::String(i.to_string())),
Value::Real(f) => Ok(Value::String(f.to_string())),
Value::Boolean(b) => Ok(Value::String(b.to_string())),
Value::String(s) => Ok(Value::String(s.clone())),
_ => Err(anyhow::anyhow!("str: type cannot be converted to string")),
}
}
fn ord_fn(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Char(c) => Ok(Value::Integer(c as i64)),
Value::Boolean(b) => Ok(Value::Integer(if b { 1 } else { 0 })),
_ => Err(anyhow::anyhow!("ord: char or boolean expected")),
}
}
fn chr_fn(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Integer(i) if i >= 0 && i <= 255 => Ok(Value::Char(i as u8 as char)),
_ => Err(anyhow::anyhow!("chr: integer between 0-255 expected")),
}
}
fn low(args: &[Value]) -> Result<Value> {
match &args[0] {
Value::Array { elements: _arr, .. } => Ok(Value::Integer(0)), _ => Err(anyhow::anyhow!("low: array expected")),
}
}
fn high(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Array { elements: ref arr, .. } => Ok(Value::Integer((arr.len() - 1) as i64)),
_ => Err(anyhow::anyhow!("high: array expected")),
}
}
fn __index__(args: &[Value]) -> Result<Value> {
match (&args[0], &args[1]) {
(Value::Array { elements, lower_bound }, Value::Integer(idx)) => {
let adjusted = (idx - *lower_bound) as usize;
if adjusted >= elements.len() {
return Err(anyhow::anyhow!(
"Array index out of bounds: index {} (bounds: {}..{})",
idx, lower_bound, lower_bound + elements.len() as i64 - 1
));
}
Ok(elements[adjusted].clone())
},
(Value::String(s), Value::Integer(idx)) => {
let adjusted = (idx - 1) as usize; if adjusted >= s.len() {
return Err(anyhow::anyhow!(
"String index out of bounds: {} (length: {})", idx, s.len()
));
}
Ok(Value::Char(s.chars().nth(adjusted).unwrap_or('\0')))
},
_ => Err(anyhow::anyhow!("__index__: array/string and integer expected")),
}
}
fn random(args: &[Value]) -> Result<Value> {
match args[0] {
Value::Nil => {
let _rng = rand::thread_rng();
Ok(Value::Real(rand::random::<f64>()))
},
Value::Integer(n) if n > 0 => {
let mut rng = rand::thread_rng();
Ok(Value::Integer(rng.gen_range(0..n)))
},
_ => Err(anyhow::anyhow!("random: no argument or positive integer expected")),
}
}
fn randomize(_args: &[Value]) -> Result<Value> {
let _ = rand::thread_rng();
Ok(Value::Nil)
}
}
pub fn create_default_registry() -> BuiltinRegistry {
let mut registry = BuiltinRegistry::new();
Builtins::register_builtins(&mut registry);
registry
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builtin_registry() {
let mut registry = BuiltinRegistry::new();
registry.register("test".to_string(), 1, Box::new(|_| Ok(Value::Integer(42))));
assert!(registry.has_function("test"));
assert_eq!(registry.function_names(), vec!["test".to_string()]);
}
#[test]
fn test_builtins() {
let mut registry = create_default_registry();
if let Some((_, _, abs_func)) = registry.get_function("abs") {
let result = abs_func(&[Value::Integer(-5)]).unwrap();
assert_eq!(result, Value::Integer(5));
}
if let Some((_, _, sqrt_func)) = registry.get_function("sqrt") {
let result = sqrt_func(&[Value::Integer(4)]).unwrap();
assert_eq!(result, Value::Real(2.0));
}
if let Some((_, _, len_func)) = registry.get_function("length") {
let result = len_func(&[Value::Array {
elements: vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)],
lower_bound: 1
}]).unwrap();
assert_eq!(result, Value::Integer(3));
}
}
}