use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use crate::chunk::ModuleChunk;
use serde::{Deserialize, Serialize};
use crate::native::native_functions::NativeFn;
use crate::native::native_methods::NativeMethod;
use crate::vm::{VMState, VM};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub enum Value {
Float(f32),
Long(i64),
Bool(bool),
#[default]
Nil,
PhoenixString(String),
PhoenixFunction(usize),
#[serde(skip)]
NativeFunction(Option<usize>, NativeFn),
#[serde(skip)]
NativeMethod(Option<usize>, NativeMethod),
PhoenixClass(usize),
PhoenixModule(usize),
PhoenixPointer(usize),
PhoenixBoundMethod(ObjBoundMethod),
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Value::Float(x) => x.to_bits().hash(state),
Value::Long(x) => x.hash(state),
Value::Bool(x) => x.hash(state),
Value::Nil => 0.hash(state),
Value::PhoenixString(x) => x.hash(state),
Value::PhoenixFunction(x) => x.hash(state),
Value::NativeFunction(x, _) => x.hash(state),
Value::NativeMethod(x, _) => x.hash(state),
Value::PhoenixClass(x) => x.hash(state),
Value::PhoenixModule(x) => x.hash(state),
Value::PhoenixPointer(x) => x.hash(state),
Value::PhoenixBoundMethod(x) => x.hash(state),
}
}
}
impl Eq for Value {}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
values_equal((self, other))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ValueArray {
pub values: Vec<Value>,
}
impl ValueArray {
pub fn new() -> ValueArray {
ValueArray { values: Vec::new() }
}
pub fn write(&mut self, value: Value) {
self.values.push(value);
}
pub fn free(&mut self) {
self.values.clear();
}
}
impl Value {
pub fn to_string(&self, vm: &VM, state: &VMState, modules: &Vec<ModuleChunk>) -> String {
match self {
Value::Float(x) => format!("{}", x),
Value::Long(x) => format!("{}", x),
Value::Bool(x) => format!("{}", x),
Value::PhoenixString(x) => x.to_string(),
Value::Nil => String::from("nil"),
Value::PhoenixFunction(x) => format!(
"<fn {}>",
&modules[state.current_frame.module]
.functions
.get(*x)
.unwrap()
.name
.as_ref()
.unwrap()
),
Value::NativeFunction(_x, _) => "<native_fn>".to_string(),
Value::NativeMethod(_x, _) => "<native_method>".to_string(),
Value::PhoenixClass(class) => format!("<class {}>", class),
Value::PhoenixPointer(pointer) => {
if let HeapObjVal::PhoenixList(_) = &state.deref(*pointer).obj {
state.deref(*pointer).to_string(vm, state, modules)
} else if let HeapObjVal::PhoenixString(_) = &state.deref(*pointer).obj {
state.deref(*pointer).to_string(vm, state, modules)
} else if let HeapObjVal::PhoenixHashMap(_) = &state.deref(*pointer).obj {
state.deref(*pointer).to_string(vm, state, modules)
} else {
format!(
"<pointer {}> to {}",
pointer,
state.deref(*pointer).to_string(vm, state, modules)
)
}
} Value::PhoenixBoundMethod(method) => format!(
"<method {} from {}",
&modules[state.current_frame.module]
.functions
.get(method.method)
.unwrap()
.name
.as_ref()
.unwrap(),
state.deref(method.pointer).to_string(vm, state, modules)
),
Value::PhoenixModule(module) => format!("<module {}>", module),
}
}
pub fn as_float(&self) -> Option<f32> {
if let Value::Float(val) = self {
Some(*val)
} else if let Value::Long(val) = self {
Some(*val as f32)
} else {
None
}
}
pub fn as_long(&self) -> Option<i64> {
if let Value::Long(val) = self {
Some(*val)
} else if let Value::Float(val) = self {
Some(*val as i64)
} else {
None
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(val) => Some(*val),
Value::Long(val) => Some(*val != 0),
Value::Float(val) => Some(*val != 0.0),
_ => None,
}
}
pub fn as_string<'a>(&'a self, state: &'a VMState) -> Option<&String> {
if let Value::PhoenixPointer(x) = self {
Some(&state.deref_string(*x).value)
} else {
None
}
}
pub const fn get_type(&self) -> &str {
match self {
Value::Float(_) => "float",
Value::Long(_) => "long",
Value::Bool(_) => "bool",
Value::Nil => "nil",
Value::PhoenixFunction(_) => "function",
Value::PhoenixClass(_) => "class",
Value::NativeFunction(_, _) => "native_function",
Value::PhoenixPointer(_) => "pointer",
Value::PhoenixBoundMethod(_) => "bound_method",
_ => "unknown (Please report this bug)",
}
}
pub fn as_pointer(&self) -> usize {
if let Value::PhoenixPointer(ptr) = self {
*ptr
} else {
panic!(
"VM panic! Failed to cast value to a pointer. Found {:?} instead",
self
)
}
}
pub fn to_float(&self) -> Result<f32, String> {
match self.as_float() {
Some(num) => {
if num.is_nan() {
return Err("Invalid argument: expected number, got NaN".to_string());
}
Ok(num)
}
None => Err(format!(
"Invalid argument: expected number: instead got {}",
self.get_type()
)),
}
}
pub fn to_long(&self) -> Result<i64, String> {
match self.as_long() {
Some(num) => Ok(num),
None => Err(format!(
"Invalid argument: expected number: instead got {}",
self.get_type()
)),
}
}
pub fn to_bool(&self) -> Result<bool, String> {
match self.as_bool() {
Some(val) => Ok(val),
None => Err(format!(
"Invalid argument: expected boolean: instead got {}",
self.get_type()
)),
}
}
pub fn to_list(&self, state: &VMState) -> Result<Vec<Value>, String> {
if let Value::PhoenixPointer(p) = self {
if let HeapObjVal::PhoenixList(_) = &state.deref(*p).obj {
return Ok(state.deref(*p).obj.as_list().values.clone());
}
}
Err(format!(
"Invalid argument: expected list: instead got {}",
self.get_type()
))
}
pub fn to_class<'a>(&self, state: &'a VMState) -> Result<&'a ObjInstance, String> {
if let Value::PhoenixPointer(p) = self {
if let HeapObjVal::PhoenixInstance(_) = &state.deref(*p).obj {
return Ok(state.deref(*p).obj.as_instance());
}
}
Err(format!(
"Invalid argument: expected class: instead got {}",
self.get_type()
))
}
}
pub fn is_falsey(val: &Value) -> bool {
matches!(val, Value::Bool(false) | Value::Nil)
}
pub fn values_equal(t: (&Value, &Value)) -> bool {
match t {
(Value::Float(x), Value::Float(y)) => x == y,
(Value::Long(x), Value::Long(y)) => x == y,
(Value::Bool(x), Value::Bool(y)) => x == y,
(Value::Nil, Value::Nil) => true,
(Value::PhoenixString(x), Value::PhoenixString(y)) => x.eq(y),
(Value::PhoenixPointer(x), Value::PhoenixPointer(y)) => x == y,
(Value::PhoenixClass(x), Value::PhoenixClass(y)) => x == y,
(Value::PhoenixFunction(x), Value::PhoenixFunction(y)) => x == y,
(Value::NativeFunction(x, x2), Value::NativeFunction(y, y2)) => x == y && x2 == y2,
(Value::PhoenixBoundMethod(x), Value::PhoenixBoundMethod(y)) => x == y,
_ => false,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Ord, PartialOrd, Eq, Hash)]
pub struct ObjBoundMethod {
pub method: usize,
pub pointer: usize, }
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum HeapObjType {
HeapPlaceholder,
PhoenixInstance,
PhoenixClosure,
PhoenixList,
PhoenixString,
PhoenixHashMap,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct HeapObj {
pub obj: HeapObjVal,
pub obj_type: HeapObjType,
pub is_marked: bool,
}
impl HeapObj {
fn to_string(&self, vm: &VM, state: &VMState, modules: &Vec<ModuleChunk>) -> String {
self.obj.to_string(vm, state, modules)
}
pub fn new_instance(val: ObjInstance) -> HeapObj {
HeapObj {
obj: HeapObjVal::PhoenixInstance(val),
obj_type: HeapObjType::PhoenixInstance,
is_marked: false,
}
}
pub fn new_closure(val: ObjClosure) -> HeapObj {
HeapObj {
obj: HeapObjVal::PhoenixClosure(val),
obj_type: HeapObjType::PhoenixClosure,
is_marked: false,
}
}
pub fn new_placeholder() -> HeapObj {
HeapObj {
obj: HeapObjVal::HeapPlaceholder,
obj_type: HeapObjType::HeapPlaceholder,
is_marked: false,
}
}
pub fn new_list(val: ObjList) -> HeapObj {
HeapObj {
obj: HeapObjVal::PhoenixList(val),
obj_type: HeapObjType::PhoenixList,
is_marked: false,
}
}
pub fn new_string(val: ObjString) -> HeapObj {
HeapObj {
obj: HeapObjVal::PhoenixString(val),
obj_type: HeapObjType::PhoenixString,
is_marked: false,
}
}
pub fn new_hashmap(map: ObjHashMap) -> HeapObj {
HeapObj {
obj: HeapObjVal::PhoenixHashMap(map),
obj_type: HeapObjType::PhoenixHashMap,
is_marked: false,
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub enum HeapObjVal {
HeapPlaceholder,
PhoenixInstance(ObjInstance),
PhoenixClosure(ObjClosure),
PhoenixString(ObjString),
PhoenixList(ObjList),
PhoenixHashMap(ObjHashMap),
}
impl HeapObjVal {
fn to_string(&self, vm: &VM, state: &VMState, modules: &Vec<ModuleChunk>) -> String {
match self {
HeapObjVal::PhoenixClosure(closure) => format!(
"<fn {} | {:?}>",
&modules[state.current_frame.module]
.functions
.get(closure.function)
.unwrap()
.name
.as_ref()
.unwrap(),
closure
),
HeapObjVal::PhoenixInstance(instance) => format!(
"<instance {}>",
&modules[state.current_frame.module]
.classes
.get(instance.class)
.unwrap()
.name
),
HeapObjVal::PhoenixList(list) => format!(
"[{}]",
list.values
.iter()
.map(|x| x.to_string(vm, state, modules))
.collect::<Vec<String>>()
.join(", ")
),
HeapObjVal::HeapPlaceholder => {
panic!("VM panic! How did a placeholder value get here?")
}
HeapObjVal::PhoenixString(string) => string.value.clone(),
HeapObjVal::PhoenixHashMap(map) => format!(
"{{{}}}",
map.map
.iter()
.map(|(k, v)| format!(
"{}: {}",
k.to_string(vm, state, modules),
v.to_string(vm, state, modules)
))
.collect::<Vec<String>>()
.join(", ")
),
}
}
pub fn as_closure(&self) -> &ObjClosure {
if let HeapObjVal::PhoenixClosure(closure) = self {
closure
} else {
panic!("VM panic!")
}
}
pub fn as_closure_mut(&mut self) -> &mut ObjClosure {
if let HeapObjVal::PhoenixClosure(closure) = self {
closure
} else {
panic!("VM panic!")
}
}
pub fn as_instance(&self) -> &ObjInstance {
if let HeapObjVal::PhoenixInstance(instance) = self {
instance
} else {
panic!("VM panic!")
}
}
pub fn as_instance_mut(&mut self) -> &mut ObjInstance {
if let HeapObjVal::PhoenixInstance(instance) = self {
instance
} else {
panic!("VM panic!")
}
}
pub fn as_list(&self) -> &ObjList {
if let HeapObjVal::PhoenixList(list) = self {
list
} else {
panic!("VM panic!")
}
}
pub fn as_list_mut(&mut self) -> &mut ObjList {
if let HeapObjVal::PhoenixList(list) = self {
list
} else {
panic!("VM panic!")
}
}
pub fn as_string(&self) -> &ObjString {
if let HeapObjVal::PhoenixString(string) = self {
string
} else {
panic!("VM panic!")
}
}
pub fn as_string_mut(&mut self) -> &mut ObjString {
if let HeapObjVal::PhoenixString(string) = self {
string
} else {
panic!("VM panic!")
}
}
pub fn as_hashmap(&self) -> &ObjHashMap {
if let HeapObjVal::PhoenixHashMap(map) = self {
map
} else {
panic!("VM panic!")
}
}
}
impl TryFrom<Value> for f32 {
type Error = String;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Float(f) => Ok(f),
Value::Long(l) => Ok(l as f32),
_ => Err("Invalid type".to_string()),
}
}
}
impl TryFrom<Value> for i64 {
type Error = String;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Long(l) => Ok(l),
Value::Float(f) => Ok(f as i64),
_ => Err("Invalid type".to_string()),
}
}
}
impl TryFrom<Value> for i32 {
type Error = String;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Long(l) => Ok(l as i32),
Value::Float(f) => Ok(f as i32),
_ => Err("Invalid type".to_string()),
}
}
}
impl TryFrom<Value> for bool {
type Error = String;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Bool(b) => Ok(b),
_ => Err("Invalid type".to_string()),
}
}
}
impl TryFrom<Value> for String {
type Error = String;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::PhoenixString(s) => Ok(s),
_ => Err("Invalid type".to_string()),
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct ObjInstance {
pub class: usize,
pub fields: HashMap<usize, Value>, }
impl ObjInstance {
pub fn new(class: usize) -> ObjInstance {
ObjInstance {
class,
fields: HashMap::new(),
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct ObjClosure {
pub function: usize,
pub values: Vec<Value>, }
impl ObjClosure {
pub fn new(function: usize) -> ObjClosure {
ObjClosure {
function,
values: Vec::new(),
}
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct ObjList {
pub values: Vec<Value>,
}
impl ObjList {
pub fn new(v: Vec<Value>) -> ObjList {
ObjList { values: v }
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct ObjString {
pub value: String,
}
impl ObjString {
pub fn new(value: String) -> ObjString {
ObjString { value }
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
pub struct ObjHashMap {
pub map: HashMap<Value, Value>,
}
impl ObjHashMap {
pub fn new(map: HashMap<Value, Value>) -> ObjHashMap {
ObjHashMap { map }
}
}