funcscript/
obj.rs

1//! Heap-allocated object types used by the FuncScript VM.
2
3use crate::value::Value;
4use std::collections::HashMap;
5use std::cell::RefCell;
6use std::collections::HashSet;
7use std::rc::Rc;
8use uuid::Uuid;
9
10use crate::chunk::Chunk;
11
12#[derive(Debug, Clone)]
13pub struct FsFunction {
14    pub arity: usize,
15    pub chunk: Chunk,
16    pub name: String,
17    // Names for stack slots (locals + parameters). Slot 0 is reserved and typically empty.
18    // Used to allow lazy KVC key thunks to still resolve lexical variables via provider lookup.
19    pub slot_names: Vec<String>,
20}
21
22impl PartialEq for FsFunction {
23    fn eq(&self, _other: &Self) -> bool {
24        false 
25    }
26}
27
28#[derive(Debug, Clone)]
29pub enum Obj {
30    String(String),
31    List(Vec<Value>),
32    Range(RangeObject),
33    Bytes(Vec<u8>),
34    Guid(Uuid),
35    DateTimeTicks(i64),
36
37    Kvc(Rc<RefCell<KvcObject>>),
38   
39    Provider(Rc<ProviderObject>),
40    Function(std::rc::Rc<FsFunction>),
41
42    NativeFn(fn(&[Value]) -> Value),
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub struct RangeObject {
47    pub start: i64,
48    pub count: usize,
49}
50
51#[derive(Debug)]
52pub struct ProviderObject {
53    pub current: Value,
54    pub parent: Option<Value>,
55}
56
57#[derive(Debug)]
58pub struct KvcObject {
59    pub entries: HashMap<String, Rc<FsFunction>>, 
60    pub cache: HashMap<String, Value>,           
61    pub evaluating: HashSet<String>,           
62    pub parent: Option<Value>,                    
63    pub order: Vec<String>,                       
64    pub display_names: HashMap<String, String>,   
65}
66
67impl PartialEq for Obj {
68    fn eq(&self, other: &Self) -> bool {
69        match (self, other) {
70            (Obj::String(a), Obj::String(b)) => a == b,
71            (Obj::List(a), Obj::List(b)) => a == b,
72            (Obj::Range(a), Obj::Range(b)) => a == b,
73            (Obj::Bytes(a), Obj::Bytes(b)) => a == b,
74            (Obj::Guid(a), Obj::Guid(b)) => a == b,
75            (Obj::DateTimeTicks(a), Obj::DateTimeTicks(b)) => a == b,
76            (Obj::Kvc(_), Obj::Kvc(_)) => false,
77            (Obj::Provider(_), Obj::Provider(_)) => false,
78            (Obj::NativeFn(a), Obj::NativeFn(b)) => {
79                std::ptr::eq(*a as *const (), *b as *const ())
80            }
81            _ => false,
82        }
83    }
84}
85
86impl std::fmt::Display for Obj {
87    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
88        match self {
89            Obj::String(s) => write!(f, "{}", s),
90            Obj::List(l) => {
91                let s: Vec<String> = l.iter().map(|v| v.to_string()).collect();
92                write!(f, "[{}]", s.join(", "))
93            },
94            Obj::Range(r) => write!(f, "<range start={} count={}>", r.start, r.count),
95            Obj::Bytes(b) => write!(f, "<bytes len={}>", b.len()),
96            Obj::Guid(g) => write!(f, "{}", g),
97            Obj::DateTimeTicks(ticks) => write!(f, "<datetime ticks={}>", ticks),
98            Obj::Kvc(kvc) => {
99                let kvc = kvc.borrow();
100                write!(f, "{{ ")?;
101                let mut first = true;
102                for k in kvc.order.iter() {
103                    if !first { write!(f, ", ")?; }
104                    first = false;
105                    let display = kvc.display_names.get(k).map(|s| s.as_str()).unwrap_or(k.as_str());
106                    if let Some(v) = kvc.cache.get(k) {
107                        write!(f, "{}: {}", display, v)?;
108                    } else {
109                        write!(f, "{}: <lazy>", display)?;
110                    }
111                }
112                write!(f, " }}")
113            }
114            Obj::Provider(_) => write!(f, "<provider>"),
115            Obj::Function(func) => write!(f, "<fn {}>", func.name),
116            Obj::NativeFn(_) => write!(f, "<native fn>"),
117        }
118    }
119}