spore_vm/val/
formatter.rs1use crate::Vm;
2
3use super::UnsafeVal;
4
5pub struct ValFormatter<'a> {
7 vm: &'a Vm,
8 val: UnsafeVal,
9 quote_strings: bool,
10}
11
12impl<'a> ValFormatter<'a> {
13 pub fn new(vm: &'a Vm, v: UnsafeVal) -> ValFormatter {
15 ValFormatter {
16 vm,
17 val: v,
18 quote_strings: false,
19 }
20 }
21
22 pub fn new_quoted(vm: &'a Vm, v: UnsafeVal) -> ValFormatter {
25 ValFormatter {
26 vm,
27 val: v,
28 quote_strings: true,
29 }
30 }
31}
32
33impl<'a> std::fmt::Display for ValFormatter<'a> {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match &self.val {
36 UnsafeVal::Void => write!(f, "<void>"),
37 UnsafeVal::Bool(x) => write!(f, "{x}"),
38 UnsafeVal::Int(x) => write!(f, "{x}"),
39 UnsafeVal::Float(x) => write!(f, "{x}"),
40 UnsafeVal::Symbol(x) => {
41 let name = self.vm.symbol_to_str(*x).unwrap_or("*corrupt-symbol*");
42 write!(f, "'{name}")
43 }
44 UnsafeVal::String(x) => {
45 if self.quote_strings {
46 write!(f, "{:?}", self.vm.objects.get_str(*x))
47 } else {
48 write!(f, "{}", self.vm.objects.get_str(*x))
49 }
50 }
51 UnsafeVal::MutableBox(x) => {
52 let inner = ValFormatter {
53 vm: self.vm,
54 val: *self.vm.objects.get_mutable_box(*x),
55 quote_strings: true,
56 };
57 write!(f, "box<{}>", inner)
58 }
59 UnsafeVal::List(x) => {
60 write!(f, "(")?;
61 for (idx, val) in self.vm.objects.get_list(*x).iter().enumerate() {
62 if idx == 0 {
63 write!(f, "{}", val.format_quoted(self.vm))?;
64 } else {
65 write!(f, " {}", val.format_quoted(self.vm))?;
66 }
67 }
68 write!(f, ")")
69 }
70 UnsafeVal::Struct(x) => {
71 write!(f, "(struct")?;
72 for (name, val) in self.vm.objects.get_struct(*x).iter() {
73 let val = val.format_quoted(self.vm);
74 let name = self
75 .vm
76 .symbol_to_str(name)
77 .unwrap_or("*unknown-symbol-name*");
78 write!(f, " '{name} {val}")?;
79 }
80 write!(f, ")")
81 }
82 UnsafeVal::ByteCodeFunction(bc) => {
83 let bc = self.vm.objects.get_bytecode(*bc).unwrap();
84 write!(
85 f,
86 "<function {name}>",
87 name = if bc.name.is_empty() {
88 "_"
89 } else {
90 bc.name.as_str()
91 }
92 )
93 }
94 UnsafeVal::NativeFunction(_) => write!(f, "<native-function>"),
95 UnsafeVal::Custom(c) => {
96 let c = self.vm.objects.get_custom(*c);
97 write!(f, "{c}")
98 }
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn format_void_is_empty() {
109 assert_eq!(
110 UnsafeVal::from(()).formatted(&Vm::default()).to_string(),
111 "<void>"
112 );
113 }
114
115 #[test]
116 fn format_bool_is_true_or_false() {
117 assert_eq!(
118 UnsafeVal::from(true).formatted(&Vm::default()).to_string(),
119 "true"
120 );
121 assert_eq!(
122 UnsafeVal::from(false).formatted(&Vm::default()).to_string(),
123 "false"
124 );
125 }
126
127 #[test]
128 fn format_int_prints_number() {
129 assert_eq!(
130 UnsafeVal::from(0).formatted(&Vm::default()).to_string(),
131 "0"
132 );
133 assert_eq!(
134 UnsafeVal::from(-1).formatted(&Vm::default()).to_string(),
135 "-1"
136 );
137 }
138
139 #[test]
140 fn format_float_prints_number() {
141 assert_eq!(
142 UnsafeVal::from(0.0).formatted(&Vm::default()).to_string(),
143 "0"
144 );
145 assert_eq!(
146 UnsafeVal::from(-1.5).formatted(&Vm::default()).to_string(),
147 "-1.5"
148 );
149 }
150
151 #[test]
152 fn format_string_produces_string() {
153 let mut vm = Vm::default();
154 let string_id = vm.objects.insert_string("my string".into());
155 assert_eq!(
156 UnsafeVal::from(string_id).formatted(&vm).to_string(),
157 "my string"
158 );
159 }
160
161 #[test]
162 fn format_struct_produces_all_key_values() {
163 let mut vm = Vm::default();
164 vm.eval_str("(define x (struct 'field-1 1 'field-2 \"2\"))")
165 .unwrap();
166 let res = vm.eval_str("x").unwrap();
167 assert!(
168 res.formatted(res.vm()).to_string() == "(struct 'field-1 1 'field-2 \"2\")"
169 || res.formatted(res.vm()).to_string() == "(struct 'field-2 \"2\" 'field-1 1)",
170 "{}",
171 res.formatted(res.vm()).to_string()
172 );
173 }
174
175 #[test]
176 fn format_mutable_box_produces_underlying_value() {
177 let mut vm = Vm::default();
178 vm.eval_str("(define x (new-box \"string\"))").unwrap();
179 let res = vm.eval_str("x").unwrap();
180 assert!(res.get_mutable_box_ref().is_ok());
181 assert_eq!(res.formatted(res.vm()).to_string(), "box<\"string\">");
182 }
183
184 #[test]
185 fn format_function_prints_name() {
186 let mut vm = Vm::default();
187 let v = vm.eval_str("(define (foo) 10) foo").unwrap();
188 assert_eq!(v.to_string(), "<function foo>");
189 }
190
191 #[test]
192 fn format_native_function_prints_native_function() {
193 let mut vm = Vm::default();
194 let v = vm.eval_str("+").unwrap();
195 assert_eq!(v.to_string(), "<native-function>");
196 }
197}