phoenix_lang/
value.rs

1use std::collections::HashMap;
2use std::hash::{Hash, Hasher};
3
4use crate::chunk::ModuleChunk;
5use serde::{Deserialize, Serialize};
6
7use crate::native::native_functions::NativeFn;
8use crate::native::native_methods::NativeMethod;
9use crate::vm::{VMState, VM};
10
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12pub enum Value {
13    Float(f32),
14    Long(i64),
15    Bool(bool),
16    #[default]
17    Nil,
18    // todo: fix this, cause this type is only used for initialisation of strings
19    // todo: create static strings, for example for prints, so we can allocate them once and have multiple immutable references to them
20    PhoenixString(String),
21    // Index of the function in the functions Vec in VM // Fixme: Is this even reachable? Can this be completely removed and the parameter put in OpClosure?
22    PhoenixFunction(usize),
23    #[serde(skip)]
24    NativeFunction(Option<usize>, NativeFn),
25    #[serde(skip)]
26    NativeMethod(Option<usize>, NativeMethod),
27    PhoenixClass(usize),
28    // similar to class, but for modules
29    PhoenixModule(usize),
30    // for strings, instances and lists
31    PhoenixPointer(usize),
32    PhoenixBoundMethod(ObjBoundMethod),
33}
34
35// todo fix this (dont hash the pointer, but the value)
36impl Hash for Value {
37    fn hash<H: Hasher>(&self, state: &mut H) {
38        match self {
39            Value::Float(x) => x.to_bits().hash(state),
40            Value::Long(x) => x.hash(state),
41            Value::Bool(x) => x.hash(state),
42            Value::Nil => 0.hash(state),
43            Value::PhoenixString(x) => x.hash(state),
44            Value::PhoenixFunction(x) => x.hash(state),
45            Value::NativeFunction(x, _) => x.hash(state),
46            Value::NativeMethod(x, _) => x.hash(state),
47            Value::PhoenixClass(x) => x.hash(state),
48            Value::PhoenixModule(x) => x.hash(state),
49            Value::PhoenixPointer(x) => x.hash(state),
50            Value::PhoenixBoundMethod(x) => x.hash(state),
51        }
52    }
53}
54
55impl Eq for Value {}
56
57impl PartialEq for Value {
58    fn eq(&self, other: &Self) -> bool {
59        values_equal((self, other))
60    }
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, Default)]
64pub struct ValueArray {
65    pub values: Vec<Value>,
66}
67
68impl ValueArray {
69    pub fn new() -> ValueArray {
70        ValueArray { values: Vec::new() }
71    }
72
73    pub fn write(&mut self, value: Value) {
74        self.values.push(value);
75    }
76
77    pub fn free(&mut self) {
78        self.values.clear();
79    }
80}
81
82impl Value {
83    /// Used for print statements, use {:?} debug formatting for trace and stack examining
84    pub fn to_string(&self, vm: &VM, state: &VMState, modules: &Vec<ModuleChunk>) -> String {
85        match self {
86            Value::Float(x) => format!("{}", x),
87            Value::Long(x) => format!("{}", x),
88            Value::Bool(x) => format!("{}", x),
89            Value::PhoenixString(x) => x.to_string(),
90            Value::Nil => String::from("nil"),
91            Value::PhoenixFunction(x) => format!(
92                "<fn {}>",
93                &modules[state.current_frame.module]
94                    .functions
95                    .get(*x)
96                    .unwrap()
97                    .name
98                    .as_ref()
99                    .unwrap()
100            ),
101            Value::NativeFunction(_x, _) => "<native_fn>".to_string(),
102            Value::NativeMethod(_x, _) => "<native_method>".to_string(),
103            Value::PhoenixClass(class) => format!("<class {}>", class),
104            Value::PhoenixPointer(pointer) => {
105                // hacky way to check if this is a list or string
106                if let HeapObjVal::PhoenixList(_) = &state.deref(*pointer).obj {
107                    state.deref(*pointer).to_string(vm, state, modules)
108                } else if let HeapObjVal::PhoenixString(_) = &state.deref(*pointer).obj {
109                    state.deref(*pointer).to_string(vm, state, modules)
110                } else if let HeapObjVal::PhoenixHashMap(_) = &state.deref(*pointer).obj {
111                    state.deref(*pointer).to_string(vm, state, modules)
112                } else {
113                    format!(
114                        "<pointer {}> to {}",
115                        pointer,
116                        state.deref(*pointer).to_string(vm, state, modules)
117                    )
118                }
119            } // Suggestion: Don't reveal to the user the internals?
120            Value::PhoenixBoundMethod(method) => format!(
121                "<method {} from {}",
122                &modules[state.current_frame.module]
123                    .functions
124                    .get(method.method)
125                    .unwrap()
126                    .name
127                    .as_ref()
128                    .unwrap(),
129                state.deref(method.pointer).to_string(vm, state, modules)
130            ),
131            Value::PhoenixModule(module) => format!("<module {}>", module),
132        }
133    }
134
135    pub fn as_float(&self) -> Option<f32> {
136        if let Value::Float(val) = self {
137            Some(*val)
138        } else if let Value::Long(val) = self {
139            Some(*val as f32)
140        } else {
141            None
142        }
143    }
144
145    pub fn as_long(&self) -> Option<i64> {
146        if let Value::Long(val) = self {
147            Some(*val)
148        } else if let Value::Float(val) = self {
149            Some(*val as i64)
150        } else {
151            None
152        }
153    }
154
155    pub fn as_bool(&self) -> Option<bool> {
156        match self {
157            Value::Bool(val) => Some(*val),
158            Value::Long(val) => Some(*val != 0),
159            Value::Float(val) => Some(*val != 0.0),
160            _ => None,
161        }
162    }
163
164    pub fn as_string<'a>(&'a self, state: &'a VMState) -> Option<&String> {
165        if let Value::PhoenixPointer(x) = self {
166            Some(&state.deref_string(*x).value)
167        } else {
168            None
169        }
170    }
171
172    pub const fn get_type(&self) -> &str {
173        match self {
174            Value::Float(_) => "float",
175            Value::Long(_) => "long",
176            Value::Bool(_) => "bool",
177            Value::Nil => "nil",
178            Value::PhoenixFunction(_) => "function",
179            Value::PhoenixClass(_) => "class",
180            Value::NativeFunction(_, _) => "native_function",
181            Value::PhoenixPointer(_) => "pointer",
182            Value::PhoenixBoundMethod(_) => "bound_method",
183            _ => "unknown (Please report this bug)",
184        }
185    }
186
187    /// Hard cast to a ObjPointer. Panics if this value is not a PhoenixPointer
188    pub fn as_pointer(&self) -> usize {
189        if let Value::PhoenixPointer(ptr) = self {
190            *ptr
191        } else {
192            panic!(
193                "VM panic! Failed to cast value to a pointer. Found {:?} instead",
194                self
195            )
196        }
197    }
198
199    /// Convert arg to a float and do some error checks (without casting), never panics, unlike as_float()
200    pub fn to_float(&self) -> Result<f32, String> {
201        match self.as_float() {
202            Some(num) => {
203                if num.is_nan() {
204                    return Err("Invalid argument: expected number, got NaN".to_string());
205                }
206                Ok(num)
207            }
208            None => Err(format!(
209                "Invalid argument: expected number: instead got {}",
210                self.get_type()
211            )),
212        }
213    }
214
215    /// Convert arg to a long and do some error checks (without casting), never panics, unlike as_long()
216    pub fn to_long(&self) -> Result<i64, String> {
217        match self.as_long() {
218            Some(num) => Ok(num),
219            None => Err(format!(
220                "Invalid argument: expected number: instead got {}",
221                self.get_type()
222            )),
223        }
224    }
225
226    /// Convert arg to a bool and do some error checks (without casting), never panics, unlike as_bool()
227    pub fn to_bool(&self) -> Result<bool, String> {
228        match self.as_bool() {
229            Some(val) => Ok(val),
230            None => Err(format!(
231                "Invalid argument: expected boolean: instead got {}",
232                self.get_type()
233            )),
234        }
235    }
236
237    /// Convert arg to a list and some error checks (without casting), never panics, unlike as_list()
238    pub fn to_list(&self, state: &VMState) -> Result<Vec<Value>, String> {
239        if let Value::PhoenixPointer(p) = self {
240            if let HeapObjVal::PhoenixList(_) = &state.deref(*p).obj {
241                return Ok(state.deref(*p).obj.as_list().values.clone());
242            }
243        }
244        Err(format!(
245            "Invalid argument: expected list: instead got {}",
246            self.get_type()
247        ))
248    }
249
250    /// Convert self to a class and some error checks (without casting), never panics, unlike as_class()
251    pub fn to_class<'a>(&self, state: &'a VMState) -> Result<&'a ObjInstance, String> {
252        if let Value::PhoenixPointer(p) = self {
253            if let HeapObjVal::PhoenixInstance(_) = &state.deref(*p).obj {
254                return Ok(state.deref(*p).obj.as_instance());
255            }
256        }
257        Err(format!(
258            "Invalid argument: expected class: instead got {}",
259            self.get_type()
260        ))
261    }
262}
263
264pub fn is_falsey(val: &Value) -> bool {
265    matches!(val, Value::Bool(false) | Value::Nil)
266}
267
268pub fn values_equal(t: (&Value, &Value)) -> bool {
269    match t {
270        (Value::Float(x), Value::Float(y)) => x == y,
271        (Value::Long(x), Value::Long(y)) => x == y,
272        (Value::Bool(x), Value::Bool(y)) => x == y,
273        (Value::Nil, Value::Nil) => true,
274        (Value::PhoenixString(x), Value::PhoenixString(y)) => x.eq(y),
275        (Value::PhoenixPointer(x), Value::PhoenixPointer(y)) => x == y,
276        (Value::PhoenixClass(x), Value::PhoenixClass(y)) => x == y,
277        (Value::PhoenixFunction(x), Value::PhoenixFunction(y)) => x == y,
278        (Value::NativeFunction(x, x2), Value::NativeFunction(y, y2)) => x == y && x2 == y2,
279        (Value::PhoenixBoundMethod(x), Value::PhoenixBoundMethod(y)) => x == y,
280        _ => false,
281    }
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Ord, PartialOrd, Eq, Hash)]
285pub struct ObjBoundMethod {
286    pub method: usize,
287    // Index into the functions vec for which function to call
288    pub pointer: usize, // Pointer to the PhoenixInstance that this method is bound to
289}
290
291// End of stack/implicit copy objects
292
293// Heap Objects
294
295#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
296pub enum HeapObjType {
297    HeapPlaceholder,
298    PhoenixInstance,
299    PhoenixClosure,
300    PhoenixList,
301    PhoenixString,
302    PhoenixHashMap,
303}
304
305#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
306pub struct HeapObj {
307    pub obj: HeapObjVal,
308    pub obj_type: HeapObjType,
309    pub is_marked: bool,
310}
311
312impl HeapObj {
313    fn to_string(&self, vm: &VM, state: &VMState, modules: &Vec<ModuleChunk>) -> String {
314        self.obj.to_string(vm, state, modules)
315    }
316
317    pub fn new_instance(val: ObjInstance) -> HeapObj {
318        HeapObj {
319            obj: HeapObjVal::PhoenixInstance(val),
320            obj_type: HeapObjType::PhoenixInstance,
321            is_marked: false,
322        }
323    }
324
325    pub fn new_closure(val: ObjClosure) -> HeapObj {
326        HeapObj {
327            obj: HeapObjVal::PhoenixClosure(val),
328            obj_type: HeapObjType::PhoenixClosure,
329            is_marked: false,
330        }
331    }
332
333    pub fn new_placeholder() -> HeapObj {
334        HeapObj {
335            obj: HeapObjVal::HeapPlaceholder,
336            obj_type: HeapObjType::HeapPlaceholder,
337            is_marked: false,
338        }
339    }
340
341    pub fn new_list(val: ObjList) -> HeapObj {
342        HeapObj {
343            obj: HeapObjVal::PhoenixList(val),
344            obj_type: HeapObjType::PhoenixList,
345            is_marked: false,
346        }
347    }
348
349    pub fn new_string(val: ObjString) -> HeapObj {
350        HeapObj {
351            obj: HeapObjVal::PhoenixString(val),
352            obj_type: HeapObjType::PhoenixString,
353            is_marked: false,
354        }
355    }
356
357    pub fn new_hashmap(map: ObjHashMap) -> HeapObj {
358        HeapObj {
359            obj: HeapObjVal::PhoenixHashMap(map),
360            obj_type: HeapObjType::PhoenixHashMap,
361            is_marked: false,
362        }
363    }
364}
365
366// I swear i really tried to not have this be duplicate with HeapObjType, but couldn't figure out a way to do it
367#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
368pub enum HeapObjVal {
369    HeapPlaceholder,
370    PhoenixInstance(ObjInstance),
371    PhoenixClosure(ObjClosure),
372    PhoenixString(ObjString),
373    PhoenixList(ObjList),
374    PhoenixHashMap(ObjHashMap),
375}
376
377impl HeapObjVal {
378    fn to_string(&self, vm: &VM, state: &VMState, modules: &Vec<ModuleChunk>) -> String {
379        match self {
380            HeapObjVal::PhoenixClosure(closure) => format!(
381                "<fn {} | {:?}>",
382                &modules[state.current_frame.module]
383                    .functions
384                    .get(closure.function)
385                    .unwrap()
386                    .name
387                    .as_ref()
388                    .unwrap(),
389                closure
390            ),
391            HeapObjVal::PhoenixInstance(instance) => format!(
392                "<instance {}>",
393                &modules[state.current_frame.module]
394                    .classes
395                    .get(instance.class)
396                    .unwrap()
397                    .name
398            ),
399            HeapObjVal::PhoenixList(list) => format!(
400                "[{}]",
401                list.values
402                    .iter()
403                    .map(|x| x.to_string(vm, state, modules))
404                    .collect::<Vec<String>>()
405                    .join(", ")
406            ),
407            HeapObjVal::HeapPlaceholder => {
408                panic!("VM panic! How did a placeholder value get here?")
409            }
410            HeapObjVal::PhoenixString(string) => string.value.clone(),
411            HeapObjVal::PhoenixHashMap(map) => format!(
412                "{{{}}}",
413                map.map
414                    .iter()
415                    .map(|(k, v)| format!(
416                        "{}: {}",
417                        k.to_string(vm, state, modules),
418                        v.to_string(vm, state, modules)
419                    ))
420                    .collect::<Vec<String>>()
421                    .join(", ")
422            ),
423        }
424    }
425
426    pub fn as_closure(&self) -> &ObjClosure {
427        if let HeapObjVal::PhoenixClosure(closure) = self {
428            closure
429        } else {
430            panic!("VM panic!")
431        }
432    }
433
434    pub fn as_closure_mut(&mut self) -> &mut ObjClosure {
435        if let HeapObjVal::PhoenixClosure(closure) = self {
436            closure
437        } else {
438            panic!("VM panic!")
439        }
440    }
441
442    pub fn as_instance(&self) -> &ObjInstance {
443        if let HeapObjVal::PhoenixInstance(instance) = self {
444            instance
445        } else {
446            panic!("VM panic!")
447        }
448    }
449
450    pub fn as_instance_mut(&mut self) -> &mut ObjInstance {
451        if let HeapObjVal::PhoenixInstance(instance) = self {
452            instance
453        } else {
454            panic!("VM panic!")
455        }
456    }
457
458    pub fn as_list(&self) -> &ObjList {
459        if let HeapObjVal::PhoenixList(list) = self {
460            list
461        } else {
462            panic!("VM panic!")
463        }
464    }
465
466    pub fn as_list_mut(&mut self) -> &mut ObjList {
467        if let HeapObjVal::PhoenixList(list) = self {
468            list
469        } else {
470            panic!("VM panic!")
471        }
472    }
473
474    pub fn as_string(&self) -> &ObjString {
475        if let HeapObjVal::PhoenixString(string) = self {
476            string
477        } else {
478            panic!("VM panic!")
479        }
480    }
481
482    pub fn as_string_mut(&mut self) -> &mut ObjString {
483        if let HeapObjVal::PhoenixString(string) = self {
484            string
485        } else {
486            panic!("VM panic!")
487        }
488    }
489
490    pub fn as_hashmap(&self) -> &ObjHashMap {
491        if let HeapObjVal::PhoenixHashMap(map) = self {
492            map
493        } else {
494            panic!("VM panic!")
495        }
496    }
497}
498
499
500// Convert Value to Primitives: f32, f64, i32, i64, bool, String
501impl TryFrom<Value> for f32 {
502    type Error = String;
503
504    fn try_from(value: Value) -> Result<Self, Self::Error> {
505        match value {
506            Value::Float(f) => Ok(f),
507            Value::Long(l) => Ok(l as f32),
508            _ => Err(format!("Could not convert {} to float!", value.get_type())),
509        }
510    }
511}
512
513impl TryFrom<Value> for i64 {
514    type Error = String;
515
516    fn try_from(value: Value) -> Result<Self, Self::Error> {
517        match value {
518            Value::Long(l) => Ok(l),
519            Value::Float(f) => Ok(f as i64),
520            _ => Err(format!("Could not convert {} to int!", value.get_type())),
521        }
522    }
523}
524
525impl TryFrom<Value> for i32 {
526    type Error = String;
527
528    fn try_from(value: Value) -> Result<Self, Self::Error> {
529        match value {
530            Value::Long(l) => Ok(l as i32),
531            Value::Float(f) => Ok(f as i32),
532            _ => Err(format!("Could not convert {} to int!", value.get_type())),
533        }
534    }
535}
536
537impl TryFrom<Value> for bool {
538    type Error = String;
539
540    fn try_from(value: Value) -> Result<Self, Self::Error> {
541        match value {
542            Value::Bool(b) => Ok(b),
543            _ => Err(format!("Could not convert {} to bool!", value.get_type())),
544        }
545    }
546}
547
548impl TryFrom<Value> for String {
549    type Error = String;
550
551    fn try_from(value: Value) -> Result<Self, Self::Error> {
552        match value {
553            Value::PhoenixString(s) => Ok(s),
554            _ => Err(format!("Could not convert {} to string!", value.get_type())),
555        }
556    }
557}
558
559
560
561/// Runtime instantiation of class definitions
562#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
563pub struct ObjInstance {
564    pub class: usize,
565    // Which class was this instance made from?
566    pub fields: HashMap<usize, Value>, // Stores the field values. FunctionChunks are stored in the ClassChunk, which is not ideal since it adds an extra vec lookup before getting to the function
567}
568
569impl ObjInstance {
570    pub fn new(class: usize) -> ObjInstance {
571        ObjInstance {
572            class,
573            fields: HashMap::new(),
574        }
575    }
576}
577
578/// Runtime representation of the closure, ie what variables are in scope
579#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
580pub struct ObjClosure {
581    pub function: usize,
582    pub values: Vec<Value>, // Will be filled at runtime
583}
584
585impl ObjClosure {
586    pub fn new(function: usize) -> ObjClosure {
587        ObjClosure {
588            function,
589            values: Vec::new(),
590        }
591    }
592}
593
594/// Runtime representation of a list
595#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
596pub struct ObjList {
597    pub values: Vec<Value>,
598}
599
600impl ObjList {
601    pub fn new(v: Vec<Value>) -> ObjList {
602        ObjList { values: v }
603    }
604}
605
606/// Runtime representation of a string
607#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
608pub struct ObjString {
609    pub value: String,
610}
611
612impl ObjString {
613    pub fn new(value: String) -> ObjString {
614        ObjString { value }
615    }
616}
617
618/// Runtime representation of a HashMap
619#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, Default)]
620pub struct ObjHashMap {
621    pub map: HashMap<Value, Value>,
622}
623
624impl ObjHashMap {
625    pub fn new(map: HashMap<Value, Value>) -> ObjHashMap {
626        ObjHashMap { map }
627    }
628}