1use 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
19pub mod lookup {
25 iota::iota! {
26 pub const
27 BUILTIN: u8 = iota;,
28 VAR,
29 PROMPT,
30 SECRET,
31 USER_BUILTIN
32 }
33}
34
35fn 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
42pub struct BuiltinFn {
44 pub name: String,
46 pub arity: u8,
48 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#[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
325pub 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}