1use std::cell::Cell;
15use std::collections::BTreeMap;
16use std::fmt;
17use std::path::PathBuf;
18use std::rc::Rc;
19
20use crate::chunk::Chunk;
21use crate::intern::{Interner, Symbol};
22use crate::nanbox::NanBox;
23
24#[derive(Clone)]
33pub enum VMValue {
34 Null,
36 Bool(bool),
38 Int(i64),
40 Float(f64),
42 String(String),
44 Path(String),
46 List(Vec<VMValue>),
48 Attrs(BTreeMap<Symbol, VMValue>),
50 Closure(VMClosure),
52 Builtin(VMBuiltin),
54 Thunk(VMThunk),
56 HigherOrderBuiltin(HigherOrderBuiltin),
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum HigherOrderOp {
66 Map,
68 Filter,
70 FoldlP1,
72 FoldlP2,
74 Sort,
76 GenList,
78 ConcatMap,
80 Any,
82 All,
84 Partition,
86 GroupBy,
88 MapAttrs,
90 FilterAttrs,
92 Elem,
94}
95
96#[derive(Clone)]
99pub struct HigherOrderBuiltin {
100 pub op: HigherOrderOp,
102 pub func: Box<VMValue>,
104 pub extra_args: Vec<VMValue>,
106}
107
108#[derive(Clone)]
110pub struct VMClosure {
111 pub chunk: Rc<Chunk>,
113 pub upvalues: Vec<NanBox>,
119 pub arity: u16,
122 pub name: Option<String>,
124 pub formals: Vec<(String, bool)>,
129}
130
131#[derive(Clone)]
133pub struct VMBuiltin {
134 pub name: &'static str,
136 pub func: Rc<dyn Fn(Vec<VMValue>) -> Result<VMValue, crate::error::VMError>>,
138 pub arity: u8,
140}
141
142impl fmt::Debug for VMBuiltin {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 write!(f, "<builtin {}>", self.name)
145 }
146}
147
148impl fmt::Debug for HigherOrderBuiltin {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 write!(f, "<hof {:?}>", self.op)
151 }
152}
153
154#[derive(Clone)]
156pub enum ThunkState {
157 Pending {
160 chunk: Rc<Chunk>,
161 upvalues: Vec<NanBox>,
162 },
163 LazySource {
167 source: Rc<String>,
169 offset: usize,
171 length: usize,
173 base_dir: PathBuf,
175 upvalues: Vec<NanBox>,
177 },
178 NativeCallback(Rc<dyn Fn() -> Result<StringKeyedValue, String>>),
185 Evaluating,
187 Done(Box<VMValue>),
189}
190
191#[derive(Clone)]
193pub struct VMThunk {
194 pub state: Rc<Cell<Option<ThunkState>>>,
195}
196
197impl VMThunk {
198 pub fn new(chunk: Rc<Chunk>, upvalues: Vec<NanBox>) -> Self {
200 Self {
201 state: Rc::new(Cell::new(Some(ThunkState::Pending { chunk, upvalues }))),
202 }
203 }
204
205 pub fn new_done(value: VMValue) -> Self {
207 Self {
208 state: Rc::new(Cell::new(Some(ThunkState::Done(Box::new(value))))),
209 }
210 }
211
212 pub fn new_native<F>(callback: F) -> Self
218 where
219 F: Fn() -> Result<VMValue, crate::error::VMError> + 'static,
220 {
221 let wrapped: Rc<dyn Fn() -> Result<StringKeyedValue, String>> =
224 Rc::new(move || {
225 let val = callback().map_err(|e| e.to_string())?;
226 let interner = crate::intern::Interner::new();
227 Ok(val.to_string_keyed(&interner))
228 });
229 Self {
230 state: Rc::new(Cell::new(Some(ThunkState::NativeCallback(wrapped)))),
231 }
232 }
233}
234
235impl fmt::Debug for VMThunk {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 write!(f, "<thunk>")
238 }
239}
240
241impl fmt::Debug for VMClosure {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 write!(f, "<closure arity={}", self.arity)?;
244 if let Some(ref name) = self.name {
245 write!(f, " name={name}")?;
246 }
247 write!(f, ">")
248 }
249}
250
251impl VMValue {
252 #[must_use]
254 pub fn type_name(&self) -> &'static str {
255 match self {
256 VMValue::Null => "null",
257 VMValue::Bool(_) => "bool",
258 VMValue::Int(_) => "int",
259 VMValue::Float(_) => "float",
260 VMValue::String(_) => "string",
261 VMValue::Path(_) => "path",
262 VMValue::List(_) => "list",
263 VMValue::Attrs(_) => "set",
264 VMValue::Closure(_) | VMValue::Builtin(_) | VMValue::HigherOrderBuiltin(_) => "lambda",
265 VMValue::Thunk(_) => "thunk",
266 }
267 }
268
269 pub fn is_truthy(&self) -> Result<bool, crate::error::VMError> {
271 match self {
272 VMValue::Bool(b) => Ok(*b),
273 other => Err(crate::error::VMError::TypeError {
274 expected: "bool",
275 got: other.type_name(),
276 context: "condition".to_string(),
277 }),
278 }
279 }
280
281 #[must_use]
284 pub fn attrs_to_strings(&self, interner: &Interner) -> Option<BTreeMap<String, VMValue>> {
285 match self {
286 VMValue::Attrs(attrs) => {
287 let map = attrs
288 .iter()
289 .map(|(sym, val)| (interner.resolve(*sym).to_string(), val.clone()))
290 .collect();
291 Some(map)
292 }
293 _ => None,
294 }
295 }
296
297 #[must_use]
300 pub fn to_string_keyed(&self, interner: &Interner) -> StringKeyedValue {
301 match self {
302 VMValue::Null => StringKeyedValue::Null,
303 VMValue::Bool(b) => StringKeyedValue::Bool(*b),
304 VMValue::Int(n) => StringKeyedValue::Int(*n),
305 VMValue::Float(f) => StringKeyedValue::Float(*f),
306 VMValue::String(s) => StringKeyedValue::String(s.clone()),
307 VMValue::Path(p) => StringKeyedValue::Path(p.clone()),
308 VMValue::List(items) => {
309 StringKeyedValue::List(items.iter().map(|v| v.to_string_keyed(interner)).collect())
310 }
311 VMValue::Attrs(attrs) => {
312 let map = attrs
313 .iter()
314 .map(|(sym, val)| {
315 (interner.resolve(*sym).to_string(), val.to_string_keyed(interner))
316 })
317 .collect();
318 StringKeyedValue::Attrs(map)
319 }
320 VMValue::Closure(_) | VMValue::Builtin(_) | VMValue::HigherOrderBuiltin(_) => {
321 StringKeyedValue::Lambda
322 }
323 VMValue::Thunk(t) => {
324 let state = t.state.take();
327 match &state {
328 Some(ThunkState::Done(v)) => {
329 let result = v.to_string_keyed(interner);
330 t.state.set(state);
331 result
332 }
333 _ => {
334 t.state.set(state);
335 StringKeyedValue::Lambda
336 }
337 }
338 }
339 }
340 }
341
342 pub fn display_with(&self, interner: &Interner, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self {
345 VMValue::Null => write!(f, "null"),
346 VMValue::Bool(b) => write!(f, "{b}"),
347 VMValue::Int(n) => write!(f, "{n}"),
348 VMValue::Float(n) => {
349 if n.fract() == 0.0 {
350 write!(f, "{n:.6}")
351 } else {
352 write!(f, "{n}")
353 }
354 }
355 VMValue::String(s) => write!(f, "\"{s}\""),
356 VMValue::Path(p) => write!(f, "{p}"),
357 VMValue::List(items) => {
358 write!(f, "[ ")?;
359 for item in items {
360 item.display_with(interner, f)?;
361 write!(f, " ")?;
362 }
363 write!(f, "]")
364 }
365 VMValue::Attrs(map) => {
366 write!(f, "{{ ")?;
367 for (sym, v) in map {
368 let key = interner.resolve(*sym);
369 write!(f, "{key} = ")?;
370 v.display_with(interner, f)?;
371 write!(f, "; ")?;
372 }
373 write!(f, "}}")
374 }
375 VMValue::Closure(_) => write!(f, "<<lambda>>"),
376 VMValue::Builtin(b) => write!(f, "<<builtin {}>>", b.name),
377 VMValue::HigherOrderBuiltin(h) => write!(f, "<<builtin {:?}>>", h.op),
378 VMValue::Thunk(_) => write!(f, "<<thunk>>"),
379 }
380 }
381
382 pub fn debug_with(&self, interner: &Interner, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 match self {
385 VMValue::Null => write!(f, "null"),
386 VMValue::Bool(b) => write!(f, "{b}"),
387 VMValue::Int(n) => write!(f, "{n}"),
388 VMValue::Float(n) => write!(f, "{}", sui_compat::versions::cppnix_format_float(*n)),
389 VMValue::String(s) => write!(f, "{s:?}"),
390 VMValue::Path(p) => write!(f, "{p}"),
391 VMValue::List(items) => {
392 write!(f, "[ ")?;
393 for item in items {
394 item.debug_with(interner, f)?;
395 write!(f, " ")?;
396 }
397 write!(f, "]")
398 }
399 VMValue::Attrs(map) => {
400 write!(f, "{{ ")?;
401 for (sym, v) in map {
402 let key = interner.resolve(*sym);
403 write!(f, "{key} = ")?;
404 v.debug_with(interner, f)?;
405 write!(f, "; ")?;
406 }
407 write!(f, "}}")
408 }
409 VMValue::Closure(c) => write!(f, "{c:?}"),
410 VMValue::Builtin(b) => write!(f, "{b:?}"),
411 VMValue::HigherOrderBuiltin(h) => write!(f, "{h:?}"),
412 VMValue::Thunk(t) => write!(f, "{t:?}"),
413 }
414 }
415}
416
417pub enum StringKeyedValue {
428 Null,
429 Bool(bool),
430 Int(i64),
431 Float(f64),
432 String(String),
433 Path(String),
434 List(Vec<StringKeyedValue>),
435 Attrs(BTreeMap<String, StringKeyedValue>),
436 Lambda,
437 Thunk(Rc<dyn Fn() -> Result<StringKeyedValue, String>>),
443 Callable(Rc<dyn Fn(StringKeyedValue) -> Result<StringKeyedValue, String>>),
449}
450
451impl Clone for StringKeyedValue {
452 fn clone(&self) -> Self {
453 match self {
454 Self::Null => Self::Null,
455 Self::Bool(b) => Self::Bool(*b),
456 Self::Int(n) => Self::Int(*n),
457 Self::Float(f) => Self::Float(*f),
458 Self::String(s) => Self::String(s.clone()),
459 Self::Path(p) => Self::Path(p.clone()),
460 Self::List(items) => Self::List(items.clone()),
461 Self::Attrs(map) => Self::Attrs(map.clone()),
462 Self::Lambda => Self::Lambda,
463 Self::Thunk(cb) => Self::Thunk(Rc::clone(cb)),
464 Self::Callable(cb) => Self::Callable(Rc::clone(cb)),
465 }
466 }
467}
468
469impl PartialEq for StringKeyedValue {
470 fn eq(&self, other: &Self) -> bool {
471 match (self, other) {
472 (Self::Null, Self::Null) => true,
473 (Self::Bool(a), Self::Bool(b)) => a == b,
474 (Self::Int(a), Self::Int(b)) => a == b,
475 (Self::Float(a), Self::Float(b)) => a == b,
476 (Self::String(a), Self::String(b)) => a == b,
477 (Self::Path(a), Self::Path(b)) => a == b,
478 (Self::List(a), Self::List(b)) => a == b,
479 (Self::Attrs(a), Self::Attrs(b)) => a == b,
480 (Self::Lambda, Self::Lambda) => true,
481 (Self::Thunk(_), _) | (_, Self::Thunk(_)) => false,
484 (Self::Callable(_), _) | (_, Self::Callable(_)) => false,
486 _ => false,
487 }
488 }
489}
490
491impl Eq for StringKeyedValue {}
492
493impl fmt::Debug for StringKeyedValue {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 match self {
496 Self::Null => write!(f, "Null"),
497 Self::Bool(b) => write!(f, "Bool({b})"),
498 Self::Int(n) => write!(f, "Int({n})"),
499 Self::Float(v) => write!(f, "Float({v})"),
500 Self::String(s) => write!(f, "String({s:?})"),
501 Self::Path(p) => write!(f, "Path({p:?})"),
502 Self::List(items) => f.debug_tuple("List").field(items).finish(),
503 Self::Attrs(map) => f.debug_tuple("Attrs").field(map).finish(),
504 Self::Lambda => write!(f, "Lambda"),
505 Self::Thunk(_) => write!(f, "Thunk(<deferred>)"),
506 Self::Callable(_) => write!(f, "Callable(<bridge-fn>)"),
507 }
508 }
509}
510
511impl fmt::Display for StringKeyedValue {
512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513 match self {
514 StringKeyedValue::Null => write!(f, "null"),
515 StringKeyedValue::Bool(b) => write!(f, "{b}"),
516 StringKeyedValue::Int(n) => write!(f, "{n}"),
517 StringKeyedValue::Float(n) => write!(f, "{}", sui_compat::versions::cppnix_format_float(*n)),
518 StringKeyedValue::String(s) => write!(f, "\"{s}\""),
519 StringKeyedValue::Path(p) => write!(f, "{p}"),
520 StringKeyedValue::List(items) => {
521 write!(f, "[ ")?;
522 for item in items {
523 write!(f, "{item} ")?;
524 }
525 write!(f, "]")
526 }
527 StringKeyedValue::Attrs(map) => {
528 write!(f, "{{ ")?;
529 for (k, v) in map {
530 write!(f, "{k} = {v}; ")?;
531 }
532 write!(f, "}}")
533 }
534 StringKeyedValue::Lambda => write!(f, "<<lambda>>"),
535 StringKeyedValue::Thunk(_) => write!(f, "<<thunk>>"),
536 StringKeyedValue::Callable(_) => write!(f, "<<lambda>>"),
537 }
538 }
539}
540
541impl fmt::Debug for VMValue {
544 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545 match self {
546 VMValue::Null => write!(f, "null"),
547 VMValue::Bool(b) => write!(f, "{b}"),
548 VMValue::Int(n) => write!(f, "{n}"),
549 VMValue::Float(n) => write!(f, "{}", sui_compat::versions::cppnix_format_float(*n)),
550 VMValue::String(s) => write!(f, "{s:?}"),
551 VMValue::Path(p) => write!(f, "{p}"),
552 VMValue::List(items) => {
553 write!(f, "[ ")?;
554 for item in items {
555 write!(f, "{item:?} ")?;
556 }
557 write!(f, "]")
558 }
559 VMValue::Attrs(map) => {
560 write!(f, "{{ ")?;
561 for (sym, v) in map {
562 write!(f, "#{} = {v:?}; ", sym.index())?;
563 }
564 write!(f, "}}")
565 }
566 VMValue::Closure(c) => write!(f, "{c:?}"),
567 VMValue::Builtin(b) => write!(f, "{b:?}"),
568 VMValue::HigherOrderBuiltin(h) => write!(f, "{h:?}"),
569 VMValue::Thunk(t) => write!(f, "{t:?}"),
570 }
571 }
572}
573
574impl fmt::Display for VMValue {
575 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576 match self {
577 VMValue::Null => write!(f, "null"),
578 VMValue::Bool(b) => write!(f, "{b}"),
579 VMValue::Int(n) => write!(f, "{n}"),
580 VMValue::Float(n) => {
581 if n.fract() == 0.0 {
583 write!(f, "{n:.6}")
584 } else {
585 write!(f, "{n}")
586 }
587 }
588 VMValue::String(s) => write!(f, "\"{s}\""),
589 VMValue::Path(p) => write!(f, "{p}"),
590 VMValue::List(items) => {
591 write!(f, "[ ")?;
592 for item in items {
593 write!(f, "{item} ")?;
594 }
595 write!(f, "]")
596 }
597 VMValue::Attrs(map) => {
598 write!(f, "{{ ")?;
599 for (sym, v) in map {
600 write!(f, "#{} = {v}; ", sym.index())?;
601 }
602 write!(f, "}}")
603 }
604 VMValue::Closure(_) => write!(f, "<<lambda>>"),
605 VMValue::Builtin(b) => write!(f, "<<builtin {}>>", b.name),
606 VMValue::HigherOrderBuiltin(h) => write!(f, "<<builtin {:?}>>", h.op),
607 VMValue::Thunk(_) => write!(f, "<<thunk>>"),
608 }
609 }
610}
611
612impl PartialEq for VMValue {
613 fn eq(&self, other: &Self) -> bool {
614 match (self, other) {
615 (VMValue::Null, VMValue::Null) => true,
616 (VMValue::Bool(a), VMValue::Bool(b)) => a == b,
617 (VMValue::Int(a), VMValue::Int(b)) => a == b,
618 (VMValue::Float(a), VMValue::Float(b)) => a == b,
619 (VMValue::Int(a), VMValue::Float(b)) | (VMValue::Float(b), VMValue::Int(a)) => {
620 (*a as f64) == *b
621 }
622 (VMValue::String(a), VMValue::String(b)) => a == b,
623 (VMValue::Path(a), VMValue::Path(b)) => a == b,
624 (VMValue::List(a), VMValue::List(b)) => a == b,
625 (VMValue::Attrs(a), VMValue::Attrs(b)) => a == b,
626 _ => false,
627 }
628 }
629}
630
631impl Eq for VMValue {}
632
633#[cfg(test)]
634mod tests {
635 use super::*;
636
637 #[test]
638 fn type_names() {
639 assert_eq!(VMValue::Null.type_name(), "null");
640 assert_eq!(VMValue::Bool(true).type_name(), "bool");
641 assert_eq!(VMValue::Int(0).type_name(), "int");
642 assert_eq!(VMValue::Float(0.0).type_name(), "float");
643 assert_eq!(VMValue::String("".to_string()).type_name(), "string");
644 assert_eq!(VMValue::Path("/tmp".to_string()).type_name(), "path");
645 assert_eq!(VMValue::List(vec![]).type_name(), "list");
646 assert_eq!(VMValue::Attrs(BTreeMap::new()).type_name(), "set");
647 }
648
649 #[test]
650 fn equality_int_float_coercion() {
651 assert_eq!(VMValue::Int(1), VMValue::Float(1.0));
652 assert_eq!(VMValue::Float(1.0), VMValue::Int(1));
653 assert_ne!(VMValue::Int(1), VMValue::Float(1.5));
654 }
655
656 #[test]
657 fn equality_same_types() {
658 assert_eq!(VMValue::Null, VMValue::Null);
659 assert_eq!(VMValue::Bool(true), VMValue::Bool(true));
660 assert_ne!(VMValue::Bool(true), VMValue::Bool(false));
661 assert_eq!(VMValue::Int(42), VMValue::Int(42));
662 assert_eq!(
663 VMValue::String("hello".to_string()),
664 VMValue::String("hello".to_string())
665 );
666 }
667
668 #[test]
669 fn equality_different_types() {
670 assert_ne!(VMValue::Null, VMValue::Bool(false));
671 assert_ne!(VMValue::Int(0), VMValue::Bool(false));
672 assert_ne!(VMValue::String("1".to_string()), VMValue::Int(1));
673 }
674
675 #[test]
676 fn is_truthy_bool() {
677 assert!(VMValue::Bool(true).is_truthy().unwrap());
678 assert!(!VMValue::Bool(false).is_truthy().unwrap());
679 }
680
681 #[test]
682 fn is_truthy_non_bool_errors() {
683 assert!(VMValue::Int(1).is_truthy().is_err());
684 assert!(VMValue::Null.is_truthy().is_err());
685 }
686
687 #[test]
688 fn attrs_to_strings_conversion() {
689 let mut interner = Interner::new();
690 let key = interner.intern("hello");
691 let mut attrs = BTreeMap::new();
692 attrs.insert(key, VMValue::Int(42));
693 let val = VMValue::Attrs(attrs);
694 let string_map = val.attrs_to_strings(&interner).unwrap();
695 assert_eq!(string_map.get("hello"), Some(&VMValue::Int(42)));
696 }
697
698 #[test]
699 fn to_string_keyed_roundtrip() {
700 let mut interner = Interner::new();
701 let key = interner.intern("x");
702 let mut attrs = BTreeMap::new();
703 attrs.insert(key, VMValue::Int(1));
704 let val = VMValue::Attrs(attrs);
705 let sk = val.to_string_keyed(&interner);
706 match sk {
707 StringKeyedValue::Attrs(map) => {
708 assert_eq!(map.get("x"), Some(&StringKeyedValue::Int(1)));
709 }
710 _ => panic!("expected Attrs"),
711 }
712 }
713
714 #[test]
715 fn symbol_keyed_attrs_equality() {
716 let mut interner = Interner::new();
717 let k1 = interner.intern("a");
718 let k2 = interner.intern("a");
719 let mut a1 = BTreeMap::new();
720 a1.insert(k1, VMValue::Int(1));
721 let mut a2 = BTreeMap::new();
722 a2.insert(k2, VMValue::Int(1));
723 assert_eq!(VMValue::Attrs(a1), VMValue::Attrs(a2));
724 }
725}