mod method_error;
mod params;
mod ps_string;
mod runtime_object;
mod script_block;
mod system_convert;
mod system_encoding;
mod val_error;
use std::{
collections::{BTreeMap, HashMap},
fmt::Debug,
ops::Neg,
};
mod class;
mod val_type;
pub(super) use class::{ClassProperties, ClassType, MethodName};
pub(crate) use method_error::{MethodError, MethodResult};
pub(crate) use params::Param;
pub(crate) use ps_string::PsString;
use ps_string::str_cmp;
pub(crate) use runtime_object::RuntimeError;
pub(super) use runtime_object::RuntimeObjectTrait;
use runtime_object::{MethodCallType, StaticFnCallType};
pub(crate) use script_block::ScriptBlock;
use smart_default::SmartDefault;
pub(crate) use system_convert::Convert;
pub(crate) use system_encoding::Encoding;
pub(crate) use val_error::ValError;
pub(crate) use val_type::RuntimeTypeTrait;
pub(super) use val_type::ValType;
pub type ValResult<T> = core::result::Result<T, ValError>;
use runtime_object::RuntimeResult;
use super::NEWLINE;
#[derive(Debug, SmartDefault)]
pub(crate) enum Val {
#[default]
Null,
Bool(bool),
Int(i64),
Float(f64),
Char(u32),
String(PsString),
Array(Vec<Val>),
HashTable(HashMap<String, Val>),
RuntimeObject(Box<dyn RuntimeObjectTrait>),
RuntimeType(Box<dyn RuntimeTypeTrait>),
ScriptBlock(ScriptBlock),
ScriptText(String),
NonDisplayed(Box<Val>),
}
impl std::fmt::Display for Val {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Val::Null
| Val::Char(_)
| Val::Bool(_)
| Val::Int(_)
| Val::Float(_)
| Val::String(_)
| Val::ScriptBlock(_)
| Val::ScriptText(_) => self.cast_to_string(),
Val::HashTable(h) => {
let mut s = vec![String::from("---- -----")];
for (k, v) in h {
s.push(format!("{:<30} {}", k, v.cast_to_string()));
}
s.join(NEWLINE)
}
Val::Array(ar) => ar
.iter()
.map(|v| v.to_string())
.collect::<Vec<String>>()
.join(NEWLINE),
Val::RuntimeObject(rt) => rt.to_string(),
Val::RuntimeType(rt) => rt.full_name(),
Val::NonDisplayed(_) => String::new(),
};
write!(f, "{}", str)
}
}
impl PartialEq for Val {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Val::Null, Val::Null) => true,
(Val::Bool(a), Val::Bool(b)) => a == b,
(Val::Int(a), Val::Int(b)) => a == b,
(Val::Float(a), Val::Float(b)) => a == b,
(Val::Char(a), Val::Char(b)) => a == b,
(Val::String(a), Val::String(b)) => a == b,
(Val::Array(a), Val::Array(b)) => a == b,
(Val::RuntimeObject(a), Val::RuntimeObject(b)) => a.name() == b.name(),
(Val::NonDisplayed(box_a), Val::NonDisplayed(box_b)) => *box_a == *box_b,
(Val::RuntimeType(rt1), Val::RuntimeType(rt2)) => rt1.name() == rt2.name(),
_ => false,
}
}
}
impl Clone for Val {
fn clone(&self) -> Self {
match self {
Val::Null => Val::Null,
Val::Bool(a) => Val::Bool(*a),
Val::Int(a) => Val::Int(*a),
Val::Float(a) => Val::Float(*a),
Val::Char(a) => Val::Char(*a),
Val::String(a) => Val::String(a.clone()),
Val::Array(a) => Val::Array(a.clone()),
Val::HashTable(a) => Val::HashTable(a.clone()),
Val::RuntimeObject(ro) => Val::RuntimeObject(ro.clone_rt()),
Val::RuntimeType(rt) => Val::RuntimeType(rt.clone_rt()),
Val::ScriptBlock(a) => Val::ScriptBlock(a.clone()),
Val::ScriptText(a) => Val::ScriptText(a.clone()),
Val::NonDisplayed(box_val) => Val::NonDisplayed(box_val.clone()),
}
}
}
impl Val {
fn not_defined(v1: &Val, v2: &Val, op: &str) -> ValError {
ValError::OperationNotDefined(
op.to_string(),
v1.ttype().to_string(),
v2.ttype().to_string(),
)
}
pub fn display(&self) -> String {
format!("{}", self)
}
pub fn eq(&self, val: Val, case_insensitive: bool) -> ValResult<bool> {
Ok(match self {
Val::Null => val.ttype() == ValType::Null,
Val::Bool(b) => *b == val.cast_to_bool(),
Val::Char(c) => *c == val.cast_to_char()?,
Val::Int(i) => *i == val.cast_to_int()?,
Val::Float(f) => *f == val.cast_to_float()?,
Val::String(PsString(s1)) => {
let s2 = val.cast_to_string();
str_cmp(s1, &s2, case_insensitive) == std::cmp::Ordering::Equal
}
Val::Array(_) => false,
Val::HashTable(ht1) => {
if let Val::HashTable(ht2) = val {
!ht1.is_empty() && !ht2.is_empty()
} else {
false
}
}
Val::RuntimeObject(s1) => {
if let Val::RuntimeObject(s2) = val {
str_cmp(&s1.name(), &s2.name(), case_insensitive) == std::cmp::Ordering::Equal
} else {
false
}
}
Val::RuntimeType(rt1) => {
if let Val::RuntimeType(rt2) = val {
rt1.name() == rt2.name()
} else {
false
}
}
Val::ScriptBlock(sb1) => {
if let Val::ScriptBlock(sb2) = val {
str_cmp(&sb1.raw_text, &sb2.raw_text, case_insensitive)
== std::cmp::Ordering::Equal
} else {
false
}
}
Val::ScriptText(_) => false,
Val::NonDisplayed(box_val) => box_val.lt(val, case_insensitive)?,
})
}
pub fn gt(&self, val: Val, case_insensitive: bool) -> ValResult<bool> {
Ok(match self {
Val::Null => false,
Val::Bool(b) => *b & !val.cast_to_bool(),
Val::Char(c) => *c > val.cast_to_char()?,
Val::Int(i) => *i > val.cast_to_int()?,
Val::Float(f) => *f > val.cast_to_float()?,
Val::String(PsString(s1)) => {
let s2 = val.cast_to_string();
str_cmp(s1, &s2, case_insensitive) == std::cmp::Ordering::Greater
}
Val::Array(_) => todo!(),
Val::HashTable(_) => false, Val::RuntimeObject(_) => todo!(),
Val::RuntimeType(_) => false, Val::ScriptBlock(_) => false, Val::ScriptText(_) => false,
Val::NonDisplayed(box_val) => box_val.gt(val, case_insensitive)?,
})
}
pub fn lt(&self, val: Val, case_insensitive: bool) -> ValResult<bool> {
Ok(match self {
Val::Null => false,
Val::Bool(b) => !(*b) & val.cast_to_bool(),
Val::Char(c) => *c < val.cast_to_char()?,
Val::Int(i) => *i < val.cast_to_int()?,
Val::Float(f) => *f < val.cast_to_float()?,
Val::String(PsString(s1)) => {
let s2 = val.cast_to_string();
str_cmp(s1, &s2, case_insensitive) == std::cmp::Ordering::Less
}
Val::Array(_) => todo!(),
Val::HashTable(_) => false, Val::RuntimeObject(_) => todo!(),
Val::RuntimeType(_) => false, Val::ScriptBlock(_) => false, Val::ScriptText(_) => false,
Val::NonDisplayed(box_val) => box_val.lt(val, case_insensitive)?,
})
}
pub fn ttype(&self) -> ValType {
match self {
Val::Null => ValType::Null,
Val::Bool(_) => ValType::Bool,
Val::Int(_) => ValType::Int,
Val::Float(_) => ValType::Float,
Val::Char(_) => ValType::Char,
Val::String(_) => ValType::String,
Val::Array(_) => ValType::Array(None),
Val::HashTable(_) => ValType::HashTable,
Val::ScriptBlock(_) => ValType::ScriptBlock,
Val::ScriptText(_) => ValType::ScriptText,
Val::RuntimeObject(rt) => ValType::RuntimeObject(rt.to_string()),
Val::RuntimeType(_) => ValType::TypeInfo,
Val::NonDisplayed(box_val) => box_val.ttype(),
}
}
pub fn add(&mut self, val: Val) -> ValResult<()> {
match self {
Val::Null => *self = val,
Val::Bool(_) | Val::Int(_) | Val::Float(_) => {
*self = if val.ttype() == ValType::Float {
Val::Float(self.cast_to_float()? + val.cast_to_float()?)
} else {
Val::Int(self.cast_to_int()? + val.cast_to_int()?)
};
}
Val::Char(_) | Val::String(_) => {
*self = Val::String(PsString(
self.cast_to_string() + val.cast_to_string().as_str(),
))
}
Val::Array(arr) => {
if let Val::Array(val_arr) = val {
arr.extend(val_arr);
} else {
arr.push(val);
}
}
Val::HashTable(ht) => {
if val.ttype() != ValType::HashTable {
return Err(ValError::OperationNotDefined(
"add".to_string(),
self.ttype().to_string(),
val.ttype().to_string(),
));
} else {
ht.extend(val.cast_to_hashtable()?);
}
}
Val::NonDisplayed(box_val) => box_val.add(val)?,
_ => {
return Err(ValError::OperationNotDefined(
"add".to_string(),
self.ttype().to_string(),
val.ttype().to_string(),
));
}
}
Ok(())
}
fn inc_or_dec_operation(&mut self, amount: i64, op: String) -> ValResult<()> {
match self {
Val::Null => *self = Val::Int(amount),
Val::Int(i) => *self = Val::Int(*i + amount),
Val::Float(f) => *self = Val::Float(*f + amount as f64),
Val::NonDisplayed(box_val) => box_val.inc_or_dec_operation(amount, op)?,
_ => {
Err(ValError::OperationNotDefined(
op,
self.ttype().to_string(),
self.ttype().to_string(),
))?
}
}
Ok(())
}
pub fn inc(&mut self) -> ValResult<()> {
self.inc_or_dec_operation(1, "++".to_string())
}
pub fn dec(&mut self) -> ValResult<()> {
self.inc_or_dec_operation(-1, "--".to_string())
}
pub fn sub(&mut self, val: Val) -> ValResult<()> {
if let ValType::Null = self.ttype() {
*self = val.ttype().init();
self.sub(val)?;
return Ok(());
}
if let ValType::RuntimeObject(_) = self.ttype() {
Err(Self::not_defined(self, &val, "-"))?
}
if let ValType::RuntimeObject(_) = val.ttype() {
Err(Self::not_defined(self, &val, "-"))?
}
if let ValType::TypeInfo = self.ttype() {
Err(Self::not_defined(self, &val, "-"))?
}
if let ValType::TypeInfo = val.ttype() {
Err(Self::not_defined(self, &val, "-"))?
}
if let ValType::Array(_) = self.ttype() {
Err(Self::not_defined(self, &val, "-"))?
}
if let ValType::Array(_) = val.ttype() {
Err(Self::not_defined(self, &val, "-"))?
}
if self.ttype() == ValType::ScriptBlock || val.ttype() == ValType::ScriptBlock {
Err(Self::not_defined(self, &val, "-"))?
}
if self.ttype() == ValType::Float || val.ttype() == ValType::Float {
*self = Val::Float(self.cast_to_float()? - val.cast_to_float()?);
} else {
*self = Val::Int(self.cast_to_int()? - val.cast_to_int()?);
}
Ok(())
}
pub fn mul(&mut self, val: Val) -> ValResult<()> {
let res = match self {
Val::Null => Ok(self.clone()),
Val::Bool(_) => Err(Self::not_defined(self, &val, "*")),
Val::Int(_) | Val::Float(_) => {
if self.ttype() == ValType::Float || val.ttype() == ValType::Float {
Ok(Val::Float(self.cast_to_float()? * val.cast_to_float()?))
} else {
Ok(Val::Int(self.cast_to_int()? * val.cast_to_int()?))
}
}
Val::Char(_) => Err(ValError::OperationNotDefined(
"*".to_string(),
"Char".to_string(),
val.ttype().to_string(),
))?,
Val::String(PsString(s)) => {
let repeat_count = val.cast_to_int()?;
if repeat_count < 0 {
Err(ValError::ArgumentOutOfRange("*".to_string(), repeat_count))?
}
Ok(Val::String(PsString(s.repeat(repeat_count as usize))))
}
Val::Array(v) => {
let repeat_count = val.cast_to_int()?;
if repeat_count < 0 {
Err(ValError::ArgumentOutOfRange("*".to_string(), repeat_count))?
}
Ok(Val::Array(Self::repeat(v, repeat_count as usize)))
}
_ => Err(ValError::OperationNotDefined(
"*".to_string(),
self.ttype().to_string(),
val.ttype().to_string(),
)),
};
*self = res?;
Ok(())
}
pub fn div(&mut self, val: Val) -> ValResult<()> {
if let Val::Array(_) = self {
Err(Self::not_defined(self, &val, "/"))?
}
if let Val::Array(_) = &val {
Err(Self::not_defined(self, &val, "/"))?
}
if let Ok(v) = val.cast_to_float()
&& v == 0.
{
Err(ValError::DividingByZero)?
}
*self = match self {
Val::Null => Val::Int(0),
Val::Bool(_) | Val::Int(_) | Val::Char(_) | Val::String(_) => {
if val.ttype() != ValType::Float && (self.cast_to_int()? % val.cast_to_int()? == 0)
{
Val::Int(self.cast_to_int()? / val.cast_to_int()?)
} else {
Val::Float(self.cast_to_float()? / val.cast_to_float()?)
}
}
Val::Float(_) => Val::Float(self.cast_to_float()? / self.cast_to_float()?),
_ => Err(ValError::OperationNotDefined(
"/".to_string(),
self.ttype().to_string(),
val.ttype().to_string(),
))?,
};
Ok(())
}
pub fn modulo(&mut self, val: Val) -> ValResult<()> {
if let Val::Array(_) = self {
Err(Self::not_defined(self, &val, "%"))?
}
if let Val::Array(_) = &val {
Err(Self::not_defined(self, &val, "%"))?
}
if let Ok(v) = val.cast_to_float()
&& v == 0.
{
Err(ValError::DividingByZero)?
}
*self = match self {
Val::Null => Val::Int(0),
Val::Bool(_) | Val::Int(_) | Val::Char(_) | Val::String(_) => {
if val.ttype() != ValType::Float {
Val::Int(self.cast_to_int()? % val.cast_to_int()?)
} else {
Val::Float(self.cast_to_float()? % val.cast_to_float()?)
}
}
Val::Float(_) => Val::Float(self.cast_to_float()? % self.cast_to_float()?),
_ => Err(ValError::OperationNotDefined(
"%".to_string(),
self.ttype().to_string(),
val.ttype().to_string(),
))?,
};
Ok(())
}
pub fn neg(&mut self) -> ValResult<()> {
match self {
Val::Float(f) => *f = f.neg(),
Val::Null | Val::Bool(_) | Val::Int(_) | Val::Char(_) | Val::String(_) => {
*self = Val::Int(self.cast_to_int()?.neg())
}
Val::NonDisplayed(box_val) => box_val.neg()?,
_ => Err(ValError::OperationNotDefined(
"-".to_string(),
self.ttype().to_string(),
self.ttype().to_string(),
))?,
}
Ok(())
}
pub(crate) fn cast(&self, runtime_type: &Val) -> ValResult<Self> {
let Val::RuntimeType(rt) = runtime_type else {
return Err(ValError::InvalidCast(
self.ttype().to_string(),
"RuntimeType".to_string(),
))?;
};
self.cast_from_type(&rt.type_definition())
}
pub(crate) fn cast_from_type(&self, ttype: &ValType) -> ValResult<Self> {
Ok(match ttype {
ValType::Null => Err(ValError::UnknownType("Null".to_string()))?,
ValType::Bool => Val::Bool(self.cast_to_bool()),
ValType::Int => Val::Int(self.cast_to_int()?),
ValType::Float => Val::Float(self.cast_to_float()?),
ValType::Char => Val::Char(self.cast_to_char()?),
ValType::String => Val::String(PsString(self.cast_to_string())),
ValType::Array(ttype) => Val::Array(self.cast_to_typed_array(ttype.clone())?),
ValType::HashTable => Val::HashTable(self.cast_to_hashtable()?),
ValType::ScriptBlock => Val::ScriptBlock(self.cast_to_scriptblock()?),
ValType::ScriptText => Val::ScriptText(self.cast_to_script()),
ValType::RuntimeObject(_) => Err(ValError::InvalidCast(
self.ttype().to_string(),
"RuntimeObject".to_string(),
))?,
ValType::TypeInfo => Err(ValError::InvalidCast(
self.ttype().to_string(),
"TypeInfo".to_string(),
))?,
ValType::Switch => Err(ValError::InvalidCast(
self.ttype().to_string(),
"Switch".to_string(),
))?,
})
}
pub(crate) fn cast_to_bool(&self) -> bool {
match self {
Val::Null => false,
Val::Bool(b) => *b,
Val::Char(c) => *c != 0,
Val::Int(i) => *i != 0,
Val::Float(f) => *f != 0.,
Val::String(PsString(s)) => !s.is_empty(),
Val::Array(v) => !v.is_empty(),
Val::HashTable(h) => !h.is_empty(),
Val::RuntimeObject(rt) => !rt.name().is_empty(),
Val::RuntimeType(_rt) => true,
Val::ScriptBlock(_) => true,
Val::ScriptText(st) => !st.is_empty(),
Val::NonDisplayed(box_val) => box_val.cast_to_bool(),
}
}
fn cast_to_char(&self) -> ValResult<u32> {
let res = match self {
Val::Null | Val::Int(_) | Val::Char(_) => self.cast_to_int()? as u32,
Val::Bool(_) => Err(ValError::InvalidCast(
"Bool".to_string(),
"Char".to_string(),
))?,
Val::Float(_) => Err(ValError::InvalidCast(
"Float".to_string(),
"Char".to_string(),
))?,
Val::String(PsString(s)) => {
if s.len() == 1 {
s.chars().next().unwrap_or_default() as u32
} else {
Err(ValError::InvalidCast(
"String with len() more than 1".to_string(),
"Char".to_string(),
))?
}
}
Val::Array(_) => Err(ValError::InvalidCast(
"Array".to_string(),
"Char".to_string(),
))?,
Val::HashTable(_) => Err(ValError::InvalidCast(
"HashTable".to_string(),
"Char".to_string(),
))?,
Val::RuntimeObject(_) => {
Err(ValError::InvalidCast(self.to_string(), "Char".to_string()))?
}
Val::RuntimeType(_) => {
Err(ValError::InvalidCast(self.to_string(), "Char".to_string()))?
}
Val::ScriptBlock(_) => Err(ValError::InvalidCast(
"ScriptBlock".to_string(),
"Char".to_string(),
))?,
Val::ScriptText(_) => Err(ValError::InvalidCast(
"ScriptText".to_string(),
"Char".to_string(),
))?,
Val::NonDisplayed(box_val) => box_val.cast_to_char()?,
};
Ok(res)
}
pub(crate) fn cast_to_int(&self) -> ValResult<i64> {
Ok(match self {
Val::Null => 0,
Val::Bool(b) => *b as i64,
Val::Int(i) => *i,
Val::Float(f) => f.round() as i64,
Val::Char(c) => *c as i64,
Val::String(PsString(s)) => {
let s = s.to_ascii_lowercase();
if let Some(hex) = s.strip_prefix("0x") {
i64::from_str_radix(hex, 16)?
} else if let Ok(casted) = s.trim().parse::<f64>() {
Self::round_bankers(casted) as i64
} else {
s.trim()
.parse::<i64>()
.map_err(|_| ValError::InvalidCast(format!("\"{s}\""), "Int".to_string()))?
}
}
Val::Array(_) => Err(ValError::InvalidCast(
"Array".to_string(),
"Int".to_string(),
))?,
Val::HashTable(_) => Err(ValError::InvalidCast(
"HashTable".to_string(),
"Int".to_string(),
))?,
Val::RuntimeObject(_) => {
Err(ValError::InvalidCast(self.to_string(), "Int".to_string()))?
}
Val::RuntimeType(_) => Err(ValError::InvalidCast(self.to_string(), "Int".to_string()))?,
Val::ScriptBlock(_) => Err(ValError::InvalidCast(
"ScriptBlock".to_string(),
"Int".to_string(),
))?,
Val::ScriptText(_) => Err(ValError::InvalidCast(
"ScriptText".to_string(),
"Int".to_string(),
))?,
Val::NonDisplayed(box_val) => box_val.cast_to_int()?,
})
}
pub(crate) fn cast_to_float(&self) -> ValResult<f64> {
Ok(match self {
Val::Null => 0.,
Val::Bool(b) => *b as i64 as f64,
Val::Int(i) => *i as f64,
Val::Float(f) => *f,
Val::Char(c) => *c as f64,
Val::String(PsString(s)) => s
.trim()
.parse::<f64>()
.map_err(|_| ValError::InvalidCast(format!("\"{s}\""), "Float".to_string()))?,
Val::Array(_) => Err(ValError::InvalidCast(
"Array".to_string(),
"Float".to_string(),
))?,
Val::HashTable(_) => Err(ValError::InvalidCast(
"HashTable".to_string(),
"Float".to_string(),
))?,
Val::RuntimeObject(_) => {
Err(ValError::InvalidCast(self.to_string(), "Float".to_string()))?
}
Val::RuntimeType(_) => Err(ValError::InvalidCast(
self.to_string(),
"InFloatt".to_string(),
))?,
Val::ScriptBlock(_) => Err(ValError::InvalidCast(
"ScriptBlock".to_string(),
"Float".to_string(),
))?,
Val::ScriptText(_) => Err(ValError::InvalidCast(
"ScriptText".to_string(),
"Float".to_string(),
))?,
Val::NonDisplayed(box_val) => box_val.cast_to_float()?,
})
}
pub(super) fn cast_to_string(&self) -> String {
match self {
Val::Null => String::new(),
Val::Bool(b) => String::from(if *b { "True" } else { "False" }),
Val::Int(i) => i.to_string(),
Val::Float(f) => f.to_string(),
Val::Char(c) => char::from_u32(*c).unwrap_or_default().to_string(),
Val::String(PsString(s)) => s.clone(),
Val::Array(v) => v
.iter()
.map(|val| val.cast_to_string())
.collect::<Vec<String>>()
.join(" "),
Val::HashTable(_) => "System.Collections.Hashtable".to_string(),
Val::RuntimeObject(s) => s.to_string(),
Val::RuntimeType(_) => self.to_string(),
Val::ScriptBlock(sb) => sb.to_string(),
Val::ScriptText(st) => st.clone(),
Val::NonDisplayed(box_val) => box_val.cast_to_string(),
}
}
pub(super) fn cast_to_join_string(&self) -> String {
if let Val::Array(_) = self {
"System.Object[]".to_string()
} else {
self.cast_to_string()
}
}
pub(crate) fn cast_to_typed_array(&self, ttype: Option<Box<ValType>>) -> ValResult<Vec<Self>> {
if let Some(ttype) = &ttype
&& **ttype == ValType::String
{
return Ok(self
.to_string()
.split_ascii_whitespace()
.map(|s| Val::String(s.into()))
.collect());
}
let mut arr = match self {
Val::Null => vec![],
Val::Bool(_) | Val::Int(_) | Val::Float(_) | Val::Char(_) | Val::String(_) => {
vec![self.clone()]
}
Val::Array(v) => v.clone(),
Val::HashTable(_) => vec![self.clone()],
Val::RuntimeObject(a) => vec![Val::String(a.name().into())],
Val::ScriptBlock(sb) => vec![Val::String(sb.to_string().into())],
Val::ScriptText(s) => vec![Val::String(s.clone().into())],
Val::NonDisplayed(s) => s.cast_to_typed_array(ttype.clone())?,
_ => Err(ValError::InvalidCast(
self.ttype().to_string(),
"Array".to_string(),
))?,
};
if let Some(ttype) = ttype {
for elem in arr.iter_mut() {
*elem = elem.cast_from_type(&ttype)?;
}
}
Ok(arr)
}
pub(crate) fn cast_to_array(&self) -> Vec<Self> {
self.cast_to_typed_array(None).unwrap_or_default()
}
pub(crate) fn into_array(self) -> Vec<Self> {
if let Val::Array(v) = self {
v.clone()
} else {
vec![self.clone()]
}
}
fn repeat(v: &[Val], amount: usize) -> Vec<Val> {
let mut res = v.to_owned();
for _ in 1..amount {
res.append(&mut v.to_owned());
}
res
}
fn round_bankers(x: f64) -> f64 {
let rounded = x.trunc();
let frac = x.fract().abs();
if frac > 0.5 {
if x.is_sign_positive() {
rounded + 1.0
} else {
rounded - 1.0
}
} else if frac < 0.5 {
rounded
} else {
if rounded as i64 % 2 == 0 {
rounded
} else if x.is_sign_positive() {
rounded + 1.0
} else {
rounded - 1.0
}
}
}
pub(crate) fn cast_to_hashtable(&self) -> ValResult<HashMap<String, Val>> {
if let Val::HashTable(h) = self {
Ok(h.clone())
} else {
Err(ValError::InvalidCast(
self.ttype().to_string(),
"HashTable".to_string(),
))
}
}
pub(crate) fn cast_to_scriptblock(&self) -> ValResult<ScriptBlock> {
if let Val::ScriptBlock(sb) = self {
Ok(sb.clone())
} else {
Err(ValError::InvalidCast(
self.ttype().to_string(),
"ScriptBlock".to_string(),
))
}
}
pub fn get_index_ref(&mut self, index: Val) -> ValResult<&mut Val> {
let self_string = self.to_string();
match self {
Val::Null => Err(ValError::IndexedNullArray)?,
Val::Array(v) => {
let i = index.cast_to_int()? as usize;
if v.len() > i {
Ok(&mut v[i])
} else {
Err(RuntimeError::IndexOutOfBounds(self_string, i).into())
}
}
Val::HashTable(v) => v
.get_mut(&index.cast_to_string().to_ascii_lowercase())
.ok_or(RuntimeError::MemberNotFound(index.cast_to_string()).into()),
_ => {
if let Ok(i) = index.cast_to_int() {
if i == 0 {
Ok(self)
} else {
Err(RuntimeError::IndexOutOfBounds(self_string, i as usize).into())
}
} else {
let member_name = index.cast_to_string();
Err(RuntimeError::MemberNotFound(member_name).into())
}
}
}
}
pub fn get_index(&self, index: Val) -> ValResult<Val> {
let self_string = self.to_string();
match self {
Val::Null => Err(ValError::IndexedNullArray)?,
Val::Array(v) => {
let i = index.cast_to_int()? as usize;
if v.len() > i {
Ok(v[i].clone())
} else {
Err(RuntimeError::IndexOutOfBounds(self_string, i).into())
}
}
Val::String(PsString(s)) => {
let i = index.cast_to_int()? as usize;
let Some(c) = s.chars().nth(i) else {
return Err(RuntimeError::IndexOutOfBounds(self_string, i).into());
};
Ok(Val::Char(c as u32))
}
Val::HashTable(v) => v
.get(&index.cast_to_string().to_ascii_lowercase())
.cloned()
.ok_or(RuntimeError::MemberNotFound(index.cast_to_string()).into()),
_ => {
if let Ok(i) = index.cast_to_int() {
if i == 0 {
Ok(self.clone())
} else {
Err(RuntimeError::IndexOutOfBounds(self_string, i as usize).into())
}
} else {
let member_name = index.cast_to_string();
Err(RuntimeError::MemberNotFound(member_name).into())
}
}
}
}
pub fn flatten(&self) -> Vec<Self> {
match self {
Val::Array(v) => {
let mut res = vec![];
for item in v {
res.append(&mut item.flatten());
}
res
}
Val::HashTable(_) => vec![Val::String(self.cast_to_string().into())],
_ => self.cast_to_array(),
}
}
pub(super) fn cast_to_script(&self) -> String {
log::trace!("cast_to_script {:?}", self);
match self {
Val::Null => "$null".to_string(),
Val::Bool(b) => String::from(if *b { "$true" } else { "$false" }),
Val::Int(i) => i.to_string(),
Val::Float(f) => f.to_string(),
Val::Char(c) => format!("'{}'", char::from_u32(*c).unwrap_or_default()),
Val::String(PsString(s)) => format!("\"{}\"", s),
Val::Array(v) => {
let inner = v
.iter()
.map(|val| val.cast_to_script())
.collect::<Vec<String>>()
.join(",");
format!("@({})", inner)
}
Val::HashTable(h) => {
let tree_map = BTreeMap::from_iter(h.clone());
let inner = tree_map
.iter()
.map(|(k, v)| format!("\t{} = {}", k, v.cast_to_script()))
.collect::<Vec<String>>()
.join(NEWLINE);
format!("@{{{NEWLINE}{}{NEWLINE}}}", inner)
}
Val::RuntimeObject(s) => s.to_string(),
Val::RuntimeType(s) => format!("[{}]", s.full_name()),
Val::ScriptBlock(sb) => format!("{{{}}}", sb),
Val::ScriptText(st) => st.clone(),
Val::NonDisplayed(box_val) => (*box_val).cast_to_script(),
}
}
}
impl From<&str> for Val {
fn from(value: &str) -> Self {
Self::String(PsString(value.into()))
}
}
impl From<String> for Val {
fn from(value: String) -> Self {
value.as_str().into()
}
}
impl From<&String> for Val {
fn from(value: &String) -> Self {
value.as_str().into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
let mut val = Val::Int(4);
val.add(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::Float(4.1));
let mut val = Val::String(" 123".into());
val.add(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::String(" 1230.1".into()));
let mut val = Val::Char(97);
val.add(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::String("a0.1".into()));
let mut val = Val::Int(4);
val.add(Val::Int(1)).unwrap();
assert_eq!(val, Val::Int(5));
let mut val = Val::String(" 123".into());
val.add(Val::Int(1)).unwrap();
assert_eq!(val, Val::String(" 1231".into()));
let mut val = Val::Char(97);
val.add(Val::Int(1)).unwrap();
assert_eq!(val, Val::String("a1".into()));
let mut val = Val::Char(97);
val.add(Val::Int(1)).unwrap();
assert_eq!(val, Val::String("a1".into()));
let mut val = Val::String(" 123".into());
val.add(Val::Int(1)).unwrap();
assert_eq!(val, Val::String(" 1231".into()));
let mut val = Val::Char(97);
val.add(Val::String("bsef".into())).unwrap();
assert_eq!(val, Val::String("absef".into()));
let mut val = Val::Array(vec![Val::Int(7), Val::String(" adsf".into())]);
val.add(Val::Float(2.3)).unwrap();
assert_eq!(
val,
Val::Array(vec![
Val::Int(7),
Val::String(" adsf".into()),
Val::Float(2.3)
])
);
}
#[test]
fn test_sub() {
let mut val = Val::Int(4);
val.sub(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::Float(3.9));
let mut val = Val::String(" 123".into());
val.sub(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::Float(122.9));
let mut val = Val::Char(123);
val.sub(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::Float(122.9));
let mut val = Val::Int(4);
val.sub(Val::Int(1)).unwrap();
assert_eq!(val, Val::Int(3));
let mut val = Val::String(" 123".into());
val.sub(Val::Int(1)).unwrap();
assert_eq!(val, Val::Int(122));
let mut val = Val::Char(123);
val.sub(Val::Int(1)).unwrap();
assert_eq!(val, Val::Int(122));
}
#[test]
fn test_mul() {
let mut val = Val::Int(4);
val.mul(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::Float(0.4));
let mut val = Val::String(" 123".into());
val.mul(Val::Float(0.1)).unwrap();
assert_eq!(val, Val::String("".into()));
let mut val = Val::String(" 123".into());
val.mul(Val::Float(2.1)).unwrap();
assert_eq!(val, Val::String(" 123 123".into()));
let mut val = Val::Int(4);
val.mul(Val::Int(1)).unwrap();
assert_eq!(val, Val::Int(4));
let mut val = Val::String(" 123".into());
val.mul(Val::Int(2)).unwrap();
assert_eq!(val, Val::String(" 123 123".into()));
let mut val = Val::Array(vec![Val::Int(7), Val::String(" adsf".into())]);
val.mul(Val::Int(2)).unwrap();
assert_eq!(
val,
Val::Array(vec![
Val::Int(7),
Val::String(" adsf".into()),
Val::Int(7),
Val::String(" adsf".into())
])
);
let mut val = Val::Array(vec![Val::Int(7), Val::String(" adsf".into())]);
val.mul(Val::Float(2.3)).unwrap();
assert_eq!(
val,
Val::Array(vec![
Val::Int(7),
Val::String(" adsf".into()),
Val::Int(7),
Val::String(" adsf".into())
])
);
}
#[test]
fn test_cast_to_bool() {
assert_eq!(Val::Null.cast_to_bool(), false);
assert_eq!(Val::Bool(true).cast_to_bool(), true);
assert_eq!(Val::Bool(false).cast_to_bool(), false);
assert_eq!(Val::Int(-4).cast_to_bool(), true);
assert_eq!(Val::Int(0).cast_to_bool(), false);
assert_eq!(Val::Int(123456).cast_to_bool(), true);
assert_eq!(Val::Float(0.).cast_to_bool(), false);
assert_eq!(Val::Float(0.09874).cast_to_bool(), true);
assert_eq!(Val::Float(-0.09874).cast_to_bool(), true);
assert_eq!(Val::Char(0).cast_to_bool(), false);
assert_eq!(Val::Char(97).cast_to_bool(), true);
assert_eq!(Val::String("a".into()).cast_to_bool(), true);
assert_eq!(Val::String(" 888 a".into()).cast_to_bool(), true);
assert_eq!(Val::String("".into()).cast_to_bool(), false);
assert_eq!(Val::Array(vec![]).cast_to_bool(), false);
assert_eq!(Val::Array(vec![Val::Int(7)]).cast_to_bool(), true);
}
#[test]
fn test_cast_to_char() {
assert_eq!(Val::Null.cast_to_char().unwrap(), 0);
assert_eq!(
Val::Bool(true).cast_to_char().unwrap_err(),
ValError::InvalidCast("Bool".to_string(), "Char".to_string())
);
assert_eq!(
Val::Bool(false).cast_to_char().unwrap_err(),
ValError::InvalidCast("Bool".to_string(), "Char".to_string())
);
assert_eq!(Val::Int(123456).cast_to_char().unwrap(), 123456);
assert_eq!(Val::Int(-123456).cast_to_char().unwrap(), 4294843840);
assert_eq!(
Val::Float(0.09874).cast_to_char().unwrap_err(),
ValError::InvalidCast("Float".to_string(), "Char".to_string())
);
assert_eq!(
Val::Float(-0.09874).cast_to_char().unwrap_err(),
ValError::InvalidCast("Float".to_string(), "Char".to_string())
);
assert_eq!(Val::Char(97).cast_to_char().unwrap(), 97);
assert_eq!(Val::String("a".into()).cast_to_char().unwrap(), 97);
assert_eq!(
Val::String(" 888 a".into()).cast_to_char().unwrap_err(),
ValError::InvalidCast(
"String with len() more than 1".to_string(),
"Char".to_string()
)
);
assert_eq!(
Val::Array(vec![Val::Char(7)]).cast_to_char().unwrap_err(),
ValError::InvalidCast("Array".to_string(), "Char".to_string())
);
}
#[test]
fn test_cast_to_int() {
assert_eq!(Val::Null.cast_to_int().unwrap(), 0);
assert_eq!(Val::Bool(true).cast_to_int().unwrap(), 1);
assert_eq!(Val::Bool(false).cast_to_int().unwrap(), 0);
assert_eq!(Val::Int(123456).cast_to_int().unwrap(), 123456);
assert_eq!(Val::Int(-123456).cast_to_int().unwrap(), -123456);
assert_eq!(Val::Float(0.09874).cast_to_int().unwrap(), 0);
assert_eq!(Val::Float(-0.09874).cast_to_int().unwrap(), 0);
assert_eq!(Val::Char(97).cast_to_int().unwrap(), 97);
assert_eq!(Val::String("00001".into()).cast_to_int().unwrap(), 1);
assert_eq!(Val::String(" 888 ".into()).cast_to_int().unwrap(), 888);
assert_eq!(
Val::String(" 888 a".into()).cast_to_int().unwrap_err(),
ValError::InvalidCast("\" 888 a\"".to_string(), "Int".to_string())
);
assert_eq!(
Val::Array(vec![Val::Int(7)]).cast_to_int().unwrap_err(),
ValError::InvalidCast("Array".to_string(), "Int".to_string())
);
}
#[test]
fn test_cast_to_float() {
assert_eq!(Val::Null.cast_to_float().unwrap(), 0.);
assert_eq!(Val::Bool(true).cast_to_float().unwrap(), 1.);
assert_eq!(Val::Bool(false).cast_to_float().unwrap(), 0.);
assert_eq!(Val::Int(123456).cast_to_float().unwrap(), 123456.);
assert_eq!(Val::Int(-123456).cast_to_float().unwrap(), -123456.);
assert_eq!(Val::Float(0.09874).cast_to_float().unwrap(), 0.09874);
assert_eq!(Val::Float(-0.09874).cast_to_float().unwrap(), -0.09874);
assert_eq!(Val::Char(97).cast_to_float().unwrap(), 97.);
assert_eq!(Val::String("00001.".into()).cast_to_float().unwrap(), 1.);
assert_eq!(
Val::String("00001.12".into()).cast_to_float().unwrap(),
1.12
);
assert_eq!(
Val::String(" 888.123 ".into()).cast_to_float().unwrap(),
888.123
);
assert_eq!(
Val::String(" 888 a".into()).cast_to_float().unwrap_err(),
ValError::InvalidCast("\" 888 a\"".to_string(), "Float".to_string())
);
assert_eq!(
Val::Array(vec![Val::Float(7.)])
.cast_to_float()
.unwrap_err(),
ValError::InvalidCast("Array".to_string(), "Float".to_string())
);
}
#[test]
fn test_cast_to_string() {
assert_eq!(Val::Null.cast_to_string(), "".to_string());
assert_eq!(Val::Bool(true).cast_to_string(), "True".to_string());
assert_eq!(Val::Bool(false).cast_to_string(), "False".to_string());
assert_eq!(Val::Int(123456).cast_to_string(), "123456".to_string());
assert_eq!(Val::Int(-123456).cast_to_string(), "-123456".to_string());
assert_eq!(Val::Float(1.).cast_to_string(), "1".to_string());
assert_eq!(Val::Float(0.09874).cast_to_string(), "0.09874".to_string());
assert_eq!(
Val::Float(-0.09874).cast_to_string(),
"-0.09874".to_string()
);
assert_eq!(Val::Char(97).cast_to_string(), "a".to_string());
assert_eq!(Val::Char(9997).cast_to_string(), "\u{270D}".to_string());
assert_eq!(
Val::Array(vec![Val::Int(7), Val::Null, Val::String(" adsf".into())]).cast_to_string(),
"7 adsf".to_string()
);
}
#[test]
fn test_cast_to_array() {
assert_eq!(Val::Null.cast_to_array(), vec![]);
assert_eq!(Val::Bool(true).cast_to_array(), vec![Val::Bool(true)]);
assert_eq!(
Val::Float(0.09874).cast_to_array(),
vec![Val::Float(0.09874)]
);
assert_eq!(Val::Char(5).cast_to_array(), vec![Val::Char(5)]);
assert_eq!(
Val::String("elo".into()).cast_to_array(),
vec![Val::String("elo".into())]
);
assert_eq!(
Val::Array(vec![Val::Int(7)]).cast_to_array(),
vec![Val::Int(7)]
);
assert_eq!(
Val::Array(vec![Val::Int(7)])
.cast_to_typed_array(Some(Box::new(ValType::String)))
.unwrap(),
vec![Val::String("7".into())]
);
}
}