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