pub trait VValUserData {
Show 16 methods
// Required methods
fn clone_ud(&self) -> Box<dyn VValUserData>;
fn as_any(&mut self) -> &mut dyn Any;
// Provided methods
fn s(&self) -> String { ... }
fn s_raw(&self) -> String { ... }
fn i(&self) -> i64 { ... }
fn byte(&self) -> u8 { ... }
fn c(&self) -> char { ... }
fn f(&self) -> f64 { ... }
fn b(&self) -> bool { ... }
fn eqv(&self, _other: &Box<dyn VValUserData>) -> bool { ... }
fn set_key(&self, _key: &VVal, _val: VVal) -> Result<(), StackAction> { ... }
fn delete_key(&self, _key: &VVal) -> Result<VVal, StackAction> { ... }
fn get_key(&self, _key: &str) -> Option<VVal> { ... }
fn call_method(
&self,
_key: &str,
_env: &mut Env,
) -> Result<VVal, StackAction> { ... }
fn call(&self, _env: &mut Env) -> Result<VVal, StackAction> { ... }
fn as_thread_safe_usr(&mut self) -> Option<Box<dyn ThreadSafeUsr>> { ... }
}
Expand description
You can implement your own VVal data type and provide it via global functions:
use std::rc::Rc;
use std::cell::RefCell;
use wlambda::vval::Env;
use wlambda::{VVal, GlobalEnv, StackAction};
#[derive(Clone, Debug)]
struct MyType {
x: Rc<RefCell<(i64, i64)>>,
}
impl wlambda::vval::VValUserData for MyType {
fn s(&self) -> String { format!("$<MyType({:?})>", self.x.borrow()) }
fn i(&self) -> i64 { self.x.borrow_mut().1 }
fn get_key(&self, key: &str) -> Option<VVal> {
Some(VVal::new_str(key))
}
fn call_method(&self, key: &str, env: &mut Env) -> Result<VVal, StackAction> {
let args = env.argv_ref();
match key {
"test" => Ok(VVal::Int(42)),
_ => Ok(VVal::err_msg(&format!("Unknown method called: {}", key))),
}
}
fn call(&self, env: &mut Env) -> Result<VVal, StackAction> {
let args = env.argv_ref();
if args.len() < 0 {
return Err(StackAction::panic_msg(
format!("{} called with too few arguments", self.s())));
}
Ok(args[0].clone())
}
fn as_any(&mut self) -> &mut dyn std::any::Any { self }
fn clone_ud(&self) -> Box<dyn wlambda::vval::VValUserData> {
Box::new(self.clone())
}
}
let global_env = GlobalEnv::new_default();
global_env.borrow_mut().add_func(
"new_mytype",
|_env: &mut Env, _argc: usize| {
Ok(VVal::new_usr(MyType { x: Rc::new(RefCell::new((13, 42))) }))
}, Some(0), Some(0));
global_env.borrow_mut().add_func(
"modify_mytype",
|env: &mut Env, _argc: usize| {
Ok(if let VVal::Usr(mut u) = env.arg(0) {
if let Some(ud) = u.as_any().downcast_mut::<MyType>() {
ud.x.borrow_mut().0 += 1;
ud.x.borrow_mut().1 *= 2;
VVal::Int(ud.x.borrow().0 + ud.x.borrow().1)
} else {
VVal::None
}
} else { VVal::None })
}, Some(1), Some(1));
let mut ctx = wlambda::compiler::EvalContext::new(global_env);
let r = &mut ctx.eval(r#"
!x = new_mytype[];
!i = modify_mytype x;
$[i, x]
"#).unwrap();
assert_eq!(
r.s(), "$[98,$<MyType((14, 84))>]", "Userdata implementation works");
Sometimes, if your UserData is a Rc<RefCell<…>>, it can pay off defining some wrapper and From/Into traits for easier handling:
Here an example from a game I worked on:
use wlambda::vval::{VVal, StackAction, VValUserData};
use std::rc::Rc;
use std::cell::RefCell;
#[derive(Clone)]
struct Ship { }
#[derive(Clone)]
struct ShipWlWrapper(Rc<RefCell<Ship>>);
impl From<Rc<RefCell<Ship>>> for ShipWlWrapper {
fn from(r: Rc<RefCell<Ship>>) -> ShipWlWrapper {
ShipWlWrapper(r)
}
}
impl Into<VVal> for ShipWlWrapper {
fn into(self) -> VVal { VVal::new_usr(self) }
}
impl VValUserData for ShipWlWrapper {
// ...
fn as_any(&mut self) -> &mut dyn std::any::Any { self }
fn clone_ud(&self) -> Box<dyn wlambda::vval::VValUserData> {
Box::new(self.clone())
}
}
Required Methods§
Sourcefn clone_ud(&self) -> Box<dyn VValUserData>
fn clone_ud(&self) -> Box<dyn VValUserData>
Should clone your user data instance. Whether you are doing a deep clone or a shallow clone or something else is up to you.
Sourcefn as_any(&mut self) -> &mut dyn Any
fn as_any(&mut self) -> &mut dyn Any
This should be implemented simply by returning a mutable reference to the concrete type self. It allows you to access your data structure from inside a function yourself.
This is a good default implementation for your struct/type:
fn as_any(&mut self) -> &mut dyn std::any::Any { self }
Provided Methods§
Sourcefn s(&self) -> String
fn s(&self) -> String
This method should return a human readable syntax representation of your VValUserData.
Sourcefn s_raw(&self) -> String
fn s_raw(&self) -> String
If your data has a plain string representation, you can return the string directly from here.
Sourcefn b(&self) -> bool
fn b(&self) -> bool
Returns the boolean representation of your data. Can for instance be used to check if your data is valid or something.
Sourcefn eqv(&self, _other: &Box<dyn VValUserData>) -> bool
fn eqv(&self, _other: &Box<dyn VValUserData>) -> bool
Allows you to specify how two instances of your data should be compared for equivalentness.
Sourcefn set_key(&self, _key: &VVal, _val: VVal) -> Result<(), StackAction>
fn set_key(&self, _key: &VVal, _val: VVal) -> Result<(), StackAction>
Makes your user data act like a map. This can be useful for implementing your own registries or data structures. Implement this method for setting a key to a value.
Sourcefn delete_key(&self, _key: &VVal) -> Result<VVal, StackAction>
fn delete_key(&self, _key: &VVal) -> Result<VVal, StackAction>
This method is called when the user wants to remove a key.
Typically called when std:delete
from the prelude is called.
Sourcefn get_key(&self, _key: &str) -> Option<VVal>
fn get_key(&self, _key: &str) -> Option<VVal>
This method returns some value that your user data associates with the given key.
Sourcefn call_method(&self, _key: &str, _env: &mut Env) -> Result<VVal, StackAction>
fn call_method(&self, _key: &str, _env: &mut Env) -> Result<VVal, StackAction>
This method is called, when the user data object is used in a method call directly.
Use this to implement convenient APIs for the user of the user data object.
To quickly get the arguments you may use env.argv_ref()
.
Sourcefn call(&self, _env: &mut Env) -> Result<VVal, StackAction>
fn call(&self, _env: &mut Env) -> Result<VVal, StackAction>
This method is called when the user data is called.
To quickly get the arguments you may use env.argv_ref()
.
Sourcefn as_thread_safe_usr(&mut self) -> Option<Box<dyn ThreadSafeUsr>>
fn as_thread_safe_usr(&mut self) -> Option<Box<dyn ThreadSafeUsr>>
This function is called when you try to pass a user data value between threads via the Atoms provided by the thread implementation of WLambda.
You need to return something that implements the ThreadSafeUsr
trait,
that handles transformation into and from an AVal
.