1use super::context::Context;
4use super::func::{Callable, Func};
5use super::trap::Trap;
6use alloc::boxed::Box;
7use alloc::vec::Vec;
8
9#[derive(Default)]
11pub struct Script<'a> {
12 funcs: Vec<Func<'a>>,
13}
14
15#[derive(Default)]
17pub struct InstanceState {
18 ctx: Context,
19 pc: usize,
20}
21
22pub struct Instance<'s, 'a> {
24 script: &'s Script<'a>,
25 state: InstanceState,
26}
27
28impl<'a> Script<'a> {
29 #[inline]
31 pub fn new() -> Self {
32 Self::default()
33 }
34
35 #[inline]
37 pub fn instance(&self) -> Instance {
38 Instance {
39 script: self,
40 state: InstanceState::default(),
41 }
42 }
43
44 #[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 #[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 #[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 #[inline]
76 pub fn reset(&mut self) {
77 self.state.reset()
78 }
79
80 #[inline]
82 pub fn ctx(&self) -> &Context {
83 self.state.ctx()
84 }
85
86 #[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}