Skip to main content

zen_expression/vm/
vm.rs

1use crate::compiler::{Compare, FetchFastTarget, Jump, Opcode};
2use crate::functions::arguments::Arguments;
3use crate::functions::registry::FunctionRegistry;
4use crate::functions::{internal, MethodRegistry};
5use crate::scope::Scope;
6use crate::variable::Variable;
7use crate::variable::Variable::*;
8use crate::vm::date::DynamicVariableExt;
9use crate::vm::error::VMError::*;
10use crate::vm::error::VMResult;
11use crate::vm::interval::{VmInterval, VmIntervalData};
12use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
13use rust_decimal::{Decimal, MathematicalOps};
14use std::rc::Rc;
15use std::string::String as StdString;
16use zen_types::symbol::Symbol;
17
18#[derive(Debug)]
19pub struct LoopScope {
20    array: Variable,
21    len: usize,
22    iter: usize,
23    count: usize,
24}
25
26#[derive(Debug)]
27pub struct VM {
28    scopes: Vec<LoopScope>,
29    stack: Vec<Variable>,
30}
31
32impl VM {
33    pub fn new() -> Self {
34        Self {
35            scopes: Default::default(),
36            stack: Default::default(),
37        }
38    }
39
40    pub fn run(&mut self, bytecode: &[Opcode], scope: &Scope) -> VMResult<Variable> {
41        self.stack.clear();
42        self.scopes.clear();
43
44        let s = VMInner::new(bytecode, &mut self.stack, &mut self.scopes).run(scope);
45        Ok(s?)
46    }
47}
48
49struct VMInner<'parent_ref, 'bytecode_ref> {
50    scopes: &'parent_ref mut Vec<LoopScope>,
51    stack: &'parent_ref mut Vec<Variable>,
52    bytecode: &'bytecode_ref [Opcode],
53    ip: u32,
54}
55
56impl<'arena, 'parent_ref, 'bytecode_ref> VMInner<'parent_ref, 'bytecode_ref> {
57    pub fn new(
58        bytecode: &'bytecode_ref [Opcode],
59        stack: &'parent_ref mut Vec<Variable>,
60        scopes: &'parent_ref mut Vec<LoopScope>,
61    ) -> Self {
62        Self {
63            ip: 0,
64            scopes,
65            stack,
66            bytecode,
67        }
68    }
69
70    fn push(&mut self, var: Variable) {
71        self.stack.push(var);
72    }
73
74    fn pop(&mut self) -> VMResult<Variable> {
75        self.stack.pop().ok_or_else(|| StackOutOfBounds {
76            stack: format!("{:?}", self.stack),
77        })
78    }
79
80    pub fn run(&mut self, root_scope: &Scope) -> VMResult<Variable> {
81        let mut env = root_scope.clone();
82        let mut assigned_objects: Vec<Variable> = Vec::new();
83        if self.ip != 0 {
84            self.ip = 0;
85        }
86
87        while self.ip < self.bytecode.len() as u32 {
88            let op = self
89                .bytecode
90                .get(self.ip as usize)
91                .ok_or_else(|| OpcodeOutOfBounds {
92                    bytecode: format!("{:?}", self.bytecode),
93                    index: self.ip as usize,
94                })?;
95
96            self.ip += 1;
97
98            match op {
99                Opcode::PushNull => self.push(Null),
100                Opcode::PushBool(b) => self.push(Bool(*b)),
101                Opcode::PushNumber(n) => self.push(Number(*n)),
102                Opcode::PushString(s) => self.push(String((s.as_ref()).into())),
103                Opcode::Pop => {
104                    self.pop()?;
105                }
106                Opcode::Fetch => {
107                    let b = self.pop()?;
108                    let a = self.pop()?;
109
110                    match (a, b) {
111                        (Object(o), String(s)) => {
112                            let obj = o.borrow();
113                            self.push(obj.get_str(s.as_str()).cloned().unwrap_or(Null));
114                        }
115                        (Array(a), Number(n)) => {
116                            let arr = a.borrow();
117                            self.push(
118                                arr.get(n.to_usize().ok_or_else(|| OpcodeErr {
119                                    opcode: "Fetch".into(),
120                                    message: "Failed to convert to usize".into(),
121                                })?)
122                                .cloned()
123                                .unwrap_or(Null),
124                            )
125                        }
126                        (String(str), Number(n)) => {
127                            let index = n.to_usize().ok_or_else(|| OpcodeErr {
128                                opcode: "Fetch".into(),
129                                message: "Failed to convert to usize".into(),
130                            })?;
131
132                            let slice = index.checked_add(1).and_then(|end| str.get(index..end));
133                            if let Some(slice) = slice {
134                                self.push(String((slice).into()));
135                            } else {
136                                self.push(Null)
137                            };
138                        }
139                        _ => self.push(Null),
140                    }
141                }
142                Opcode::FetchFast(path) => {
143                    let mut steps = path.iter();
144                    let mut variable = match steps.next() {
145                        Some(FetchFastTarget::Root) => root_scope.materialize(),
146                        Some(FetchFastTarget::Begin) => match steps.clone().next() {
147                            Some(FetchFastTarget::String(key)) => {
148                                steps.next();
149                                env.get_str(key).unwrap_or(Null)
150                            }
151                            _ => env.materialize(),
152                        },
153                        _ => Null,
154                    };
155
156                    variable = steps.fold(variable, |v, p| match p {
157                        FetchFastTarget::Root => root_scope.materialize(),
158                        FetchFastTarget::Begin => env.materialize(),
159                        FetchFastTarget::String(key) => match v {
160                            Object(obj) => {
161                                let obj_ref = obj.borrow();
162                                obj_ref.get_str(key).cloned().unwrap_or(Null)
163                            }
164                            _ => Null,
165                        },
166                        FetchFastTarget::Number(num) => match v {
167                            Array(arr) => {
168                                let arr_ref = arr.borrow();
169                                arr_ref.get(*num as usize).cloned().unwrap_or(Null)
170                            }
171                            String(str) => {
172                                let index = *num as usize;
173                                str.get(index..index + 1)
174                                    .map(|slice| String((slice).into()))
175                                    .unwrap_or(Null)
176                            }
177                            _ => Null,
178                        },
179                    });
180
181                    self.push(variable);
182                }
183                Opcode::FetchEnv(f) => match env.local_str(f) {
184                    Some(v) => self.push(v.clone()),
185                    None => match env.base() {
186                        Object(o) => {
187                            let obj = o.borrow();
188                            match obj.get_str(f) {
189                                None => self.push(Null),
190                                Some(v) => self.push(v.clone()),
191                            }
192                        }
193                        Null => self.push(Null),
194                        _ => {
195                            return Err(OpcodeErr {
196                                opcode: "FetchEnv".into(),
197                                message: "Unsupported type".into(),
198                            });
199                        }
200                    },
201                },
202                Opcode::FetchRootEnv => {
203                    self.push(env.materialize());
204                }
205                Opcode::Negate => {
206                    let a = self.pop()?;
207                    match a {
208                        Number(n) => {
209                            self.push(Number(-n));
210                        }
211                        _ => {
212                            return Err(OpcodeErr {
213                                opcode: "Negate".into(),
214                                message: "Unsupported type".into(),
215                            });
216                        }
217                    }
218                }
219                Opcode::Not => {
220                    let a = self.pop()?;
221                    match a {
222                        Bool(b) => self.push(Bool(!b)),
223                        _ => {
224                            return Err(OpcodeErr {
225                                opcode: "Not".into(),
226                                message: "Unsupported type".into(),
227                            });
228                        }
229                    }
230                }
231                Opcode::Equal => {
232                    let b = self.pop()?;
233                    let a = self.pop()?;
234                    match (a, b) {
235                        (Number(a), Number(b)) => {
236                            self.push(Bool(a == b));
237                        }
238                        (Bool(a), Bool(b)) => {
239                            self.push(Bool(a == b));
240                        }
241                        (String(a), String(b)) => {
242                            self.push(Bool(a == b));
243                        }
244                        (Null, Null) => {
245                            self.push(Bool(true));
246                        }
247                        (Dynamic(a), Dynamic(b)) => {
248                            let a = a.as_date();
249                            let b = b.as_date();
250
251                            self.push(Bool(a.is_some() && b.is_some() && a == b));
252                        }
253                        _ => {
254                            self.push(Bool(false));
255                        }
256                    }
257                }
258                Opcode::Jump(kind, j) => match kind {
259                    Jump::Forward => self.ip += j,
260                    Jump::Backward => self.ip -= j,
261                    Jump::IfTrue => {
262                        let a = self.stack.last().ok_or_else(|| OpcodeErr {
263                            opcode: "JumpIfTrue".into(),
264                            message: "Undefined object key".into(),
265                        })?;
266                        match a {
267                            Bool(a) => {
268                                if *a {
269                                    self.ip += j;
270                                }
271                            }
272                            _ => {
273                                return Err(OpcodeErr {
274                                    opcode: "JumpIfTrue".into(),
275                                    message: "Unsupported type".into(),
276                                });
277                            }
278                        }
279                    }
280                    Jump::IfFalse => {
281                        let a = self.stack.last().ok_or_else(|| OpcodeErr {
282                            opcode: "JumpIfFalse".into(),
283                            message: "Empty array".into(),
284                        })?;
285
286                        match a {
287                            Bool(a) => {
288                                if !*a {
289                                    self.ip += j;
290                                }
291                            }
292                            _ => {
293                                return Err(OpcodeErr {
294                                    opcode: "JumpIfFalse".into(),
295                                    message: "Unsupported type".into(),
296                                });
297                            }
298                        }
299                    }
300                    Jump::IfNotNull => {
301                        let a = self.stack.last().ok_or_else(|| OpcodeErr {
302                            opcode: "JumpIfNull".into(),
303                            message: "Empty array".into(),
304                        })?;
305
306                        match a {
307                            Null => {}
308                            _ => {
309                                self.ip += j;
310                            }
311                        }
312                    }
313                    Jump::IfEnd => {
314                        let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
315                            opcode: "JumpIfEnd".into(),
316                            message: "Empty stack".into(),
317                        })?;
318
319                        if scope.iter >= scope.len {
320                            self.ip += j;
321                        }
322                    }
323                },
324                Opcode::In => {
325                    let b = self.pop()?;
326                    let a = self.pop()?;
327
328                    match (a, &b) {
329                        (Number(a), Array(b)) => {
330                            let arr = b.borrow();
331                            let is_in = arr.iter().any(|b| match b {
332                                Number(b) => a == *b,
333                                _ => false,
334                            });
335
336                            self.push(Bool(is_in));
337                        }
338                        (Number(v), Dynamic(d)) => {
339                            let Some(i) = d.as_any().downcast_ref::<VmInterval>() else {
340                                return Err(OpcodeErr {
341                                    opcode: "In".into(),
342                                    message: "Unsupported type".into(),
343                                });
344                            };
345
346                            self.push(Bool(i.includes(VmIntervalData::Number(v)).map_err(
347                                |err| OpcodeErr {
348                                    opcode: "In".into(),
349                                    message: err.to_string(),
350                                },
351                            )?));
352                        }
353                        (Dynamic(d), Dynamic(i)) => {
354                            let Some(d) = d.as_date() else {
355                                return Err(OpcodeErr {
356                                    opcode: "In".into(),
357                                    message: "Unsupported type".into(),
358                                });
359                            };
360
361                            let Some(i) = i.as_any().downcast_ref::<VmInterval>() else {
362                                return Err(OpcodeErr {
363                                    opcode: "In".into(),
364                                    message: "Unsupported type".into(),
365                                });
366                            };
367
368                            self.push(Bool(i.includes(VmIntervalData::Date(d.clone())).map_err(
369                                |err| OpcodeErr {
370                                    opcode: "In".into(),
371                                    message: err.to_string(),
372                                },
373                            )?));
374                        }
375                        (Dynamic(a), Array(arr)) => {
376                            let Some(a) = a.as_date() else {
377                                return Err(OpcodeErr {
378                                    opcode: "In".into(),
379                                    message: "Unsupported type".into(),
380                                });
381                            };
382
383                            let arr = arr.borrow();
384                            let is_in = arr.iter().any(|b| match b {
385                                Dynamic(b) => Some(a) == b.as_date(),
386                                _ => false,
387                            });
388
389                            self.push(Bool(is_in));
390                        }
391                        (String(a), Array(b)) => {
392                            let arr = b.borrow();
393                            let is_in = arr.iter().any(|b| match b {
394                                String(b) => &a == b,
395                                _ => false,
396                            });
397
398                            self.push(Bool(is_in));
399                        }
400                        (String(a), Object(b)) => {
401                            let obj = b.borrow();
402                            self.push(Bool(obj.contains_key_str(a.as_str())));
403                        }
404                        (Bool(a), Array(b)) => {
405                            let arr = b.borrow();
406                            let is_in = arr.iter().any(|b| match b {
407                                Bool(b) => a == *b,
408                                _ => false,
409                            });
410
411                            self.push(Bool(is_in));
412                        }
413                        (Null, Array(b)) => {
414                            let arr = b.borrow();
415                            let is_in = arr.iter().any(|b| match b {
416                                Null => true,
417                                _ => false,
418                            });
419
420                            self.push(Bool(is_in));
421                        }
422                        _ => {
423                            return Err(OpcodeErr {
424                                opcode: "In".into(),
425                                message: "Unsupported type".into(),
426                            });
427                        }
428                    }
429                }
430                Opcode::Compare(comparison) => {
431                    let b = self.pop()?;
432                    let a = self.pop()?;
433
434                    fn compare<T: Ord>(a: &T, b: &T, comparison: &Compare) -> bool {
435                        match comparison {
436                            Compare::More => a > b,
437                            Compare::MoreOrEqual => a >= b,
438                            Compare::Less => a < b,
439                            Compare::LessOrEqual => a <= b,
440                        }
441                    }
442
443                    match (a, b) {
444                        (Number(a), Number(b)) => self.push(Bool(compare(&a, &b, comparison))),
445                        (Dynamic(a), Dynamic(b)) => {
446                            let (a, b) = match (a.as_date(), b.as_date()) {
447                                (Some(a), Some(b)) => (a, b),
448                                _ => {
449                                    return Err(OpcodeErr {
450                                        opcode: "Compare".into(),
451                                        message: "Unsupported type".into(),
452                                    })
453                                }
454                            };
455
456                            self.push(Bool(compare(a, b, comparison)));
457                        }
458                        _ => {
459                            return Err(OpcodeErr {
460                                opcode: "Compare".into(),
461                                message: "Unsupported type".into(),
462                            });
463                        }
464                    }
465                }
466                Opcode::Add => {
467                    let b = self.pop()?;
468                    let a = self.pop()?;
469
470                    match (a, b) {
471                        (Number(a), Number(b)) => {
472                            let result = a.checked_add(b).ok_or_else(|| OpcodeErr {
473                                opcode: "Add".into(),
474                                message: "Number overflow".into(),
475                            })?;
476                            self.push(Number(result));
477                        }
478                        (String(a), String(b)) => {
479                            let mut c = StdString::with_capacity(a.len() + b.len());
480
481                            c.push_str(a.as_ref());
482                            c.push_str(b.as_ref());
483
484                            self.push(String((c.as_str()).into()));
485                        }
486                        _ => {
487                            return Err(OpcodeErr {
488                                opcode: "Add".into(),
489                                message: "Unsupported type".into(),
490                            });
491                        }
492                    }
493                }
494                Opcode::Subtract => {
495                    let b = self.pop()?;
496                    let a = self.pop()?;
497
498                    match (a, b) {
499                        (Number(a), Number(b)) => {
500                            let result = a.checked_sub(b).ok_or_else(|| OpcodeErr {
501                                opcode: "Subtract".into(),
502                                message: "Number overflow".into(),
503                            })?;
504                            self.push(Number(result));
505                        }
506                        _ => {
507                            return Err(OpcodeErr {
508                                opcode: "Subtract".into(),
509                                message: "Unsupported type".into(),
510                            });
511                        }
512                    }
513                }
514                Opcode::Multiply => {
515                    let b = self.pop()?;
516                    let a = self.pop()?;
517
518                    match (a, b) {
519                        (Number(a), Number(b)) => {
520                            let result = a.checked_mul(b).ok_or_else(|| OpcodeErr {
521                                opcode: "Multiply".into(),
522                                message: "Number overflow".into(),
523                            })?;
524                            self.push(Number(result));
525                        }
526                        _ => {
527                            return Err(OpcodeErr {
528                                opcode: "Multiply".into(),
529                                message: "Unsupported type".into(),
530                            });
531                        }
532                    }
533                }
534                Opcode::Divide => {
535                    let b = self.pop()?;
536                    let a = self.pop()?;
537
538                    let (Number(a), Number(b)) = (a, b) else {
539                        return Err(OpcodeErr {
540                            opcode: "Divide".into(),
541                            message: "Unsupported type".into(),
542                        });
543                    };
544
545                    let result = match a.checked_div(b) {
546                        Some(r) => Number(r),
547                        None => Null,
548                    };
549
550                    self.push(result);
551                }
552                Opcode::Modulo => {
553                    let b = self.pop()?;
554                    let a = self.pop()?;
555
556                    match (a, b) {
557                        (Number(a), Number(b)) => {
558                            let result = match a.checked_rem(b) {
559                                Some(r) => Number(r),
560                                None => Null,
561                            };
562                            self.push(result);
563                        }
564                        _ => {
565                            return Err(OpcodeErr {
566                                opcode: "Modulo".into(),
567                                message: "Unsupported type".into(),
568                            });
569                        }
570                    }
571                }
572                Opcode::Exponent => {
573                    let b = self.pop()?;
574                    let a = self.pop()?;
575
576                    match (a, b) {
577                        (Number(a), Number(b)) => {
578                            let result = a
579                                .checked_powd(b)
580                                .or_else(|| Decimal::from_f64(a.to_f64()?.powf(b.to_f64()?)))
581                                .ok_or_else(|| OpcodeErr {
582                                    opcode: "Exponent".into(),
583                                    message: "Failed to calculate exponent".into(),
584                                })?;
585
586                            self.push(Number(result));
587                        }
588                        _ => {
589                            return Err(OpcodeErr {
590                                opcode: "Exponent".into(),
591                                message: "Unsupported type".into(),
592                            });
593                        }
594                    }
595                }
596                Opcode::Interval {
597                    left_bracket,
598                    right_bracket,
599                } => {
600                    let b = self.pop()?;
601                    let a = self.pop()?;
602
603                    match (&a, &b) {
604                        (Number(a), Number(b)) => {
605                            let interval = VmInterval {
606                                left_bracket: *left_bracket,
607                                right_bracket: *right_bracket,
608                                left: VmIntervalData::Number(*a),
609                                right: VmIntervalData::Number(*b),
610                            };
611
612                            self.push(Dynamic(Rc::new(interval)));
613                        }
614                        (Dynamic(a), Dynamic(b)) => {
615                            let (a, b) = match (a.as_date(), b.as_date()) {
616                                (Some(a), Some(b)) => (a, b),
617                                _ => {
618                                    return Err(OpcodeErr {
619                                        opcode: "Interval".into(),
620                                        message: "Unsupported type".into(),
621                                    })
622                                }
623                            };
624
625                            let interval = VmInterval {
626                                left_bracket: *left_bracket,
627                                right_bracket: *right_bracket,
628                                left: VmIntervalData::Date(a.clone()),
629                                right: VmIntervalData::Date(b.clone()),
630                            };
631
632                            self.push(Dynamic(Rc::new(interval)));
633                        }
634                        _ => {
635                            return Err(OpcodeErr {
636                                opcode: "Interval".into(),
637                                message: "Unsupported type".into(),
638                            });
639                        }
640                    }
641                }
642                Opcode::Join => {
643                    let b = self.pop()?;
644                    let a = self.pop()?;
645
646                    let (Array(a), String(separator)) = (a, &b) else {
647                        return Err(OpcodeErr {
648                            opcode: "Join".into(),
649                            message: "Unsupported type".into(),
650                        });
651                    };
652
653                    let arr = a.borrow();
654                    let parts = arr
655                        .iter()
656                        .enumerate()
657                        .map(|(i, var)| match var {
658                            String(str) => Ok(str.clone()),
659                            _ => Err(OpcodeErr {
660                                opcode: "Join".into(),
661                                message: format!("Unexpected type in array on index {i}"),
662                            }),
663                        })
664                        .collect::<Result<Vec<_>, _>>()?;
665
666                    let str_capacity = parts
667                        .iter()
668                        .fold(separator.len() * (parts.len() - 1), |acc, s| acc + s.len());
669
670                    let mut s = StdString::with_capacity(str_capacity);
671                    let mut it = parts.into_iter().peekable();
672                    while let Some(part) = it.next() {
673                        s.push_str(part.as_ref());
674                        if it.peek().is_some() {
675                            s.push_str(separator);
676                        }
677                    }
678
679                    self.push(String((s).into()));
680                }
681                Opcode::Slice => {
682                    let from_var = self.pop()?;
683                    let to_var = self.pop()?;
684                    let current = self.pop()?;
685
686                    match (from_var, to_var) {
687                        (Number(f), Number(t)) => {
688                            let from = f.to_usize().ok_or_else(|| OpcodeErr {
689                                opcode: "Slice".into(),
690                                message: "Failed to get range from".into(),
691                            })?;
692                            let to = t.to_usize().ok_or_else(|| OpcodeErr {
693                                opcode: "Slice".into(),
694                                message: "Failed to get range to".into(),
695                            })?;
696
697                            match current {
698                                Array(a) => {
699                                    let arr = a.borrow();
700                                    let slice = arr.get(from..=to).ok_or_else(|| OpcodeErr {
701                                        opcode: "Slice".into(),
702                                        message: "Index out of range".into(),
703                                    })?;
704
705                                    self.push(Variable::from_array(slice.to_vec()));
706                                }
707                                String(s) => {
708                                    let slice = s.get(from..=to).ok_or_else(|| OpcodeErr {
709                                        opcode: "Slice".into(),
710                                        message: "Index out of range".into(),
711                                    })?;
712
713                                    self.push(String((slice).into()));
714                                }
715                                _ => {
716                                    return Err(OpcodeErr {
717                                        opcode: "Slice".into(),
718                                        message: "Unsupported type".into(),
719                                    });
720                                }
721                            }
722                        }
723                        _ => {
724                            return Err(OpcodeErr {
725                                opcode: "Slice".into(),
726                                message: "Unsupported type".into(),
727                            });
728                        }
729                    }
730                }
731                Opcode::Array => {
732                    let size = self.pop()?;
733                    let Number(s) = size else {
734                        return Err(OpcodeErr {
735                            opcode: "Array".into(),
736                            message: "Unsupported type".into(),
737                        });
738                    };
739
740                    let to = s.round().to_usize().ok_or_else(|| OpcodeErr {
741                        opcode: "Array".into(),
742                        message: "Failed to extract argument".into(),
743                    })?;
744
745                    let mut arr = Vec::with_capacity(to);
746                    for _ in 0..to {
747                        arr.push(self.pop()?);
748                    }
749                    arr.reverse();
750
751                    self.push(Variable::from_array(arr));
752                }
753                Opcode::Object => {
754                    let size = self.pop()?;
755                    let Number(s) = size else {
756                        return Err(OpcodeErr {
757                            opcode: "Array".into(),
758                            message: "Unsupported type".into(),
759                        });
760                    };
761
762                    let to = s.round().to_usize().ok_or_else(|| OpcodeErr {
763                        opcode: "Array".into(),
764                        message: "Failed to extract argument".into(),
765                    })?;
766
767                    let mut map = crate::variable::VariableMap::with_capacity(to);
768                    for _ in 0..to {
769                        let value = self.pop()?;
770                        let String(key) = self.pop()? else {
771                            return Err(OpcodeErr {
772                                opcode: "Object".into(),
773                                message: "Unexpected key value".to_string(),
774                            });
775                        };
776
777                        map.insert(Symbol::from(key.as_str()), value);
778                    }
779
780                    self.push(Variable::from_object(map));
781                }
782                Opcode::AssignedObjectBegin => {
783                    assigned_objects.push(Variable::empty_object());
784                }
785                Opcode::AssignedObjectStep => {
786                    let value = self.pop()?;
787                    let String(key) = self.pop()? else {
788                        return Err(OpcodeErr {
789                            opcode: "AssignedObjectStep".into(),
790                            message: "Unexpected key value".to_string(),
791                        });
792                    };
793
794                    let Some(assigned_object) = assigned_objects.last() else {
795                        return Err(OpcodeErr {
796                            opcode: "AssignedObjectStep".into(),
797                            message: "Assigned object scope must be set".to_owned(),
798                        });
799                    };
800
801                    if !matches!(env.base(), Object(_)) {
802                        return Err(OpcodeErr {
803                            opcode: "AssignedObjectStep".into(),
804                            message: "Failed to mutate existing env".to_owned(),
805                        });
806                    }
807
808                    match key.contains('.') {
809                        false => env.set_local(Symbol::from(key.as_str()), value.clone()),
810                        true => {
811                            let Some(new_env) = env
812                                .materialize()
813                                .dot_insert_detached(key.as_ref(), value.clone())
814                            else {
815                                return Err(OpcodeErr {
816                                    opcode: "AssignedObjectStep".into(),
817                                    message: "Failed to mutate existing env".to_owned(),
818                                });
819                            };
820
821                            env = Scope::new(new_env);
822                        }
823                    }
824
825                    assigned_object.dot_insert(key.as_ref(), value);
826                }
827                Opcode::AssignedObjectEnd { with_return } => {
828                    let Some(assigned_object) = assigned_objects.pop() else {
829                        return Err(OpcodeErr {
830                            opcode: "AssignedObjectEnd".into(),
831                            message: "Assigned object scope must be set".to_owned(),
832                        });
833                    };
834
835                    if *with_return {
836                        let output = self.pop()?;
837                        self.push(output);
838                    } else {
839                        self.push(assigned_object);
840                    }
841                }
842                Opcode::Len => {
843                    let current = self.stack.last().ok_or_else(|| OpcodeErr {
844                        opcode: "Len".into(),
845                        message: "Empty stack".into(),
846                    })?;
847
848                    let len_var =
849                        internal::imp::len(Arguments(&[current.clone()])).map_err(|err| {
850                            OpcodeErr {
851                                opcode: "Len".into(),
852                                message: err.to_string(),
853                            }
854                        })?;
855
856                    self.push(len_var);
857                }
858                Opcode::Flatten => {
859                    let current = self.pop()?;
860                    let Array(a) = current else {
861                        return Err(OpcodeErr {
862                            opcode: "Flatten".into(),
863                            message: "Unsupported type".into(),
864                        });
865                    };
866
867                    let arr = a.borrow();
868
869                    let mut flat_arr = Vec::with_capacity(arr.len());
870                    arr.iter().for_each(|v| match v {
871                        Array(b) => {
872                            let arr = b.borrow();
873                            arr.iter().for_each(|v| flat_arr.push(v.clone()))
874                        }
875                        _ => flat_arr.push(v.clone()),
876                    });
877
878                    self.push(Variable::from_array(flat_arr));
879                }
880                Opcode::IncrementIt => {
881                    let scope = self.scopes.last_mut().ok_or_else(|| OpcodeErr {
882                        opcode: "IncrementIt".into(),
883                        message: "Empty scope".into(),
884                    })?;
885
886                    scope.iter += 1;
887                }
888                Opcode::IncrementCount => {
889                    let scope = self.scopes.last_mut().ok_or_else(|| OpcodeErr {
890                        opcode: "IncrementCount".into(),
891                        message: "Empty scope".into(),
892                    })?;
893
894                    scope.count += 1;
895                }
896                Opcode::GetCount => {
897                    let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
898                        opcode: "GetCount".into(),
899                        message: "Empty scope".into(),
900                    })?;
901
902                    self.push(Number(scope.count.into()));
903                }
904                Opcode::GetLen => {
905                    let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
906                        opcode: "GetLen".into(),
907                        message: "Empty scope".into(),
908                    })?;
909
910                    self.push(Number(scope.len.into()));
911                }
912                Opcode::Pointer(depth) => {
913                    let scope_index = self
914                        .scopes
915                        .len()
916                        .checked_sub(1 + *depth as usize)
917                        .ok_or_else(|| OpcodeErr {
918                            opcode: "Pointer".into(),
919                            message: format!("Scope depth {} out of bounds", depth),
920                        })?;
921
922                    let scope = self.scopes.get(scope_index).ok_or_else(|| OpcodeErr {
923                        opcode: "Pointer".into(),
924                        message: "Empty scope".into(),
925                    })?;
926
927                    match &scope.array {
928                        Array(a) => {
929                            let a_cloned = a.clone();
930                            let arr = a_cloned.borrow();
931                            let variable =
932                                arr.get(scope.iter).cloned().ok_or_else(|| OpcodeErr {
933                                    opcode: "Pointer".into(),
934                                    message: "Scope array out of bounds".into(),
935                                })?;
936
937                            self.push(variable);
938                        }
939                        _ => {
940                            return Err(OpcodeErr {
941                                opcode: "Pointer".into(),
942                                message: "Unsupported scope type".into(),
943                            });
944                        }
945                    }
946                }
947                Opcode::Begin => {
948                    let var = self.pop()?;
949                    let maybe_scope = match &var {
950                        Array(a) => {
951                            let arr = a.borrow();
952                            Some(LoopScope {
953                                len: arr.len(),
954                                array: var.clone(),
955                                count: 0,
956                                iter: 0,
957                            })
958                        }
959                        _ => match var.dynamic::<VmInterval>().map(|s| s.to_array()).flatten() {
960                            None => None,
961                            Some(arr) => Some(LoopScope {
962                                len: arr.len(),
963                                array: Variable::from_array(arr),
964                                count: 0,
965                                iter: 0,
966                            }),
967                        },
968                    };
969
970                    let Some(scope) = maybe_scope else {
971                        return Err(OpcodeErr {
972                            opcode: "Begin".into(),
973                            message: "Unsupported type".into(),
974                        });
975                    };
976
977                    self.scopes.push(scope);
978                }
979                Opcode::End => {
980                    self.scopes.pop();
981                }
982                Opcode::CallFunction { kind, arg_count } => {
983                    let function =
984                        FunctionRegistry::get_definition(kind).ok_or_else(|| OpcodeErr {
985                            opcode: "CallFunction".into(),
986                            message: format!("Function `{kind}` not found"),
987                        })?;
988
989                    let params_start = self.stack.len().saturating_sub(*arg_count as usize);
990                    let result = function
991                        .call(Arguments(&self.stack[params_start..]))
992                        .map_err(|err| OpcodeErr {
993                            opcode: "CallFunction".into(),
994                            message: format!("Function `{kind}` failed: {err}"),
995                        })?;
996
997                    self.stack.drain(params_start..);
998                    self.push(result);
999                }
1000                Opcode::CallMethod { kind, arg_count } => {
1001                    let method = MethodRegistry::get_definition(kind).ok_or_else(|| OpcodeErr {
1002                        opcode: "CallMethod".into(),
1003                        message: format!("Method `{kind}` not found"),
1004                    })?;
1005
1006                    let params_start = self.stack.len().saturating_sub(*arg_count as usize) - 1;
1007                    let result = method
1008                        .call(Arguments(&self.stack[params_start..]))
1009                        .map_err(|err| OpcodeErr {
1010                            opcode: "CallMethod".into(),
1011                            message: format!("Method `{kind}` failed: {err}"),
1012                        })?;
1013
1014                    self.stack.drain(params_start..);
1015                    self.push(result);
1016                }
1017            }
1018        }
1019
1020        self.pop()
1021    }
1022}