ogma_libs/vm/
script.rs

1//! Holds the execution Flow of the virtual machine
2
3use super::context::Context;
4use super::func::{Callable, Func};
5use super::trap::Trap;
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8
9/// A list of Functions
10#[derive(Default)]
11pub struct Script<'a> {
12    funcs: Vec<Func<'a>>,
13}
14
15/// The current state of the script instance
16#[derive(Default)]
17pub struct InstanceState {
18    ctx: Context,
19    pc: usize,
20}
21
22/// A executable reference to a Script with an internal state
23pub struct Instance<'s, 'a> {
24    script: &'s Script<'a>,
25    state: InstanceState,
26}
27
28impl<'a> Script<'a> {
29    /// Create a new empty Script
30    #[inline]
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Create an instance of a Script
36    #[inline]
37    pub fn instance(&self) -> Instance {
38        Instance {
39            script: self,
40            state: InstanceState::default(),
41        }
42    }
43
44    /// Add a new function to the end of the Script
45    #[inline]
46    pub fn push(&mut self, func: impl Callable + 'static) {
47        self.funcs.push(Box::new(func));
48    }
49}
50
51impl<'s, 'a> Instance<'s, 'a> {
52    /// Step one function down the script
53    #[inline]
54    pub fn step(&mut self) -> Result<(), Trap> {
55        self.cur_func()
56            .ok_or(Trap::ScriptOutOfBounds)?
57            .call(self.ctx_mut())?;
58        self.state.step();
59        Ok(())
60    }
61
62    /// Step through to the end of the script
63    #[inline]
64    pub fn exec(&mut self) -> Result<(), Trap> {
65        loop {
66            match self.step() {
67                Ok(()) => {}
68                Err(Trap::ScriptOutOfBounds) => return Ok(()),
69                Err(err) => return Err(err),
70            }
71        }
72    }
73
74    /// Reset to the intitial state
75    #[inline]
76    pub fn reset(&mut self) {
77        self.state.reset()
78    }
79
80    /// Get the instance context
81    #[inline]
82    pub fn ctx(&self) -> &Context {
83        self.state.ctx()
84    }
85
86    /// Get a mutable reference to the context
87    #[inline]
88    pub fn ctx_mut(&mut self) -> &mut Context {
89        self.state.ctx_mut()
90    }
91
92    #[allow(clippy::borrowed_box)]
93    #[inline]
94    fn cur_func(&self) -> Option<&'s Func<'a>> {
95        self.script.funcs.get(self.state.pc())
96    }
97}
98
99impl InstanceState {
100    #[inline]
101    fn step(&mut self) {
102        self.pc += 1;
103    }
104
105    #[inline]
106    fn pc(&self) -> usize {
107        self.pc
108    }
109
110    #[inline]
111    fn reset(&mut self) {
112        *self = Self::default()
113    }
114
115    #[inline]
116    fn ctx(&self) -> &Context {
117        &self.ctx
118    }
119
120    #[inline]
121    fn ctx_mut(&mut self) -> &mut Context {
122        &mut self.ctx
123    }
124}
125
126impl<'a> From<Vec<Func<'a>>> for Script<'a> {
127    fn from(funcs: Vec<Func<'a>>) -> Self {
128        Self { funcs }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use crate::vm::func::tests::Add;
136
137    #[test]
138    fn exec() {
139        let mut script = Script::new();
140        script.push(Add("a", "b"));
141        script.push(Add("c", "a"));
142        script.push(Add("c", "c"));
143        let mut instance = script.instance();
144        instance.ctx_mut().set_global::<_, i32>("a", 1);
145        instance.ctx_mut().set_global::<_, i32>("b", 1);
146        instance.exec().unwrap();
147        assert_eq!(instance.ctx().get_global::<_, i32>("c").unwrap(), Some(&6));
148    }
149}