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