reqlang_expr/
compiler.rs

1//! The compiler and associated types
2
3use core::fmt;
4use std::rc::Rc;
5
6use crate::{ast::Expr, errors::ExprResult, vm::Value};
7
8pub mod opcode {
9    iota::iota! {
10        pub const
11        CALL: u8 = iota;,
12        GET,
13        CONSTANT,
14        TRUE,
15        FALSE
16    }
17}
18
19/// Types of lookups for the GET op code
20///
21/// Used at compile time to encode lookup indexes
22///
23/// Used at runtime to use lookup indexes to reference runtime values
24pub mod lookup {
25    iota::iota! {
26        pub const
27        BUILTIN: u8 = iota;,
28        VAR,
29        PROMPT,
30        SECRET,
31        USER_BUILTIN
32    }
33}
34
35/// Try to get a string from a list
36fn get(list: &Vec<String>, identifier: &str) -> Option<u8> {
37    list.into_iter()
38        .position(|x| x == identifier)
39        .map(|i| i as u8)
40}
41
42/// Builtin function used in expressions
43pub struct BuiltinFn {
44    // Needs to follow identifier naming rules
45    pub name: String,
46    // Number of arguments the function expects
47    pub arity: u8,
48    // Function used at runtime
49    pub func: Rc<dyn Fn(Vec<Value>) -> Value>,
50}
51
52impl PartialEq for BuiltinFn {
53    fn eq(&self, other: &Self) -> bool {
54        self.name == other.name && self.arity == other.arity
55    }
56}
57
58impl fmt::Debug for BuiltinFn {
59    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60        write!(f, "builtin {}({})", self.name, self.arity)
61    }
62}
63
64pub struct BuiltinFns;
65
66impl BuiltinFns {
67    pub fn id(args: Vec<Value>) -> Value {
68        let arg = args.first().unwrap();
69
70        arg.get_string().into()
71    }
72
73    pub fn noop(_: Vec<Value>) -> Value {
74        Value::String(String::from("noop"))
75    }
76
77    pub fn is_empty(args: Vec<Value>) -> Value {
78        let string_arg = args
79            .first()
80            .expect("should have string expression passed")
81            .get_string();
82
83        Value::Bool(string_arg.is_empty())
84    }
85
86    pub fn not(args: Vec<Value>) -> Value {
87        let bool_arg = args
88            .first()
89            .expect("should have boolean expression passed")
90            .get_bool();
91
92        Value::Bool(!bool_arg)
93    }
94
95    pub fn and(args: Vec<Value>) -> Value {
96        let a_arg = args
97            .first()
98            .expect("should have first expression passed")
99            .get_bool();
100        let b_arg = args
101            .get(1)
102            .expect("should have second expression passed")
103            .get_bool();
104
105        Value::Bool(a_arg && b_arg)
106    }
107
108    pub fn or(args: Vec<Value>) -> Value {
109        let a_arg = args
110            .first()
111            .expect("should have first expression passed")
112            .get_bool();
113        let b_arg = args
114            .get(1)
115            .expect("should have second expression passed")
116            .get_bool();
117
118        Value::Bool(a_arg || b_arg)
119    }
120
121    pub fn cond(args: Vec<Value>) -> Value {
122        let cond_arg = args
123            .first()
124            .expect("should have cond expression passed")
125            .get_bool();
126        let then_arg = args
127            .get(1)
128            .cloned()
129            .expect("should have then expression passed");
130        let else_arg = args
131            .get(2)
132            .cloned()
133            .expect("should have else expression passed");
134
135        if cond_arg { then_arg } else { else_arg }
136    }
137
138    pub fn to_str(args: Vec<Value>) -> Value {
139        let value_arg = args.first().expect("should have string expression passed");
140
141        match value_arg {
142            Value::String(_) => value_arg.clone(),
143            _ => Value::String(value_arg.to_string()),
144        }
145    }
146
147    pub fn concat(args: Vec<Value>) -> Value {
148        let mut result = String::new();
149
150        for arg in args {
151            let value = match arg {
152                Value::String(string) => string,
153                _ => arg.to_string(),
154            };
155
156            result.push_str(value.as_str());
157        }
158
159        Value::String(result)
160    }
161
162    pub fn contains(args: Vec<Value>) -> Value {
163        let needle_arg = args
164            .first()
165            .expect("should have first expression passed")
166            .get_string();
167        let haystack_arg = args
168            .get(1)
169            .expect("should have second expression passed")
170            .get_string();
171
172        Value::Bool(haystack_arg.contains(needle_arg))
173    }
174}
175
176#[derive(Debug)]
177pub struct Env {
178    builtins: Vec<Rc<BuiltinFn>>,
179    user_builtins: Vec<Rc<BuiltinFn>>,
180    vars: Vec<String>,
181    prompts: Vec<String>,
182    secrets: Vec<String>,
183}
184
185impl Default for Env {
186    fn default() -> Self {
187        Self {
188            builtins: vec![
189                Rc::new(BuiltinFn {
190                    name: String::from("id"),
191                    arity: 1,
192                    func: Rc::new(BuiltinFns::id),
193                }),
194                Rc::new(BuiltinFn {
195                    name: String::from("noop"),
196                    arity: 0,
197                    func: Rc::new(BuiltinFns::noop),
198                }),
199                Rc::new(BuiltinFn {
200                    name: String::from("is_empty"),
201                    arity: 1,
202                    func: Rc::new(BuiltinFns::is_empty),
203                }),
204                Rc::new(BuiltinFn {
205                    name: String::from("not"),
206                    arity: 1,
207                    func: Rc::new(BuiltinFns::not),
208                }),
209                Rc::new(BuiltinFn {
210                    name: String::from("and"),
211                    arity: 2,
212                    func: Rc::new(BuiltinFns::and),
213                }),
214                Rc::new(BuiltinFn {
215                    name: String::from("or"),
216                    arity: 2,
217                    func: Rc::new(BuiltinFns::or),
218                }),
219                Rc::new(BuiltinFn {
220                    name: String::from("cond"),
221                    arity: 3,
222                    func: Rc::new(BuiltinFns::cond),
223                }),
224                Rc::new(BuiltinFn {
225                    name: String::from("to_str"),
226                    arity: 1,
227                    func: Rc::new(BuiltinFns::to_str),
228                }),
229                Rc::new(BuiltinFn {
230                    name: String::from("concat"),
231                    arity: 10,
232                    func: Rc::new(BuiltinFns::concat),
233                }),
234                Rc::new(BuiltinFn {
235                    name: String::from("contains"),
236                    arity: 2,
237                    func: Rc::new(BuiltinFns::contains),
238                }),
239            ],
240            user_builtins: vec![],
241            vars: Vec::new(),
242            prompts: Vec::new(),
243            secrets: Vec::new(),
244        }
245    }
246}
247
248impl Env {
249    pub fn new(vars: Vec<String>, prompts: Vec<String>, secrets: Vec<String>) -> Self {
250        let mut env = Self::default();
251
252        env.vars = vars;
253        env.prompts = prompts;
254        env.secrets = secrets;
255
256        env
257    }
258
259    pub fn get_builtin_index(&self, name: &str) -> Option<(&Rc<BuiltinFn>, u8)> {
260        let index = self.builtins.iter().position(|x| x.name == name);
261
262        let result = index.map(|i| (self.builtins.get(i).unwrap(), i as u8));
263        result
264    }
265
266    pub fn get_user_builtin_index(&self, name: &str) -> Option<(&Rc<BuiltinFn>, u8)> {
267        let index = self.user_builtins.iter().position(|x| x.name == name);
268
269        let result = index.map(|i| (self.user_builtins.get(i).unwrap(), i as u8));
270        result
271    }
272
273    pub fn add_user_builtins(&mut self, builtins: Vec<Rc<BuiltinFn>>) {
274        for builtin in builtins {
275            self.add_user_builtin(builtin);
276        }
277    }
278
279    pub fn add_user_builtin(&mut self, builtin: Rc<BuiltinFn>) {
280        self.user_builtins.push(builtin);
281    }
282
283    pub fn get_builtin(&self, index: usize) -> Option<&Rc<BuiltinFn>> {
284        self.builtins.get(index)
285    }
286
287    pub fn get_user_builtin(&self, index: usize) -> Option<&Rc<BuiltinFn>> {
288        self.user_builtins.get(index)
289    }
290
291    pub fn get_var(&self, index: usize) -> Option<&String> {
292        self.vars.get(index)
293    }
294
295    pub fn get_prompt(&self, index: usize) -> Option<&String> {
296        self.prompts.get(index)
297    }
298
299    pub fn get_secret(&self, index: usize) -> Option<&String> {
300        self.secrets.get(index)
301    }
302}
303
304/// The compiled bytecode for an expression
305#[derive(Debug, Clone)]
306pub struct ExprByteCode {
307    codes: Vec<u8>,
308    strings: Vec<String>,
309}
310
311impl ExprByteCode {
312    pub fn new(codes: Vec<u8>, strings: Vec<String>) -> Self {
313        Self { codes, strings }
314    }
315
316    pub fn codes(&self) -> &[u8] {
317        &self.codes
318    }
319
320    pub fn strings(&self) -> &[String] {
321        &self.strings
322    }
323}
324
325/// Compile an [`ast::Expr`] into [`ExprByteCode`]
326pub fn compile(expr: &Expr, env: &Env) -> ExprResult<ExprByteCode> {
327    let mut strings: Vec<String> = vec![];
328    let codes = compile_expr(expr, env, &mut strings)?;
329    Ok(ExprByteCode::new(codes, strings))
330}
331
332fn compile_expr(expr: &Expr, env: &Env, strings: &mut Vec<String>) -> ExprResult<Vec<u8>> {
333    use opcode::*;
334
335    let mut codes = vec![];
336
337    match expr {
338        Expr::String(string) => {
339            if let Some(index) = strings.iter().position(|x| x == &string.0) {
340                codes.push(CONSTANT);
341                codes.push(index as u8);
342            } else {
343                strings.push(string.0.clone());
344                let index = strings.len() - 1;
345                codes.push(CONSTANT);
346                codes.push(index as u8);
347            }
348        }
349        Expr::Identifier(identifier) => {
350            let identifier_name = identifier.0.as_str();
351
352            if let Some((_, index)) = env.get_builtin_index(identifier_name) {
353                codes.push(GET);
354                codes.push(lookup::BUILTIN);
355                codes.push(index);
356            } else if let Some((_, index)) = env.get_user_builtin_index(identifier_name) {
357                codes.push(GET);
358                codes.push(lookup::USER_BUILTIN);
359                codes.push(index);
360            } else {
361                let identifier_prefix = &identifier_name[..1];
362                let identifier_suffix = &identifier_name[1..];
363
364                match identifier_prefix {
365                    "?" => {
366                        if let Some(index) = get(&env.prompts, identifier_suffix) {
367                            codes.push(GET);
368                            codes.push(lookup::PROMPT);
369                            codes.push(index);
370                        }
371                    }
372                    "!" => {
373                        if let Some(index) = get(&env.secrets, identifier_suffix) {
374                            codes.push(GET);
375                            codes.push(lookup::SECRET);
376                            codes.push(index);
377                        }
378                    }
379                    ":" => {
380                        if let Some(index) = get(&env.vars, identifier_suffix) {
381                            codes.push(GET);
382                            codes.push(lookup::VAR);
383                            codes.push(index);
384                        }
385                    }
386                    _ => {}
387                };
388            }
389        }
390        Expr::Call(expr_call) => {
391            let callee_bytecode = compile_expr(&expr_call.callee.0, env, strings)?;
392
393            codes.extend(callee_bytecode);
394
395            for arg in expr_call.args.iter() {
396                let arg_bytecode = compile_expr(&arg.0, env, strings)?;
397
398                codes.extend(arg_bytecode);
399            }
400
401            codes.push(opcode::CALL);
402            codes.push(expr_call.args.len() as u8);
403        }
404        Expr::Bool(value) => match value.0 {
405            true => {
406                codes.push(opcode::TRUE);
407            }
408            false => {
409                codes.push(opcode::FALSE);
410            }
411        },
412    }
413
414    Ok(codes)
415}
416
417#[cfg(test)]
418mod value_tests {
419    use super::*;
420
421    #[test]
422    fn test_builtins_debug_0_arity() {
423        assert_eq!(
424            "builtin test_builtin(0)",
425            format!(
426                "{:#?}",
427                BuiltinFn {
428                    name: "test_builtin".to_string(),
429                    arity: 0,
430                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
431                }
432            )
433        )
434    }
435
436    #[test]
437    fn test_builtins_debug_1_arity() {
438        assert_eq!(
439            "builtin test_builtin(1)",
440            format!(
441                "{:#?}",
442                BuiltinFn {
443                    name: "test_builtin".to_string(),
444                    arity: 1,
445                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
446                }
447            )
448        )
449    }
450
451    #[test]
452    fn test_builtins_debug_2_arity() {
453        assert_eq!(
454            "builtin test_builtin(2)",
455            format!(
456                "{:#?}",
457                BuiltinFn {
458                    name: "test_builtin".to_string(),
459                    arity: 2,
460                    func: Rc::new(|_| { Value::String("test_builtin".to_string()) })
461                }
462            )
463        )
464    }
465}