reqlang_expr/
vm.rs

1//! The virtual machine and associated types
2
3use std::{fmt::Display, rc::Rc};
4
5use crate::{
6    compiler::{
7        BuiltinFn, Env, ExprByteCode,
8        lookup::{BUILTIN, PROMPT, SECRET, VAR},
9        opcode,
10    },
11    errors::ExprResult,
12    prelude::lookup::USER_BUILTIN,
13};
14
15#[derive(Debug, Clone, PartialEq)]
16pub enum Value {
17    String(String),
18    Fn(Rc<BuiltinFn>),
19    Bool(bool),
20}
21
22impl Value {
23    pub fn get_string(&self) -> &str {
24        match self {
25            Value::String(s) => s.as_str(),
26            _ => panic!("Value is not a string"),
27        }
28    }
29
30    pub fn get_func(&self) -> Rc<BuiltinFn> {
31        match self {
32            Value::Fn(f) => f.clone(),
33            _ => panic!("Value is not a function"),
34        }
35    }
36
37    pub fn get_bool(&self) -> bool {
38        match self {
39            Value::Bool(s) => s.clone(),
40            _ => panic!("Value is not a string"),
41        }
42    }
43}
44
45impl From<&str> for Value {
46    fn from(s: &str) -> Self {
47        Value::String(s.to_string())
48    }
49}
50
51impl Display for Value {
52    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53        match self {
54            Value::String(string) => write!(f, "`{}`", string),
55            Value::Fn(builtin) => write!(f, "{builtin:?}"),
56            Value::Bool(value) => write!(f, "{}", value),
57        }
58    }
59}
60
61#[derive(Debug, Clone, Default)]
62pub struct RuntimeEnv {
63    pub vars: Vec<String>,
64    pub prompts: Vec<String>,
65    pub secrets: Vec<String>,
66}
67
68#[derive(Debug)]
69pub struct Vm {
70    bytecode: Option<Box<ExprByteCode>>,
71    ip: usize,
72    stack: Vec<Value>,
73}
74
75impl Vm {
76    pub fn new() -> Self {
77        Self {
78            bytecode: None,
79            ip: 0,
80            stack: vec![],
81        }
82    }
83
84    pub fn interpret(
85        &mut self,
86        bytecode: Box<ExprByteCode>,
87        env: &Env,
88        runtime_env: &RuntimeEnv,
89    ) -> ExprResult<Value> {
90        self.bytecode = Some(bytecode.into());
91        self.ip = 0;
92
93        while let Some(op_code) = self
94            .bytecode
95            .as_ref()
96            .and_then(|bc| bc.codes().get(self.ip))
97        {
98            self.interpret_op(env, runtime_env, *op_code);
99        }
100
101        assert_eq!(1, self.stack.len());
102
103        let value = self.stack_pop();
104
105        Ok(value)
106    }
107
108    fn interpret_op(&mut self, env: &Env, runtime_env: &RuntimeEnv, op_code: u8) {
109        match op_code {
110            opcode::CALL => self.op_call(),
111            opcode::CONSTANT => self.op_constant(),
112            opcode::GET => self.op_get(env, &runtime_env),
113            opcode::TRUE => self.op_true(),
114            opcode::FALSE => self.op_false(),
115            _ => panic!("Invalid OP code: {op_code}"),
116        }
117    }
118
119    fn op_call(&mut self) {
120        // Confirm the current op code is CALL
121        assert_eq!(opcode::CALL, self.read_u8(), "Expected CALL opcode");
122
123        let arg_count = self.read_u8() as usize;
124
125        let mut args: Vec<Value> = vec![];
126
127        for _ in 0..arg_count {
128            args.push(self.stack_pop());
129        }
130
131        args.reverse();
132
133        let value = self.stack_pop();
134
135        let builtin = value.get_func().func.clone();
136
137        let result = builtin(args);
138
139        self.stack_push(result);
140    }
141
142    fn op_get(&mut self, env: &Env, runtime_env: &RuntimeEnv) {
143        assert_eq!(opcode::GET, self.read_u8(), "Expected GET opcode");
144        let get_lookup = self.read_u8();
145        let get_idx = self.read_u8() as usize;
146
147        match get_lookup {
148            BUILTIN => {
149                let value = env
150                    .get_builtin(get_idx)
151                    .expect(&format! {"undefined builtin: {get_idx}"});
152                self.stack_push(Value::Fn(value.clone()));
153            }
154            USER_BUILTIN => {
155                let value = env
156                    .get_user_builtin(get_idx)
157                    .expect(&format! {"undefined user builtin: {get_idx}"});
158                self.stack_push(Value::Fn(value.clone()));
159            }
160            VAR => {
161                let value = env
162                    .get_var(get_idx)
163                    .and_then(|_| runtime_env.vars.get(get_idx))
164                    .expect(&format! {"undefined variable: {get_idx}"});
165
166                self.stack_push(Value::String(value.clone()));
167            }
168            PROMPT => {
169                let value = env
170                    .get_prompt(get_idx)
171                    .and_then(|_| runtime_env.prompts.get(get_idx))
172                    .expect(&format! {"undefined prompt: {get_idx}"});
173
174                self.stack_push(Value::String(value.clone()));
175            }
176            SECRET => {
177                let value = env
178                    .get_secret(get_idx)
179                    .and_then(|_| runtime_env.secrets.get(get_idx))
180                    .expect(&format! {"undefined secret: {get_idx}"});
181
182                self.stack_push(Value::String(value.clone()));
183            }
184            _ => panic!("invalid get lookup code: {}", get_lookup),
185        };
186    }
187
188    fn op_constant(&mut self) {
189        assert_eq!(opcode::CONSTANT, self.read_u8(), "Expected CONSTANT opcode");
190
191        let get_idx = self.read_u8() as usize;
192
193        let s = self
194            .bytecode
195            .as_ref()
196            .expect("should have bytecode")
197            .strings()
198            .get(get_idx)
199            .expect(&format!("undefined string: {}", get_idx));
200
201        self.stack_push(Value::String(s.clone()));
202    }
203
204    fn op_true(&mut self) {
205        assert_eq!(opcode::TRUE, self.read_u8(), "Expected TRUE opcode");
206
207        self.stack_push(Value::Bool(true));
208    }
209
210    fn op_false(&mut self) {
211        assert_eq!(opcode::FALSE, self.read_u8(), "Expected FALSE opcode");
212
213        self.stack_push(Value::Bool(false));
214    }
215
216    fn stack_push(&mut self, value: Value) {
217        self.stack.push(value);
218    }
219
220    fn stack_pop(&mut self) -> Value {
221        let value = self
222            .stack
223            .pop()
224            .expect("should have a value to pop from the stack");
225
226        value
227    }
228
229    fn read_u8(&mut self) -> u8 {
230        let current_ip = (self.ip as u8).clone();
231
232        self.ip += 1;
233
234        self.bytecode
235            .as_ref()
236            .expect("should have bytecode")
237            .codes()
238            .get(current_ip as usize)
239            .expect("should have op in bytecode at {}")
240            .clone()
241    }
242}