reqlang_expr/
vm.rs

1//! The virtual machine and associated types
2
3use crate::{
4    compiler::{
5        CompileTimeEnv, ExprByteCode,
6        lookup::{BUILTIN, PROMPT, SECRET, VAR},
7        opcode,
8    },
9    errors::{ExprErrorS, ExprResult, RuntimeError},
10    prelude::lookup::{CLIENT_CTX, USER_BUILTIN},
11    value::Value,
12};
13
14#[derive(Debug, Clone, Default)]
15pub struct RuntimeEnv {
16    pub vars: Vec<String>,
17    pub prompts: Vec<String>,
18    pub secrets: Vec<String>,
19    pub client_context: Vec<Value>,
20}
21
22impl RuntimeEnv {
23    pub fn add_to_client_context(&mut self, index: usize, value: Value) {
24        if index < self.client_context.len() {
25            self.client_context[index] = value;
26        } else {
27            self.client_context.push(value);
28        }
29    }
30}
31
32#[derive(Debug)]
33pub struct Vm {
34    bytecode: Option<Box<ExprByteCode>>,
35    ip: usize,
36    stack: Vec<Value>,
37}
38
39impl Vm {
40    pub fn new() -> Self {
41        Self {
42            bytecode: None,
43            ip: 0,
44            stack: vec![],
45        }
46    }
47
48    pub fn interpret(
49        &mut self,
50        bytecode: Box<ExprByteCode>,
51        env: &CompileTimeEnv,
52        runtime_env: &RuntimeEnv,
53    ) -> ExprResult<Value> {
54        self.bytecode = Some(bytecode);
55        self.ip = 0;
56
57        let mut errs: Vec<ExprErrorS> = vec![];
58
59        while let Some(op_code) = self
60            .bytecode
61            .as_ref()
62            .and_then(|bc| bc.codes().get(self.ip))
63        {
64            if let Err(e) = self.interpret_op(env, runtime_env, *op_code) {
65                errs.extend(e);
66            }
67        }
68
69        if !errs.is_empty() {
70            return Err(errs.into());
71        }
72
73        self.stack_pop()
74    }
75
76    fn interpret_op(
77        &mut self,
78        env: &CompileTimeEnv,
79        runtime_env: &RuntimeEnv,
80        op_code: u8,
81    ) -> ExprResult<()> {
82        match op_code {
83            opcode::CALL => self.op_call(),
84            opcode::CONSTANT => self.op_constant(),
85            opcode::GET => self.op_get(env, runtime_env),
86            opcode::TRUE => self.op_true(),
87            opcode::FALSE => self.op_false(),
88            opcode::NOT => self.op_not(),
89            opcode::EQ => self.op_eq(),
90            opcode::TYPE => self.op_type(),
91            _ => panic!("Invalid OP code: {op_code}"),
92        }
93    }
94
95    fn op_call(&mut self) -> ExprResult<()> {
96        // Confirm the current op code is CALL
97        assert_eq!(opcode::CALL, self.read_u8(), "Expected CALL opcode");
98
99        let arg_count = self.read_u8() as usize;
100
101        let mut args: Vec<Value> = vec![];
102
103        for _ in 0..arg_count {
104            args.push(self.stack_pop()?);
105        }
106
107        args.reverse();
108
109        let value = self.stack_pop()?;
110
111        let builtin = value.get_func().func.clone();
112
113        let result = builtin(args);
114
115        self.stack_push(result);
116
117        Ok(())
118    }
119
120    fn op_get(&mut self, env: &CompileTimeEnv, runtime_env: &RuntimeEnv) -> ExprResult<()> {
121        assert_eq!(opcode::GET, self.read_u8(), "Expected GET opcode");
122        let get_lookup = self.read_u8();
123        let get_idx = self.read_u8() as usize;
124
125        match get_lookup {
126            BUILTIN => {
127                let value = env
128                    .get_builtin(get_idx)
129                    .unwrap_or_else(|| panic!("undefined builtin: {get_idx}"));
130                self.stack_push(Value::Fn(value.clone()));
131            }
132            USER_BUILTIN => {
133                let value = env
134                    .get_user_builtin(get_idx)
135                    .unwrap_or_else(|| panic!("undefined user builtin: {get_idx}"));
136                self.stack_push(Value::Fn(value.clone()));
137            }
138            VAR => {
139                let value = env
140                    .get_var(get_idx)
141                    .and_then(|_| runtime_env.vars.get(get_idx))
142                    .unwrap_or_else(|| panic!("undefined variable: {get_idx}"));
143
144                self.stack_push(Value::String(value.clone()));
145            }
146            PROMPT => {
147                let value = env
148                    .get_prompt(get_idx)
149                    .and_then(|_| runtime_env.prompts.get(get_idx))
150                    .unwrap_or_else(|| panic!("undefined prompt: {get_idx}"));
151
152                self.stack_push(Value::String(value.clone()));
153            }
154            SECRET => {
155                let value = env
156                    .get_secret(get_idx)
157                    .and_then(|_| runtime_env.secrets.get(get_idx))
158                    .unwrap_or_else(|| panic!("undefined secret: {get_idx}"));
159
160                self.stack_push(Value::String(value.clone()));
161            }
162            CLIENT_CTX => {
163                let value = env
164                    .get_client_context(get_idx)
165                    .and_then(|_| runtime_env.client_context.get(get_idx))
166                    .unwrap_or_else(|| panic!("undefined client context: {get_idx}"));
167
168                self.stack_push(value.clone());
169            }
170            _ => panic!("Invalid get lookup code: {}", get_lookup),
171        };
172
173        Ok(())
174    }
175
176    fn op_constant(&mut self) -> ExprResult<()> {
177        assert_eq!(opcode::CONSTANT, self.read_u8(), "Expected CONSTANT opcode");
178
179        let get_idx = self.read_u8() as usize;
180
181        let s = self
182            .bytecode
183            .as_ref()
184            .expect("should have bytecode")
185            .strings()
186            .get(get_idx)
187            .unwrap_or_else(|| panic!("undefined string: {}", get_idx));
188
189        self.stack_push(Value::String(s.clone()));
190
191        Ok(())
192    }
193
194    fn op_true(&mut self) -> ExprResult<()> {
195        assert_eq!(opcode::TRUE, self.read_u8(), "Expected TRUE opcode");
196
197        self.stack_push(Value::Bool(true));
198
199        Ok(())
200    }
201
202    fn op_false(&mut self) -> ExprResult<()> {
203        assert_eq!(opcode::FALSE, self.read_u8(), "Expected FALSE opcode");
204
205        self.stack_push(Value::Bool(false));
206
207        Ok(())
208    }
209
210    fn stack_push(&mut self, value: Value) {
211        self.stack.push(value);
212    }
213
214    fn stack_pop(&mut self) -> ExprResult<Value> {
215        if let Some(value) = self.stack.pop() {
216            return Ok(value);
217        };
218
219        Err(vec![(RuntimeError::EmptyStack.into(), 0..0)])
220    }
221
222    fn read_u8(&mut self) -> u8 {
223        let current_ip = self.ip as u8;
224
225        self.ip += 1;
226
227        *self
228            .bytecode
229            .as_ref()
230            .expect("should have bytecode")
231            .codes()
232            .get(current_ip as usize)
233            .expect("should have op in bytecode at {}")
234    }
235
236    fn op_not(&mut self) -> ExprResult<()> {
237        assert_eq!(opcode::NOT, self.read_u8(), "Expected NOT opcode");
238
239        let value = self.stack_pop()?;
240
241        self.stack_push(Value::Bool(!value.get_bool()));
242
243        Ok(())
244    }
245
246    fn op_eq(&mut self) -> ExprResult<()> {
247        assert_eq!(opcode::EQ, self.read_u8(), "Expected EQ opcode");
248
249        let value = self.stack_pop()?;
250        let value2 = self.stack_pop()?;
251
252        self.stack_push(Value::Bool(value == value2));
253
254        Ok(())
255    }
256
257    fn op_type(&mut self) -> ExprResult<()> {
258        assert_eq!(opcode::TYPE, self.read_u8(), "Expected TYPE opcode");
259
260        let value = self.stack_pop()?;
261
262        self.stack_push(Value::Type(value.get_type().into()));
263
264        Ok(())
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use crate::compiler::get_version_bytes;
271
272    use super::*;
273
274    #[test]
275    #[should_panic(expected = "Invalid OP code: 99")]
276    fn test_invalid_opcode_99() {
277        let mut vm = Vm::new();
278
279        let mut codes = get_version_bytes().to_vec();
280        codes.push(99);
281
282        let bytecode = Box::new(ExprByteCode::new(codes, vec![])); // 99 as invalid opcode
283        let env = CompileTimeEnv::default();
284        let runtime_env = RuntimeEnv::default();
285
286        // Attempt to interpret the bytecode, expecting a panic due to invalid opcode 99
287        let _ = vm.interpret(bytecode, &env, &runtime_env);
288    }
289
290    #[test]
291    #[should_panic(expected = "Invalid get lookup code: 99")]
292    fn test_invalid_look_99() {
293        let mut vm = Vm::new();
294
295        let mut codes = get_version_bytes().to_vec();
296        codes.push(opcode::GET);
297        codes.push(99);
298        codes.push(0);
299
300        let bytecode = Box::new(ExprByteCode::new(codes, vec![])); // 99 as invalid opcode
301        let env = CompileTimeEnv::default();
302        let runtime_env = RuntimeEnv::default();
303
304        // Attempt to interpret the bytecode, expecting a panic due to invalid opcode 99
305        let _ = vm.interpret(bytecode, &env, &runtime_env);
306    }
307}