aiscript_vm/
value.rs

1use std::fmt::Display;
2
3use aiscript_arena::{Collect, Gc, Mutation, RefLock, lock::GcRefLock};
4
5use crate::{
6    NativeFn,
7    ai::Agent,
8    object::{BoundMethod, Class, Closure, Enum, EnumVariant, Instance, List, ListKind, Object},
9    string::{InternedString, StringValue},
10    vm::{Context, VmError},
11};
12
13#[derive(Copy, Clone, Default, Collect)]
14#[collect(no_drop)]
15pub enum Value<'gc> {
16    Number(f64),
17    Boolean(bool),
18    // For identifiers, module names, etc.
19    String(InternedString<'gc>),
20    // For file contents, user input, etc. Not interned.
21    IoString(Gc<'gc, String>),
22    Closure(Gc<'gc, Closure<'gc>>),
23    NativeFunction(NativeFn<'gc>),
24    // Array(GcRefLock<'gc, Vec<Value<'gc>>>),
25    List(GcRefLock<'gc, List<'gc>>),
26    Object(GcRefLock<'gc, Object<'gc>>),
27    Enum(GcRefLock<'gc, Enum<'gc>>),
28    EnumVariant(Gc<'gc, EnumVariant<'gc>>),
29    Class(GcRefLock<'gc, Class<'gc>>),
30    Instance(GcRefLock<'gc, Instance<'gc>>),
31    BoundMethod(Gc<'gc, BoundMethod<'gc>>),
32    Module(InternedString<'gc>),
33    Agent(Gc<'gc, Agent<'gc>>),
34    #[default]
35    Nil,
36}
37
38impl PartialEq for Value<'_> {
39    fn eq(&self, other: &Self) -> bool {
40        self.equals(other)
41    }
42}
43
44impl Display for Value<'_> {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        match self {
47            Value::Number(v) => write!(f, "{}", v),
48            Value::Boolean(b) => write!(f, "{}", b),
49            Value::String(s) => write!(f, "{}", s),
50            Value::IoString(s) => write!(f, "{}", s),
51            Value::Closure(closure) => {
52                if let Some(name) = closure.function.name {
53                    write!(f, "<fn {}>", name)
54                } else {
55                    write!(f, "<script>")
56                }
57            }
58            Value::NativeFunction(_) => write!(f, "<native fn>"),
59            Value::List(list) => {
60                let list = list.borrow();
61                match list.kind {
62                    ListKind::Array => {
63                        write!(f, "[")?;
64                        for (i, value) in list.data.iter().enumerate() {
65                            if i > 0 {
66                                write!(f, ", ")?;
67                            }
68                            write!(f, "{}", value)?;
69                        }
70                        write!(f, "]")
71                    }
72                    ListKind::Tuple => {
73                        write!(f, "(")?;
74                        for (i, value) in list.data.iter().enumerate() {
75                            if i > 0 {
76                                write!(f, ", ")?;
77                            }
78                            write!(f, "{}", value)?;
79                        }
80                        // Add trailing comma for 1-element tuples
81                        if list.data.len() == 1 {
82                            write!(f, ",")?;
83                        }
84                        write!(f, ")")
85                    }
86                }
87            }
88            Value::Object(obj) => {
89                write!(f, "{{")?;
90                let mut first = true;
91                for (key, value) in &obj.borrow().fields {
92                    if !first {
93                        write!(f, ", ")?;
94                    }
95                    write!(f, "{}: {}", key, value)?;
96                    first = false;
97                }
98                write!(f, "}}")
99            }
100            Value::Enum(enum_) => write!(f, "enum {}", enum_.borrow().name),
101            Value::EnumVariant(variant) => {
102                write!(f, "{}::{}", variant.enum_.borrow().name, variant.name)?;
103                if !variant.value.is_nil() {
104                    write!(f, "({})", variant.value)
105                } else {
106                    Ok(())
107                }
108            }
109            Value::Class(class) => write!(f, "{}", class.borrow().name),
110            Value::Instance(instance) => {
111                let mut s = format!("{} {{", instance.borrow().class.borrow().name);
112                for (i, (key, value)) in instance.borrow().fields.iter().enumerate() {
113                    s.push_str(&format!("{}: {}", key, value));
114                    if i != instance.borrow().fields.len() - 1 {
115                        s.push_str(", ");
116                    }
117                }
118                s.push('}');
119                write!(f, "{}", s)
120            }
121            Value::BoundMethod(bm) => write!(f, "{}", bm.method.function),
122            Value::Agent(agent) => write!(f, "agent {}", agent.name),
123            Value::Module(module) => write!(f, "module {}", module),
124            Value::Nil => write!(f, "nil"),
125        }
126    }
127}
128
129impl<'gc> Value<'gc> {
130    #[inline]
131    pub fn array(mc: &Mutation<'gc>, data: Vec<Value<'gc>>) -> Self {
132        Value::List(Gc::new(mc, RefLock::new(List::array(data))))
133    }
134
135    #[inline]
136    pub fn equals(&self, other: &Value<'gc>) -> bool {
137        match (self, other) {
138            (Value::Number(a), Value::Number(b)) => a == b,
139            (Value::Boolean(a), Value::Boolean(b)) => a == b,
140            (Value::String(a), Value::String(b)) => a.equals(b),
141            (Value::IoString(a), Value::IoString(b)) => *a == *b,
142            (Value::String(a), Value::IoString(b)) => a.as_bytes() == b.as_bytes(),
143            (Value::IoString(a), Value::String(b)) => a.as_bytes() == b.as_bytes(),
144            (Value::List(a), Value::List(b)) => a.borrow().equals(&b.borrow()),
145            (Value::Object(a), Value::Object(b)) => Gc::ptr_eq(*a, *b),
146            (Value::Enum(a), Value::Enum(b)) => Gc::ptr_eq(*a, *b),
147            (Value::EnumVariant(a), Value::EnumVariant(b)) => {
148                // We only need to compare the enum type name and variant name, not the underlying value.
149                // The value is just for initialization and access, but doesn't affect the variant's identity.
150                Gc::ptr_eq(a.enum_, b.enum_) && a.name == b.name
151            }
152            (Value::Class(a), Value::Class(b)) => Gc::ptr_eq(*a, *b),
153            (Value::Closure(a), Value::Closure(b)) => Gc::ptr_eq(*a, *b),
154            (Value::Instance(a), Value::Instance(b)) => Gc::ptr_eq(*a, *b),
155            (Value::BoundMethod(a), Value::BoundMethod(b)) => Gc::ptr_eq(*a, *b),
156            (Value::Agent(a), Value::Agent(b)) => Gc::ptr_eq(*a, *b),
157            (Value::Nil, Value::Nil) => true,
158            // _ => core::mem::discriminant(self) == core::mem::discriminant(other),
159            _ => false,
160        }
161    }
162
163    pub fn as_number(self) -> Result<f64, VmError> {
164        match self {
165            Value::Number(value) => Ok(value),
166            a => Err(VmError::RuntimeError(format!(
167                "cannot convert to number: {}",
168                a
169            ))),
170        }
171    }
172
173    pub fn as_boolean(&self) -> bool {
174        match self {
175            Value::Boolean(value) => *value,
176            Value::Number(value) => *value != 0.0,
177            Value::String(s) => !s.is_empty(),
178            _ => false,
179        }
180    }
181
182    pub fn as_string(self) -> Result<InternedString<'gc>, VmError> {
183        match self {
184            Value::String(value) => Ok(value),
185            v => Err(VmError::RuntimeError(format!(
186                "cannot convert to string, the value is {v}"
187            ))),
188        }
189    }
190
191    // Helper method to convert any string value to a common format
192    pub fn as_string_value(&self) -> Result<StringValue<'gc>, VmError> {
193        match self {
194            Value::String(s) => Ok(StringValue::Interned(*s)),
195            Value::IoString(s) => Ok(StringValue::Dynamic(*s)),
196            _ => Err(VmError::RuntimeError("Not a string value".into())),
197        }
198    }
199
200    // Helper to create a new string value
201    pub fn new_string(ctx: Context<'gc>, s: &str, should_intern: bool) -> Value<'gc> {
202        if should_intern {
203            Value::String(ctx.intern(s.as_bytes()))
204        } else {
205            Value::IoString(Gc::new(&ctx, s.to_string()))
206        }
207    }
208
209    pub fn as_closure(self) -> Result<Gc<'gc, Closure<'gc>>, VmError> {
210        match self {
211            Value::Closure(closure) => Ok(closure),
212            v => Err(VmError::RuntimeError(format!(
213                "cannot convert to closure, the value is {v}"
214            ))),
215        }
216    }
217
218    pub fn as_array(self) -> Result<GcRefLock<'gc, List<'gc>>, VmError> {
219        match self {
220            Value::List(list) => Ok(list),
221            v => Err(VmError::RuntimeError(format!(
222                "cannot convert to array, the value is {v}"
223            ))),
224        }
225    }
226
227    pub fn as_agent(self) -> Result<Gc<'gc, Agent<'gc>>, VmError> {
228        match self {
229            Value::Agent(agent) => Ok(agent),
230            _ => Err(VmError::RuntimeError("cannot convert to agent.".into())),
231        }
232    }
233
234    pub fn as_class(self) -> Result<GcRefLock<'gc, Class<'gc>>, VmError> {
235        match self {
236            Value::Class(class) => Ok(class),
237            v => Err(VmError::RuntimeError(format!(
238                "cannot convert to class, the value is {v}"
239            ))),
240        }
241    }
242
243    pub fn is_error(&self) -> bool {
244        match self {
245            // Check instance of error class (NetworkError!)
246            Value::Instance(instance) => instance.borrow().class.borrow().is_error_type(),
247            // Check enum variant from error enum (IOError!::ReadError)
248            Value::EnumVariant(variant) => variant.enum_.borrow().is_error_type(),
249            _ => false,
250        }
251    }
252
253    pub fn is_object(&self) -> bool {
254        matches!(self, Value::Object(_))
255    }
256
257    pub fn is_bound_method(&self) -> bool {
258        matches!(self, Value::BoundMethod(_))
259    }
260
261    pub fn is_class(&self) -> bool {
262        matches!(self, Value::Class(_))
263    }
264
265    pub fn is_instance(&self) -> bool {
266        matches!(self, Value::Instance(_))
267    }
268
269    pub fn is_closure(&self) -> bool {
270        matches!(self, Value::Closure(_))
271    }
272
273    pub fn is_native_function(&self) -> bool {
274        matches!(self, Value::NativeFunction(_))
275    }
276
277    pub fn is_nil(&self) -> bool {
278        matches!(self, Value::Nil)
279    }
280
281    pub fn is_number(&self) -> bool {
282        matches!(self, Value::Number(_))
283    }
284
285    pub fn is_boolean(&self) -> bool {
286        matches!(self, Value::Boolean(_) | Value::Nil)
287    }
288
289    pub fn is_true(&self) -> bool {
290        !self.is_falsy()
291    }
292
293    pub fn is_falsy(&self) -> bool {
294        self.is_nil() || (self.is_boolean() && !self.as_boolean())
295    }
296
297    pub fn from_serde_value(ctx: Context<'gc>, value: &serde_json::Value) -> Value<'gc> {
298        match value {
299            serde_json::Value::Bool(b) => Value::Boolean(*b),
300            serde_json::Value::Number(number) => Value::Number(number.as_f64().unwrap()),
301            serde_json::Value::String(str) => {
302                let s = ctx.intern(str.as_bytes());
303                Value::from(s)
304            }
305            serde_json::Value::Object(obj) => {
306                let fields = obj
307                    .into_iter()
308                    .map(|(key, value)| {
309                        (
310                            ctx.intern(key.as_bytes()),
311                            Value::from_serde_value(ctx, value),
312                        )
313                    })
314                    .collect();
315                Value::Object(Gc::new(&ctx, RefLock::new(Object { fields })))
316            }
317            serde_json::Value::Array(array) => {
318                let data = array
319                    .iter()
320                    .map(|value| Value::from_serde_value(ctx, value))
321                    .collect();
322                Value::array(&ctx, data)
323            }
324            serde_json::Value::Null => Value::Nil,
325        }
326    }
327
328    pub fn to_serde_value(&self) -> serde_json::Value {
329        match self {
330            Value::Number(n) => (*n).into(),
331            Value::Boolean(b) => (*b).into(),
332            Value::String(str) => str.to_string().into(),
333            Value::IoString(str) => str.to_string().into(),
334            Value::List(list) => serde_json::Value::Array(
335                list.borrow()
336                    .data
337                    .iter()
338                    .map(|v| v.to_serde_value())
339                    .collect(),
340            ),
341            Value::Object(obj) => serde_json::Value::Object(
342                obj.borrow()
343                    .fields
344                    .iter()
345                    .map(|(k, v)| (k.to_string(), v.to_serde_value()))
346                    .collect(),
347            ),
348            Value::Instance(instance) => instance
349                .borrow()
350                .fields
351                .iter()
352                .map(|(k, v)| (k.to_string(), v.to_serde_value()))
353                .collect(),
354            Value::EnumVariant(variant) => variant.value.to_serde_value(),
355            _ => serde_json::Value::Null,
356        }
357    }
358}
359
360// Implementations for Enum and EnumVariant
361impl Value<'_> {
362    pub fn is_enum(&self) -> bool {
363        matches!(self, Value::Enum(_))
364    }
365
366    pub fn is_enum_variant(&self) -> bool {
367        matches!(self, Value::EnumVariant { .. })
368    }
369}
370
371impl From<u64> for Value<'_> {
372    fn from(value: u64) -> Self {
373        Value::Number(value as f64)
374    }
375}
376
377impl From<f64> for Value<'_> {
378    fn from(value: f64) -> Self {
379        Value::Number(value)
380    }
381}
382
383impl From<bool> for Value<'_> {
384    fn from(value: bool) -> Self {
385        Value::Boolean(value)
386    }
387}
388
389impl<'gc> From<InternedString<'gc>> for Value<'gc> {
390    fn from(value: InternedString<'gc>) -> Self {
391        Value::String(value)
392    }
393}
394
395impl<'gc> From<Gc<'gc, String>> for Value<'gc> {
396    fn from(value: Gc<'gc, String>) -> Self {
397        Value::IoString(value)
398    }
399}
400
401impl<'gc> From<Gc<'gc, Closure<'gc>>> for Value<'gc> {
402    fn from(value: Gc<'gc, Closure<'gc>>) -> Self {
403        Value::Closure(value)
404    }
405}
406
407impl<'gc> From<GcRefLock<'gc, Class<'gc>>> for Value<'gc> {
408    fn from(value: GcRefLock<'gc, Class<'gc>>) -> Self {
409        Value::Class(value)
410    }
411}
412
413impl<'gc> From<GcRefLock<'gc, Instance<'gc>>> for Value<'gc> {
414    fn from(value: GcRefLock<'gc, Instance<'gc>>) -> Self {
415        Value::Instance(value)
416    }
417}
418
419impl<'gc> From<Gc<'gc, BoundMethod<'gc>>> for Value<'gc> {
420    fn from(value: Gc<'gc, BoundMethod<'gc>>) -> Self {
421        Value::BoundMethod(value)
422    }
423}
424
425impl<'gc> From<Gc<'gc, Agent<'gc>>> for Value<'gc> {
426    fn from(value: Gc<'gc, Agent<'gc>>) -> Self {
427        Value::Agent(value)
428    }
429}