Trait VValUserData

Source
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§

Source

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.

Source

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§

Source

fn s(&self) -> String

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

Source

fn s_raw(&self) -> String

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

Source

fn i(&self) -> i64

Returns the i64 representation of your data.

Source

fn byte(&self) -> u8

Returns the byte representation of your data.

Source

fn c(&self) -> char

Returns the char representation of your data.

Source

fn f(&self) -> f64

Returns the f64 representation of your data.

Source

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.

Source

fn eqv(&self, _other: &Box<dyn VValUserData>) -> bool

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

Source

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.

Source

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.

Source

fn get_key(&self, _key: &str) -> Option<VVal>

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

Source

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

Source

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

Source

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.

Trait Implementations§

Source§

impl Clone for Box<dyn VValUserData>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for dyn VValUserData

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Implementors§