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