use super::VM;
use crate::nan_value::NanValue;
use crate::vm::symbol::VmSymbolKind;
use crate::vm::types::VmError;
impl VM {
pub(super) fn decode_vm_symbol_id(&self, val: NanValue) -> Option<u32> {
if !val.is_int() {
return None;
}
let symbol_id = val.as_int(&self.arena);
(symbol_id >= 0).then_some(symbol_id as u32).filter(|&id| {
self.code
.symbols
.get(id)
.is_some_and(|info| info.kind.is_some())
})
}
pub(super) fn nan_tag(&self, val: NanValue) -> u8 {
if val.is_float() {
return 0xFF;
}
((val.bits() >> 46) & 0xF) as u8
}
pub(super) fn decode_vm_fn_ref(
&self,
val: NanValue,
caller_fn_id: u32,
ip: usize,
) -> Result<u32, VmError> {
if let Some(symbol_id) = self.decode_vm_symbol_id(val)
&& let Some(fn_id) = self.code.symbols.resolve_function(symbol_id)
{
return Ok(fn_id);
}
let caller_name = &self.code.functions[caller_fn_id as usize].name;
Err(VmError::type_err(format!(
"cannot call non-function (got {} = {:?}) in {} at ip={}",
self.value_type_name(val),
self.value_repr(val),
caller_name,
ip
)))
}
pub(super) fn variant_ctor_id_vm(&self, val: NanValue) -> Option<u32> {
if let Some(symbol_id) = self.decode_vm_symbol_id(val)
&& let Some(ctor) = self.code.symbols.resolve_variant_ctor(symbol_id)
&& ctor.field_count == 0
{
return Some(ctor.ctor_id);
}
val.variant_ctor_id(&self.arena)
}
pub(super) fn value_type_name(&self, val: NanValue) -> String {
if let Some(symbol_id) = self.decode_vm_symbol_id(val)
&& let Some(info) = self.code.symbols.get(symbol_id)
&& let Some(kind) = info.kind
{
return match kind {
VmSymbolKind::Function(_) => "Fn".to_string(),
VmSymbolKind::Builtin(_) => "Builtin".to_string(),
VmSymbolKind::Namespace => "Namespace".to_string(),
VmSymbolKind::VariantCtor(ctor) => {
if ctor.field_count == 0 {
"Variant".to_string()
} else {
"VariantCtor".to_string()
}
}
VmSymbolKind::Wrapper(kind) => match kind {
0 => "Result.Ok".to_string(),
1 => "Result.Err".to_string(),
2 => "Option.Some".to_string(),
_ => "Wrapper".to_string(),
},
VmSymbolKind::Constant(value) => value.type_name().to_string(),
};
}
val.type_name().to_string()
}
pub(super) fn value_repr(&self, val: NanValue) -> String {
if let Some(symbol_id) = self.decode_vm_symbol_id(val)
&& let Some(info) = self.code.symbols.get(symbol_id)
&& let Some(kind) = info.kind
{
return match kind {
VmSymbolKind::Function(_) => {
format!("<fn {}>", info.name)
}
VmSymbolKind::Builtin(_) => {
format!("<builtin {}>", info.name)
}
VmSymbolKind::Namespace => {
format!("<type {}>", info.name)
}
VmSymbolKind::VariantCtor(ctor) => {
if ctor.field_count == 0 {
info.name
.rsplit('.')
.next()
.unwrap_or(info.name.as_str())
.to_string()
} else {
format!("<ctor {}>", info.name)
}
}
VmSymbolKind::Wrapper(_) => {
format!("<ctor {}>", info.name)
}
VmSymbolKind::Constant(value) => value.repr(&self.arena),
};
}
val.repr(&self.arena)
}
pub(super) fn arith_add(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, VmError> {
if a.is_int() && b.is_int() {
Ok(NanValue::new_int(
a.as_int(&self.arena) + b.as_int(&self.arena),
&mut self.arena,
))
} else if a.is_float() && b.is_float() {
Ok(NanValue::new_float(a.as_float() + b.as_float()))
} else if a.is_int() && b.is_float() {
Ok(NanValue::new_float(
a.as_int(&self.arena) as f64 + b.as_float(),
))
} else if a.is_float() && b.is_int() {
Ok(NanValue::new_float(
a.as_float() + b.as_int(&self.arena) as f64,
))
} else if a.is_string() && b.is_string() {
let s = format!(
"{}{}",
self.arena.get_string_value(a),
self.arena.get_string_value(b)
);
Ok(NanValue::new_string_value(&s, &mut self.arena))
} else {
Err(VmError::type_err(format!(
"cannot add {} and {}",
self.value_type_name(a),
self.value_type_name(b)
)))
}
}
pub(super) fn arith_sub(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, VmError> {
if a.is_int() && b.is_int() {
Ok(NanValue::new_int(
a.as_int(&self.arena) - b.as_int(&self.arena),
&mut self.arena,
))
} else if a.is_float() && b.is_float() {
Ok(NanValue::new_float(a.as_float() - b.as_float()))
} else if a.is_int() && b.is_float() {
Ok(NanValue::new_float(
a.as_int(&self.arena) as f64 - b.as_float(),
))
} else if a.is_float() && b.is_int() {
Ok(NanValue::new_float(
a.as_float() - b.as_int(&self.arena) as f64,
))
} else {
Err(VmError::type_err(format!(
"cannot subtract {} and {}",
self.value_type_name(a),
self.value_type_name(b)
)))
}
}
pub(super) fn arith_mul(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, VmError> {
if a.is_int() && b.is_int() {
Ok(NanValue::new_int(
a.as_int(&self.arena) * b.as_int(&self.arena),
&mut self.arena,
))
} else if a.is_float() && b.is_float() {
Ok(NanValue::new_float(a.as_float() * b.as_float()))
} else if a.is_int() && b.is_float() {
Ok(NanValue::new_float(
a.as_int(&self.arena) as f64 * b.as_float(),
))
} else if a.is_float() && b.is_int() {
Ok(NanValue::new_float(
a.as_float() * b.as_int(&self.arena) as f64,
))
} else {
Err(VmError::type_err(format!(
"cannot multiply {} and {}",
self.value_type_name(a),
self.value_type_name(b)
)))
}
}
pub(super) fn arith_div(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, VmError> {
if a.is_int() && b.is_int() {
let bv = b.as_int(&self.arena);
if bv == 0 {
return Err(VmError::runtime("division by zero"));
}
Ok(NanValue::new_int(
a.as_int(&self.arena) / bv,
&mut self.arena,
))
} else if a.is_float() && b.is_float() {
Ok(NanValue::new_float(a.as_float() / b.as_float()))
} else if a.is_int() && b.is_float() {
Ok(NanValue::new_float(
a.as_int(&self.arena) as f64 / b.as_float(),
))
} else if a.is_float() && b.is_int() {
Ok(NanValue::new_float(
a.as_float() / b.as_int(&self.arena) as f64,
))
} else {
Err(VmError::type_err(format!(
"cannot divide {} and {}",
self.value_type_name(a),
self.value_type_name(b)
)))
}
}
pub(super) fn arith_mod(&mut self, a: NanValue, b: NanValue) -> Result<NanValue, VmError> {
if a.is_int() && b.is_int() {
let bv = b.as_int(&self.arena);
if bv == 0 {
return Err(VmError::runtime("modulo by zero"));
}
Ok(NanValue::new_int(
a.as_int(&self.arena) % bv,
&mut self.arena,
))
} else {
Err(VmError::type_err(format!(
"cannot modulo {} and {}",
self.value_type_name(a),
self.value_type_name(b)
)))
}
}
pub(super) fn compare_lt(&self, a: NanValue, b: NanValue) -> Result<bool, VmError> {
if a.is_int() && b.is_int() {
Ok(a.as_int(&self.arena) < b.as_int(&self.arena))
} else if a.is_float() && b.is_float() {
Ok(a.as_float() < b.as_float())
} else if a.is_string() && b.is_string() {
Ok(self.arena.get_string_value(a) < self.arena.get_string_value(b))
} else if a.is_int() && b.is_float() {
Ok((a.as_int(&self.arena) as f64) < b.as_float())
} else if a.is_float() && b.is_int() {
Ok(a.as_float() < (b.as_int(&self.arena) as f64))
} else {
Err(VmError::type_err(format!(
"cannot compare {} and {}",
self.value_type_name(a),
self.value_type_name(b)
)))
}
}
}