use crate::error::{LambdustError, Result};
use crate::evaluator::Evaluator;
use crate::value::{Value, Procedure};
use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub trait ToScheme {
fn to_scheme(&self) -> Result<Value>;
}
pub trait FromScheme: Sized {
fn from_scheme(value: &Value) -> Result<Self>;
}
pub trait Callable: Send + Sync {
fn call(&self, args: &[Value]) -> Result<Value>;
fn arity(&self) -> Option<usize>;
fn name(&self) -> &str;
}
#[derive(Debug, Clone)]
pub struct ExternalObject {
pub id: u64,
pub type_name: String,
pub data: Arc<dyn Any + Send + Sync>,
}
impl PartialEq for ExternalObject {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.type_name == other.type_name
}
}
impl PartialEq for dyn Callable {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name()
}
}
pub struct ObjectRegistry {
next_id: u64,
objects: HashMap<u64, ExternalObject>,
functions: HashMap<String, Arc<dyn Callable>>,
converters: HashMap<String, fn(&dyn Any) -> Result<Value>>,
}
impl std::fmt::Debug for ObjectRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ObjectRegistry")
.field("next_id", &self.next_id)
.field("objects", &self.objects)
.field("functions", &self.functions.keys().collect::<Vec<_>>())
.finish()
}
}
impl ObjectRegistry {
pub fn new() -> Self {
ObjectRegistry {
next_id: 1,
objects: HashMap::new(),
functions: HashMap::new(),
converters: HashMap::new(),
}
}
pub fn register_object<T: Any + Send + Sync>(&mut self, obj: T, type_name: &str) -> u64 {
let id = self.next_id;
self.next_id += 1;
let external_obj = ExternalObject {
id,
type_name: type_name.to_string(),
data: Arc::new(obj),
};
self.objects.insert(id, external_obj);
id
}
pub fn register_function(&mut self, name: &str, func: Arc<dyn Callable>) {
self.functions.insert(name.to_string(), func);
}
pub fn register_converter<T: Any + Send + Sync>(
&mut self,
type_name: &str,
_converter: fn(&T) -> Result<Value>,
) {
let dummy_converter = |_any_obj: &dyn Any| -> Result<Value> {
Err(LambdustError::TypeError("Type converter not implemented".to_string()))
};
self.converters.insert(type_name.to_string(), dummy_converter);
}
pub fn get_object(&self, id: u64) -> Option<&ExternalObject> {
self.objects.get(&id)
}
pub fn get_function(&self, name: &str) -> Option<&Arc<dyn Callable>> {
self.functions.get(name)
}
pub fn object_to_value(&self, obj: &ExternalObject) -> Result<Value> {
if let Some(converter) = self.converters.get(&obj.type_name) {
converter(obj.data.as_ref())
} else {
Ok(Value::External(obj.clone()))
}
}
}
impl Default for ObjectRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct LambdustBridge {
pub evaluator: Evaluator,
pub registry: Arc<Mutex<ObjectRegistry>>,
}
impl LambdustBridge {
pub fn new() -> Self {
let mut evaluator = Evaluator::new();
let registry = Arc::new(Mutex::new(ObjectRegistry::new()));
Self::add_bridge_functions(&mut evaluator, registry.clone());
LambdustBridge {
evaluator,
registry,
}
}
fn add_bridge_functions(evaluator: &mut Evaluator, _registry: Arc<Mutex<ObjectRegistry>>) {
let global_env = evaluator.global_env.clone();
global_env.define("call-external".to_string(), Value::Procedure(Procedure::Builtin {
name: "call-external".to_string(),
arity: None, func: |_args| {
Err(LambdustError::RuntimeError("call-external not implemented yet".to_string()))
},
}));
global_env.define("get-property".to_string(), Value::Procedure(Procedure::Builtin {
name: "get-property".to_string(),
arity: Some(2),
func: |_args| {
Err(LambdustError::RuntimeError("get-property not implemented yet".to_string()))
},
}));
global_env.define("set-property!".to_string(), Value::Procedure(Procedure::Builtin {
name: "set-property!".to_string(),
arity: Some(3),
func: |_args| {
Err(LambdustError::RuntimeError("set-property! not implemented yet".to_string()))
},
}));
}
pub fn register_object<T: Any + Send + Sync>(&mut self, obj: T, type_name: &str) -> u64 {
self.registry.lock().unwrap().register_object(obj, type_name)
}
pub fn register_function<F>(&mut self, name: &str, arity: Option<usize>, func: F)
where
F: Fn(&[Value]) -> Result<Value> + Send + Sync + 'static,
{
let callable = CallableFunction {
name: name.to_string(),
arity,
func: Box::new(func),
};
self.registry.lock().unwrap().register_function(name, Arc::new(callable));
}
pub fn eval(&mut self, code: &str) -> Result<Value> {
self.evaluator.eval(crate::parser::parse(crate::lexer::tokenize(code)?)?)
}
pub fn load_file(&mut self, path: &str) -> Result<Value> {
let content = std::fs::read_to_string(path)
.map_err(|e| LambdustError::IoError(e.to_string()))?;
self.eval(&content)
}
pub fn define(&mut self, name: &str, value: Value) {
self.evaluator.global_env.define(name.to_string(), value);
}
}
impl Default for LambdustBridge {
fn default() -> Self {
Self::new()
}
}
struct CallableFunction {
name: String,
arity: Option<usize>,
func: Box<dyn Fn(&[Value]) -> Result<Value> + Send + Sync>,
}
impl Callable for CallableFunction {
fn call(&self, args: &[Value]) -> Result<Value> {
if let Some(expected_arity) = self.arity {
if args.len() != expected_arity {
return Err(LambdustError::ArityError {
expected: expected_arity,
actual: args.len(),
});
}
}
(self.func)(args)
}
fn arity(&self) -> Option<usize> {
self.arity
}
fn name(&self) -> &str {
&self.name
}
}
impl std::fmt::Debug for CallableFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CallableFunction")
.field("name", &self.name)
.field("arity", &self.arity)
.finish()
}
}
impl ToScheme for i32 {
fn to_scheme(&self) -> Result<Value> {
Ok(Value::from(*self as i64))
}
}
impl ToScheme for i64 {
fn to_scheme(&self) -> Result<Value> {
Ok(Value::from(*self))
}
}
impl ToScheme for f64 {
fn to_scheme(&self) -> Result<Value> {
Ok(Value::from(*self))
}
}
impl ToScheme for bool {
fn to_scheme(&self) -> Result<Value> {
Ok(Value::from(*self))
}
}
impl ToScheme for String {
fn to_scheme(&self) -> Result<Value> {
Ok(Value::from(self.clone()))
}
}
impl ToScheme for &str {
fn to_scheme(&self) -> Result<Value> {
Ok(Value::from(*self))
}
}
impl FromScheme for i64 {
fn from_scheme(value: &Value) -> Result<Self> {
match value {
Value::Number(n) => match n {
crate::lexer::SchemeNumber::Integer(i) => Ok(*i),
crate::lexer::SchemeNumber::Real(r) => Ok(*r as i64),
_ => Err(LambdustError::TypeError("Cannot convert to i64".to_string())),
},
_ => Err(LambdustError::TypeError("Expected number".to_string())),
}
}
}
impl FromScheme for f64 {
fn from_scheme(value: &Value) -> Result<Self> {
match value {
Value::Number(n) => match n {
crate::lexer::SchemeNumber::Integer(i) => Ok(*i as f64),
crate::lexer::SchemeNumber::Real(r) => Ok(*r),
_ => Err(LambdustError::TypeError("Cannot convert to f64".to_string())),
},
_ => Err(LambdustError::TypeError("Expected number".to_string())),
}
}
}
impl FromScheme for bool {
fn from_scheme(value: &Value) -> Result<Self> {
Ok(value.is_truthy())
}
}
impl FromScheme for String {
fn from_scheme(value: &Value) -> Result<Self> {
match value {
Value::String(s) => Ok(s.clone()),
Value::Symbol(s) => Ok(s.clone()),
_ => Err(LambdustError::TypeError("Expected string or symbol".to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bridge_creation() {
let bridge = LambdustBridge::new();
assert!(bridge.registry.lock().unwrap().objects.is_empty());
assert!(bridge.registry.lock().unwrap().functions.is_empty());
}
#[test]
fn test_register_function() {
let mut bridge = LambdustBridge::new();
bridge.register_function("add", Some(2), |args| {
let a = i64::from_scheme(&args[0])?;
let b = i64::from_scheme(&args[1])?;
(a + b).to_scheme()
});
assert!(bridge.registry.lock().unwrap().functions.contains_key("add"));
}
#[test]
fn test_type_conversion() {
assert_eq!(42i64.to_scheme().unwrap(), Value::from(42i64));
assert_eq!(3.14f64.to_scheme().unwrap(), Value::from(3.14f64));
assert_eq!(true.to_scheme().unwrap(), Value::from(true));
assert_eq!("hello".to_scheme().unwrap(), Value::from("hello"));
let value = Value::from(42i64);
assert_eq!(i64::from_scheme(&value).unwrap(), 42i64);
let value = Value::from(true);
assert_eq!(bool::from_scheme(&value).unwrap(), true);
}
#[test]
fn test_define_variable() {
let mut bridge = LambdustBridge::new();
bridge.define("my-var", Value::from(100i64));
let result = bridge.eval("my-var").unwrap();
assert_eq!(result, Value::from(100i64));
}
}