Files
zen/core/expression/src/vm.rs
T
stefan-gorules a03978fbe7 feat: add bigjs to function node; (#77)
* feat: add bigjs to function node;

* fix test

* fix tests

* fix fmt
2023-08-28 17:21:31 +02:00

1260 lines
49 KiB
Rust

use bumpalo::Bump;
use chrono::NaiveDateTime;
use chrono::{Datelike, Timelike};
use regex::Regex;
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::{Decimal, MathematicalOps};
use rust_decimal_macros::dec;
use std::collections::HashMap;
use thiserror::Error;
use crate::helpers::{date_time, time};
use crate::opcodes::Variable::{Array, Bool, Int, Interval, Null, Number, Object, String};
use crate::opcodes::{Opcode, Variable};
use crate::vm::VMError::{OpcodeErr, OpcodeOutOfBounds, ParseDateTimeErr, StackOutOfBounds};
const NULL_VAR: &'static Variable = &Null;
#[derive(Debug, Error)]
pub enum VMError {
#[error("Unsupported opcode type")]
OpcodeErr {
opcode: std::string::String,
message: std::string::String,
},
#[error("Opcode out of bounds")]
OpcodeOutOfBounds {
index: usize,
bytecode: std::string::String,
},
#[error("Stack out of bounds")]
StackOutOfBounds { stack: std::string::String },
#[error("Failed to parse date time")]
ParseDateTimeErr { timestamp: std::string::String },
}
pub struct Scope<'a> {
array: &'a Variable<'a>,
len: usize,
iter: usize,
count: usize,
}
pub struct VM<'a> {
scopes: &'a mut Vec<Scope<'a>>,
stack: &'a mut Vec<&'a Variable<'a>>,
ip: usize,
bytecode: &'a Vec<&'a Opcode<'a>>,
bump: &'a Bump,
}
impl<'a> VM<'a> {
pub fn new(
bytecode: &'a Vec<&'a Opcode<'a>>,
stack: &'a mut Vec<&'a Variable<'a>>,
scopes: &'a mut Vec<Scope<'a>>,
bump: &'a Bump,
) -> Self {
Self {
scopes,
stack,
ip: 0,
bytecode,
bump,
}
}
fn push(&mut self, var: Variable<'a>) {
self.stack.push(self.bump.alloc(var));
}
fn pop(&mut self) -> Result<&'a Variable<'a>, VMError> {
self.stack.pop().ok_or_else(|| StackOutOfBounds {
stack: format!("{:?}", self.stack),
})
}
fn push_ref(&mut self, var: &'a Variable<'a>) {
self.stack.push(var);
}
pub fn run(&mut self, env: &'a Variable<'a>) -> Result<&'a Variable<'a>, VMError> {
if self.ip != 0 {
self.ip = 0;
}
while self.ip < self.bytecode.len() {
let op = self
.bytecode
.get(self.ip)
.ok_or_else(|| OpcodeOutOfBounds {
bytecode: format!("{:?}", self.bytecode),
index: self.ip,
})?;
self.ip += 1;
match op {
Opcode::Push(v) => match v {
Int(a) => self.push(Number(Decimal::from(*a))),
_ => self.push_ref(v),
},
Opcode::Pop => {
self.pop()?;
}
Opcode::Rot => {
let b = self.stack.len() - 1;
let a = self.stack.len() - 2;
self.stack.swap(a, b);
}
Opcode::Fetch => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Object(o), String(s)) => {
self.push_ref(o.get(*s).unwrap_or(&NULL_VAR));
}
(Array(arr), Number(n)) => self.push_ref(
arr.get(n.to_usize().ok_or_else(|| OpcodeErr {
opcode: "Fetch".into(),
message: "Failed to convert to usize".into(),
})?)
.unwrap_or(&NULL_VAR),
),
_ => self.push_ref(&NULL_VAR),
}
}
Opcode::FetchEnv(f) => match env {
Object(o) => self.push_ref(o.get(*f).unwrap_or(&NULL_VAR)),
Null => self.push_ref(NULL_VAR),
_ => {
return Err(OpcodeErr {
opcode: "FetchEnv".into(),
message: "Unsupported type".into(),
})
}
},
Opcode::Negate => {
let a = self.pop()?;
match a {
Number(n) => {
self.stack.push(self.bump.alloc(Number(-*n)));
}
_ => {
return Err(OpcodeErr {
opcode: "Negate".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Not => {
let a = self.pop()?;
match a {
Bool(b) => self.stack.push(self.bump.alloc(Bool(!(*b)))),
_ => {
return Err(OpcodeErr {
opcode: "Not".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Equal => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => {
self.stack.push(self.bump.alloc(Bool(a == b)));
}
(Bool(a), Bool(b)) => {
self.stack.push(self.bump.alloc(Bool(a == b)));
}
(String(a), String(b)) => {
self.stack.push(self.bump.alloc(Bool(a == b)));
}
(Null, Null) => {
self.stack.push(self.bump.alloc(Bool(true)));
}
_ => {
self.stack.push(self.bump.alloc(Bool(false)));
}
}
}
Opcode::Jump(j) => self.ip += j,
Opcode::JumpIfTrue(j) => {
let a = self.stack.last().ok_or_else(|| OpcodeErr {
opcode: "JumpIfTrue".into(),
message: "Undefined object key".into(),
})?;
match a {
Bool(a) => {
if *a {
self.ip += j;
}
}
_ => {
return Err(OpcodeErr {
opcode: "JumpIfTrue".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::JumpIfFalse(j) => {
let a = self.stack.last().ok_or_else(|| OpcodeErr {
opcode: "JumpIfFalse".into(),
message: "Empty array".into(),
})?;
match a {
Bool(a) => {
if !*a {
self.ip += j;
}
}
_ => {
return Err(OpcodeErr {
opcode: "JumpIfFalse".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::JumpBackward(j) => {
self.ip -= j;
}
Opcode::In => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Array(arr)) => {
let is_in = arr.iter().any(|b| match b {
Number(b) => a == b,
_ => false,
});
self.stack.push(self.bump.alloc(Bool(is_in)));
}
(
Number(v),
Interval {
left_bracket,
right_bracket,
left,
right,
},
) => match (left, right) {
(Number(l), Number(r)) => {
let mut is_open = false;
let first = match *left_bracket {
"[" => l <= v,
"(" => l < v,
"]" => {
is_open = true;
l >= v
}
")" => {
is_open = true;
l > v
}
_ => {
return Err(OpcodeErr {
opcode: "In".into(),
message: "Unsupported bracket".into(),
})
}
};
let second = match *right_bracket {
"]" => r >= v,
")" => r > v,
"[" => r <= v,
"(" => r < v,
_ => {
return Err(OpcodeErr {
opcode: "In".into(),
message: "Unsupported bracket".into(),
})
}
};
let open_stmt = is_open && (first || second);
let closed_stmt = !is_open && first && second;
self.stack
.push(self.bump.alloc(Bool(open_stmt || closed_stmt)));
}
_ => {
return Err(OpcodeErr {
opcode: "In".into(),
message: "Unsupported type".into(),
})
}
},
(String(a), Array(arr)) => {
let is_in = arr.iter().any(|b| match b {
String(b) => a == b,
_ => false,
});
self.stack.push(self.bump.alloc(Bool(is_in)));
}
(Bool(a), Array(arr)) => {
let is_in = arr.iter().any(|b| match b {
Bool(b) => a == b,
_ => false,
});
self.stack.push(self.bump.alloc(Bool(is_in)));
}
(Null, Array(arr)) => {
let is_in = arr.iter().any(|b| match b {
Null => true,
_ => false,
});
self.stack.push(self.bump.alloc(Bool(is_in)));
}
_ => {
return Err(OpcodeErr {
opcode: "In".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Less => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Bool(*a < *b))),
_ => {
return Err(OpcodeErr {
opcode: "Less".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::More => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Bool(*a > *b))),
_ => {
return Err(OpcodeErr {
opcode: "More".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::LessOrEqual => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Bool(*a <= *b))),
_ => {
return Err(OpcodeErr {
opcode: "LessOrEqual".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::MoreOrEqual => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Bool(*a >= *b))),
_ => {
return Err(OpcodeErr {
opcode: "MoreOrEqual".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Abs => {
let a = self.pop()?;
match a {
Number(a) => self.stack.push(self.bump.alloc(Number(a.abs()))),
_ => {
return Err(OpcodeErr {
opcode: "Abs".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Round => {
let var = self.pop()?;
match var {
Number(a) => self.stack.push(self.bump.alloc(Number(a.round()))),
_ => {
return Err(OpcodeErr {
opcode: "Round".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Ceil => {
let var = self.pop()?;
match var {
Number(a) => self.stack.push(self.bump.alloc(Number(a.round()))),
_ => {
return Err(OpcodeErr {
opcode: "Ceil".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Floor => {
let var = self.pop()?;
match var {
Number(a) => self.stack.push(self.bump.alloc(Number(a.floor()))),
_ => {
return Err(OpcodeErr {
opcode: "Floor".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Random => {
let var = self.pop()?;
match var {
Number(a) => {
let upper_range = a.round().to_i64().ok_or_else(|| OpcodeErr {
opcode: "Random".into(),
message: "Failed to determine upper range".into(),
})?;
let random_number = fastrand::i64(0..=upper_range);
self.stack
.push(self.bump.alloc(Number(Decimal::from(random_number))));
}
_ => {
return Err(OpcodeErr {
opcode: "Random".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Average => {
let var = self.pop()?;
match var {
Array(arr) => {
let mut sum = Decimal::ZERO;
arr.iter().try_for_each(|a| match a {
Number(a) => {
sum += a;
Ok(())
}
_ => Err(OpcodeErr {
opcode: "Average".into(),
message: "Invalid array value".into(),
}),
})?;
let avg = sum / Decimal::from(arr.len());
self.stack.push(self.bump.alloc(Number(avg)));
}
_ => {
return Err(OpcodeErr {
opcode: "Average".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Median => {
let Array(arr) = self.pop()? else {
return Err(OpcodeErr {
opcode: "Median".into(),
message: "Unsupported type".into(),
});
};
let mut num_arr = arr
.iter()
.map(|n| match n {
Number(num) => Ok(num),
_ => Err(OpcodeErr {
opcode: "Median".into(),
message: "Unsupported type".into(),
}),
})
.collect::<Result<Vec<_>, _>>()?;
if num_arr.len() == 0 {
return Err(OpcodeErr {
opcode: "Median".into(),
message: "Array is empty".into(),
});
}
num_arr.sort();
let center = num_arr.len() / 2;
if num_arr.len() % 2 == 1 {
let center_num = num_arr.get(center).ok_or_else(|| OpcodeErr {
opcode: "Median".into(),
message: "Array out of bounds".into(),
})?;
self.push(Number((*center_num).clone()));
} else {
let center_left = num_arr.get(center - 1).ok_or_else(|| OpcodeErr {
opcode: "Median".into(),
message: "Array out of bounds".into(),
})?;
let center_right = num_arr.get(center).ok_or_else(|| OpcodeErr {
opcode: "Median".into(),
message: "Array out of bounds".into(),
})?;
let median = ((**center_left) + (**center_right)) / dec!(2);
self.push(Number(median));
}
}
Opcode::Mode => {
let Array(arr) = self.pop()? else {
return Err(OpcodeErr {
opcode: "Mode".into(),
message: "Unsupported type".into(),
});
};
let num_arr = arr
.iter()
.map(|n| match n {
Number(num) => Ok(num),
_ => Err(OpcodeErr {
opcode: "Mode".into(),
message: "Unsupported type".into(),
}),
})
.collect::<Result<Vec<_>, _>>()?;
if num_arr.len() == 0 {
return Err(OpcodeErr {
opcode: "Mode".into(),
message: "Array is empty".into(),
});
}
let mut map = HashMap::new();
num_arr.iter().for_each(|n| {
let count = map.entry(**n).or_insert(0);
*count += 1;
});
let maybe_mode = map
.iter()
.max_by_key(|&(_, count)| *count)
.map(|(val, _)| val);
let mode = maybe_mode.ok_or_else(|| OpcodeErr {
opcode: "Mode".into(),
message: "Failed to find most common element".into(),
})?;
self.push(Number(*mode));
}
Opcode::Min => {
let var = self.pop()?;
match var {
Array(arr) => {
let first_item = arr.get(0).ok_or_else(|| OpcodeErr {
opcode: "Min".into(),
message: "Empty array".into(),
})?;
let mut min: Decimal = match first_item {
Number(a) => *a,
_ => {
return Err(OpcodeErr {
opcode: "Min".into(),
message: "Unsupported array value".into(),
})
}
};
arr.iter().try_for_each(|a| match a {
Number(a) => {
if *a < min {
min = *a;
}
Ok(())
}
_ => Err(OpcodeErr {
opcode: "Min".into(),
message: "Unsupported array value".into(),
}),
})?;
self.stack.push(self.bump.alloc(Number(min)));
}
_ => {
return Err(OpcodeErr {
opcode: "Min".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Max => {
let var = self.pop()?;
match var {
Array(arr) => {
let first_item = arr.get(0).ok_or_else(|| OpcodeErr {
opcode: "Max".into(),
message: "Empty array".into(),
})?;
let mut max: Decimal = match first_item {
Number(a) => *a,
_ => {
return Err(OpcodeErr {
opcode: "Max".into(),
message: "Unsupported array value".into(),
})
}
};
arr.iter().try_for_each(|a| match a {
Number(a) => {
if *a > max {
max = *a;
}
Ok(())
}
_ => Err(OpcodeErr {
opcode: "Max".into(),
message: "Unsupported array value".into(),
}),
})?;
self.stack.push(self.bump.alloc(Number(max)));
}
_ => {
return Err(OpcodeErr {
opcode: "Max".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Sum => {
let var = self.pop()?;
match var {
Array(arr) => {
let mut sum = Decimal::ZERO;
arr.iter().try_for_each(|a| match a {
Number(a) => {
sum += a;
Ok(())
}
_ => Err(OpcodeErr {
opcode: "Sum".into(),
message: "Unsupported array value".into(),
}),
})?;
self.stack.push(self.bump.alloc(Number(sum)));
}
_ => {
return Err(OpcodeErr {
opcode: "Sum".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Add => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Number(a + b))),
(String(a), String(b)) => {
let mut str1 = std::string::String::with_capacity(a.len() + b.len());
str1.push_str(a);
str1.push_str(b);
self.stack
.push(self.bump.alloc(String(self.bump.alloc_str(&str1))))
}
_ => {
return Err(OpcodeErr {
opcode: "Add".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Subtract => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Number(a - b))),
_ => {
return Err(OpcodeErr {
opcode: "Subtract".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Multiply => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Number(a * b))),
_ => {
return Err(OpcodeErr {
opcode: "Multiply".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Divide => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Number(a / b))),
_ => {
return Err(OpcodeErr {
opcode: "Divide".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Modulo => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => self.stack.push(self.bump.alloc(Number(a % b))),
_ => {
return Err(OpcodeErr {
opcode: "Modulo".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Exponent => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(a), Number(b)) => {
self.stack.push(self.bump.alloc(Number(a.powd(*b))))
}
_ => {
return Err(OpcodeErr {
opcode: "Exponent".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Interval {
left_bracket,
right_bracket,
} => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(Number(_), Number(_)) => self.stack.push(self.bump.alloc(Interval {
left_bracket,
right_bracket,
left: a,
right: b,
})),
_ => {
return Err(OpcodeErr {
opcode: "Interval".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Uppercase => {
let a = self.pop()?;
match a {
String(a) => {
let str = a.to_uppercase();
self.stack
.push(self.bump.alloc(String(self.bump.alloc_str(&str))))
}
_ => {
return Err(OpcodeErr {
opcode: "Uppercase".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Lowercase => {
let a = self.pop()?;
match a {
String(a) => {
let str = a.to_lowercase();
self.stack
.push(self.bump.alloc(String(self.bump.alloc_str(&str))))
}
_ => {
return Err(OpcodeErr {
opcode: "Lowercase".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Contains => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(String(a), String(b)) => {
self.stack.push(self.bump.alloc(Bool(a.contains(b))))
}
_ => match a {
Array(arr) => {
let is_in = arr.iter().any(|a| match (a, b) {
(Number(a), Number(b)) => a == b,
(String(a), String(b)) => a == b,
(Bool(a), Bool(b)) => a == b,
(Null, Null) => true,
_ => false,
});
self.stack.push(self.bump.alloc(Bool(is_in)));
}
_ => {
return Err(OpcodeErr {
opcode: "Contains".into(),
message: "Unsupported type".into(),
})
}
},
}
}
Opcode::StartsWith => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(String(a), String(b)) => {
self.stack.push(self.bump.alloc(Bool(a.starts_with(b))))
}
_ => {
return Err(OpcodeErr {
opcode: "StartsWith".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::EndsWith => {
let b = self.pop()?;
let a = self.pop()?;
match (a, b) {
(String(a), String(b)) => {
self.stack.push(self.bump.alloc(Bool(a.ends_with(b))))
}
_ => {
return Err(OpcodeErr {
opcode: "EndsWith".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Matches => {
let b = self.pop()?;
let a = self.pop()?;
let (String(a), String(b)) = (a, b) else {
return Err(OpcodeErr {
opcode: "Matches".into(),
message: "Unsupported type".into(),
});
};
let regex = Regex::new(b).map_err(|_| OpcodeErr {
opcode: "Matches".into(),
message: "Invalid regular expression".into(),
})?;
self.push(Bool(regex.is_match(a)));
}
Opcode::Extract => {
let b = self.pop()?;
let a = self.pop()?;
let (String(a), String(b)) = (a, b) else {
return Err(OpcodeErr {
opcode: "Matches".into(),
message: "Unsupported type".into(),
});
};
let regex = Regex::new(b).map_err(|_| OpcodeErr {
opcode: "Matches".into(),
message: "Invalid regular expression".into(),
})?;
let captures = regex
.captures(a)
.map(|capture| {
capture
.iter()
.map(|c| c.map(|c| c.as_str()))
.filter_map(|c| c)
.map(|s| {
self.bump.alloc(String(self.bump.alloc_str(s))) as &Variable
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
self.push(Array(self.bump.alloc_slice_copy(captures.as_slice())));
}
Opcode::DateManipulation(operation) => {
let timestamp = self.pop()?;
let time = match timestamp {
String(a) => date_time(a)?,
Number(a) => NaiveDateTime::from_timestamp_opt(
a.to_i64().ok_or_else(|| OpcodeErr {
opcode: "DateManipulation".into(),
message: "Failed to extract date".into(),
})?,
0,
)
.ok_or_else(|| ParseDateTimeErr {
timestamp: a.to_string(),
})?,
_ => {
return Err(OpcodeErr {
opcode: "DateManipulation".into(),
message: "Unsupported type".into(),
})
}
};
let var = match *operation {
"year" => Number(time.year().into()),
"dayOfWeek" => Number(time.weekday().number_from_monday().into()),
"dayOfMonth" => Number(time.day().into()),
"dayOfYear" => Number(time.ordinal().into()),
"weekOfYear" => Number(time.iso_week().week().into()),
"monthOfYear" => Number(time.month().into()),
"monthString" => {
String(self.bump.alloc_str(&time.format("%b").to_string()))
}
"weekdayString" => String(self.bump.alloc_str(&time.weekday().to_string())),
_ => {
return Err(OpcodeErr {
opcode: "DateManipulation".into(),
message: "Unsupported operation".into(),
})
}
};
self.stack.push(self.bump.alloc(var));
}
Opcode::Slice => {
let from_var = self.pop()?;
let to_var = self.pop()?;
let slice = self.pop()?;
match (from_var, to_var) {
(Number(f), Number(t)) => {
let from = f.to_usize().ok_or_else(|| OpcodeErr {
opcode: "Slice".into(),
message: "Failed to get range from".into(),
})?;
let to = t.to_usize().ok_or_else(|| OpcodeErr {
opcode: "Slice".into(),
message: "Failed to get range to".into(),
})?;
match slice {
Array(arr) => {
self.stack.push(self.bump.alloc(Array(&arr[from..=to])))
}
String(s) => {
self.stack.push(self.bump.alloc(String(&s[from..=to])))
}
_ => {
return Err(OpcodeErr {
opcode: "Slice".into(),
message: "Unsupported type".into(),
})
}
}
}
_ => {
return Err(OpcodeErr {
opcode: "Slice".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::Array => {
let size = self.pop()?;
let mut arr = Vec::new();
match size {
Number(s) => {
let to = s.round().to_usize().ok_or_else(|| OpcodeErr {
opcode: "Array".into(),
message: "Failed to extract argument".into(),
})?;
for _ in 0..to {
arr.push(self.pop()?);
}
}
_ => {
return Err(OpcodeErr {
opcode: "Array".into(),
message: "Unsupported type".into(),
})
}
}
arr.reverse();
self.stack.push(
self.bump
.alloc(Array(self.bump.alloc_slice_copy(arr.as_slice()))),
);
}
Opcode::Len => {
let current = self.stack.last().ok_or_else(|| OpcodeErr {
opcode: "Len".into(),
message: "Empty stack".into(),
})?;
let len = match current {
String(s) => s.len(),
Array(s) => s.len(),
_ => {
return Err(OpcodeErr {
opcode: "Len".into(),
message: "Unsupported type".into(),
})
}
};
self.stack.push(self.bump.alloc(Number(len.into())))
}
Opcode::Flatten => {
let current = self.pop()?;
let Array(arr) = current else {
return Err(OpcodeErr {
opcode: "Flatten".into(),
message: "Unsupported type".into(),
});
};
let mut flat_arr = Vec::new();
arr.iter().for_each(|&v| match v {
Array(arr) => arr.iter().for_each(|&v| flat_arr.push(v)),
_ => flat_arr.push(v),
});
self.stack.push(
self.bump
.alloc(Array(self.bump.alloc_slice_copy(flat_arr.as_slice()))),
)
}
Opcode::ParseDateTime => {
let a = self.pop()?;
let ts = match a {
String(a) => date_time(a)?.timestamp(),
Number(a) => a.to_i64().ok_or_else(|| OpcodeErr {
opcode: "ParseDateTime".into(),
message: "Number overflow".into(),
})?,
_ => {
return Err(OpcodeErr {
opcode: "ParseDateTime".into(),
message: "Unsupported type".into(),
})
}
};
self.stack.push(self.bump.alloc(Number(ts.into())))
}
Opcode::ParseTime => {
let a = self.pop()?;
let ts = match a {
String(a) => time(a)?.num_seconds_from_midnight(),
Number(a) => a.to_u32().ok_or_else(|| OpcodeErr {
opcode: "ParseTime".into(),
message: "Number overflow".into(),
})?,
_ => {
return Err(OpcodeErr {
opcode: "ParseTime".into(),
message: "Unsupported type".into(),
})
}
};
self.stack.push(self.bump.alloc(Number(ts.into())))
}
Opcode::ParseDuration => {
let a = self.pop()?;
let dur = match a {
String(a) => humantime::parse_duration(a)
.map_err(|_| ParseDateTimeErr {
timestamp: a.to_string(),
})?
.as_secs(),
Number(n) => n.to_u64().ok_or_else(|| OpcodeErr {
opcode: "ParseDuration".into(),
message: "Number overflow".into(),
})?,
_ => {
return Err(OpcodeErr {
opcode: "ParseDuration".into(),
message: "Unsupported type".into(),
})
}
};
self.stack.push(self.bump.alloc(Number(dur.into())));
}
Opcode::JumpIfEnd(j) => {
let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
opcode: "JumpIfEnd".into(),
message: "Empty stack".into(),
})?;
if scope.iter >= scope.len {
self.ip += j;
}
}
Opcode::IncrementIt => {
let scope = self.scopes.last_mut().ok_or_else(|| OpcodeErr {
opcode: "IncrementIt".into(),
message: "Empty scope".into(),
})?;
scope.iter += 1;
}
Opcode::IncrementCount => {
let scope = self.scopes.last_mut().ok_or_else(|| OpcodeErr {
opcode: "IncrementCount".into(),
message: "Empty scope".into(),
})?;
scope.count += 1;
}
Opcode::GetCount => {
let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
opcode: "GetCount".into(),
message: "Empty scope".into(),
})?;
self.push(Number(scope.count.into()));
}
Opcode::GetLen => {
let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
opcode: "GetLen".into(),
message: "Empty scope".into(),
})?;
self.push(Number(scope.len.into()));
}
Opcode::Pointer => {
let scope = self.scopes.last().ok_or_else(|| OpcodeErr {
opcode: "Pointer".into(),
message: "Empty scope".into(),
})?;
match scope.array {
Array(arr) => {
let variable = *arr.get(scope.iter).ok_or_else(|| OpcodeErr {
opcode: "Pointer".into(),
message: "Scope array out of bounds".into(),
})?;
self.push_ref(variable);
}
_ => {
return Err(OpcodeErr {
opcode: "Pointer".into(),
message: "Unsupported scope type".into(),
})
}
}
}
Opcode::Begin => {
let a = self.pop()?;
match a {
Array(arr) => self.scopes.push(Scope {
array: a,
count: 0,
len: arr.len(),
iter: 0,
}),
_ => {
return Err(OpcodeErr {
opcode: "Begin".into(),
message: "Unsupported type".into(),
})
}
}
}
Opcode::End => {
self.scopes.pop();
}
}
}
self.pop()
}
}