pub trait VValUserData {
Show 16 methods fn clone_ud(&self) -> Box<dyn VValUserData>;
fn as_any(&mut self) -> &mut dyn Any; 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

Should clone your user data instance. Whether you are doing a deep clone or a shallow clone or something else is up to you.

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

This method should return a human readable syntax representation of your VValUserData.

If your data has a plain string representation, you can return the string directly from here.

Returns the i64 representation of your data.

Returns the byte representation of your data.

Returns the char representation of your data.

Returns the f64 representation of your data.

Returns the boolean representation of your data. Can for instance be used to check if your data is valid or something.

Allows you to specify how two instances of your data should be compared for equivalentness.

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.

This method is called when the user wants to remove a key. Typically called when std:delete from the prelude is called.

This method returns some value that your user data associates with the given key.

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().

This method is called when the user data is called. To quickly get the arguments you may use env.argv_ref().

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.

Trait Implementations

Formats the value using the given formatter. Read more

Implementors