1use crate::value::{NixAttrs, SmolStr, Value, Rc};
9use sui_bytecode::value::{StringKeyedValue, VMValue};
10use sui_intern::Interner;
11
12#[must_use]
18pub fn string_keyed_to_eval(sk: &StringKeyedValue) -> Value {
19 match sk {
20 StringKeyedValue::Null => Value::Null,
21 StringKeyedValue::Bool(b) => Value::Bool(*b),
22 StringKeyedValue::Int(n) => Value::Int(*n),
23 StringKeyedValue::Float(f) => Value::Float(*f),
24 StringKeyedValue::String(s) => Value::string(s.clone()),
25 StringKeyedValue::Path(p) => Value::Path(Box::new(SmolStr::from(p.as_str()))),
26 StringKeyedValue::List(items) => {
27 Value::list(items.iter().map(string_keyed_to_eval).collect())
28 }
29 StringKeyedValue::Attrs(map) => {
30 let mut attrs = NixAttrs::with_capacity(map.len());
31 for (k, v) in map {
32 attrs.insert(k.clone(), string_keyed_to_eval(v));
33 }
34 Value::Attrs(Rc::new(attrs))
35 }
36 StringKeyedValue::Lambda => Value::Null, StringKeyedValue::Callable(cb) => {
38 let cb_clone = std::rc::Rc::clone(cb);
40 Value::Builtin(Box::new(crate::value::BuiltinFn {
41 name: "<bridge-fn>",
42 func: std::rc::Rc::new(move |args: &[Value]| {
43 let arg = args.first().cloned().unwrap_or(Value::Null);
44 let sk_arg = crate::eval_to_string_keyed(&arg);
45 let sk_result = cb_clone(sk_arg)
46 .map_err(|e| crate::value::EvalError::TypeError(e))?;
47 Ok(string_keyed_to_eval(&sk_result))
48 }),
49 }))
50 }
51 StringKeyedValue::Thunk(cb) => {
52 let cb_clone = std::rc::Rc::clone(cb);
54 Value::Thunk(crate::value::Thunk::new_native(move || {
55 let sk_val = cb_clone()
56 .map_err(|e| crate::value::EvalError::TypeError(e))?;
57 Ok(string_keyed_to_eval(&sk_val))
58 }))
59 }
60 }
61}
62
63#[must_use]
67pub fn vm_to_eval(vm: &VMValue, interner: &Interner) -> Value {
68 let sk = vm.to_string_keyed(interner);
69 string_keyed_to_eval(&sk)
70}
71
72#[must_use]
79pub fn eval_to_vm(val: &Value, interner: &mut Interner) -> VMValue {
80 match val {
81 Value::Null => VMValue::Null,
82 Value::Bool(b) => VMValue::Bool(*b),
83 Value::Int(n) => VMValue::Int(*n),
84 Value::Float(f) => VMValue::Float(*f),
85 Value::String(s) => VMValue::String(s.chars.to_string()),
86 Value::Path(p) => VMValue::Path(p.to_string()),
87 Value::List(items) => {
88 VMValue::List(items.iter().map(|v| eval_to_vm(v, interner)).collect())
89 }
90 Value::Attrs(attrs) => {
91 let mut map = std::collections::BTreeMap::new();
92 for (k, v) in attrs.iter() {
93 let sym = interner.intern(&k);
94 map.insert(sym, eval_to_vm(v, interner));
95 }
96 VMValue::Attrs(map)
97 }
98 Value::Lambda(_) | Value::Builtin(_) => {
100 let sk = crate::eval_to_string_keyed(val);
101 sui_bytecode::builtins::string_keyed_to_vmvalue(&sk, interner)
102 }
103 Value::Thunk(t) => {
104 if t.is_evaluated() {
105 match t.force(&|e, env| crate::eval::eval_expr(e, env)) {
106 Ok(v) => eval_to_vm(&v, interner),
107 Err(_) => VMValue::Null,
108 }
109 } else {
110 let thunk_clone = t.clone();
112 let cb: std::rc::Rc<dyn Fn() -> Result<StringKeyedValue, String>> =
113 std::rc::Rc::new(move || {
114 let forced = thunk_clone
115 .force(&|e, env| crate::eval::eval_expr(e, env))
116 .map_err(|e| e.to_string())?;
117 Ok(crate::eval_to_string_keyed(&forced))
118 });
119 VMValue::Thunk(sui_bytecode::VMThunk {
120 state: std::rc::Rc::new(std::cell::Cell::new(Some(
121 sui_bytecode::value::ThunkState::NativeCallback(cb),
122 ))),
123 })
124 }
125 }
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn roundtrip_scalar_null() {
135 let sk = StringKeyedValue::Null;
136 let val = string_keyed_to_eval(&sk);
137 assert_eq!(val, Value::Null);
138 }
139
140 #[test]
141 fn roundtrip_scalar_bool() {
142 assert_eq!(string_keyed_to_eval(&StringKeyedValue::Bool(true)), Value::Bool(true));
143 assert_eq!(string_keyed_to_eval(&StringKeyedValue::Bool(false)), Value::Bool(false));
144 }
145
146 #[test]
147 fn roundtrip_scalar_int() {
148 assert_eq!(string_keyed_to_eval(&StringKeyedValue::Int(42)), Value::Int(42));
149 }
150
151 #[test]
152 fn roundtrip_scalar_float() {
153 assert_eq!(string_keyed_to_eval(&StringKeyedValue::Float(3.14)), Value::Float(3.14));
154 }
155
156 #[test]
157 fn roundtrip_string() {
158 let sk = StringKeyedValue::String("hello".to_string());
159 let val = string_keyed_to_eval(&sk);
160 assert_eq!(val, Value::string("hello"));
161 }
162
163 #[test]
164 fn roundtrip_path() {
165 let sk = StringKeyedValue::Path("/tmp/x".to_string());
166 let val = string_keyed_to_eval(&sk);
167 assert_eq!(val, Value::Path(Box::new(SmolStr::from("/tmp/x"))));
168 }
169
170 #[test]
171 fn roundtrip_list() {
172 let sk = StringKeyedValue::List(vec![
173 StringKeyedValue::Int(1),
174 StringKeyedValue::Int(2),
175 ]);
176 let val = string_keyed_to_eval(&sk);
177 assert_eq!(val, Value::list(vec![Value::Int(1), Value::Int(2)]));
178 }
179
180 #[test]
181 fn roundtrip_attrs() {
182 let mut map = std::collections::BTreeMap::new();
183 map.insert("a".to_string(), StringKeyedValue::Int(1));
184 map.insert("b".to_string(), StringKeyedValue::String("hi".to_string()));
185 let sk = StringKeyedValue::Attrs(map);
186 let val = string_keyed_to_eval(&sk);
187 match val {
188 Value::Attrs(attrs) => {
189 assert_eq!(attrs.get("a"), Some(&Value::Int(1)));
190 assert_eq!(attrs.get("b"), Some(&Value::string("hi")));
191 }
192 _ => panic!("expected Attrs"),
193 }
194 }
195
196 #[test]
197 fn lambda_becomes_null() {
198 let sk = StringKeyedValue::Lambda;
199 let val = string_keyed_to_eval(&sk);
200 assert_eq!(val, Value::Null);
201 }
202
203 #[test]
204 fn vm_to_eval_with_interner() {
205 let mut interner = Interner::new();
206 let key = interner.intern("x");
207 let mut attrs = std::collections::BTreeMap::new();
208 attrs.insert(key, VMValue::Int(42));
209 let vm_val = VMValue::Attrs(attrs);
210 let eval_val = vm_to_eval(&vm_val, &interner);
211 match eval_val {
212 Value::Attrs(a) => assert_eq!(a.get("x"), Some(&Value::Int(42))),
213 _ => panic!("expected Attrs"),
214 }
215 }
216
217 #[test]
218 fn eval_to_vm_scalars() {
219 let mut interner = Interner::new();
220 assert_eq!(eval_to_vm(&Value::Null, &mut interner), VMValue::Null);
221 assert_eq!(eval_to_vm(&Value::Bool(true), &mut interner), VMValue::Bool(true));
222 assert_eq!(eval_to_vm(&Value::Int(7), &mut interner), VMValue::Int(7));
223 assert_eq!(eval_to_vm(&Value::Float(1.5), &mut interner), VMValue::Float(1.5));
224 }
225
226 #[test]
227 fn eval_to_vm_string() {
228 let mut interner = Interner::new();
229 let val = Value::string("test");
230 let vm = eval_to_vm(&val, &mut interner);
231 assert_eq!(vm, VMValue::String("test".to_string()));
232 }
233
234 #[test]
235 fn eval_to_vm_attrs() {
236 let mut interner = Interner::new();
237 let mut attrs = NixAttrs::new();
238 attrs.insert("key".to_string(), Value::Int(99));
239 let val = Value::Attrs(Rc::new(attrs));
240 let vm = eval_to_vm(&val, &mut interner);
241 let sk = vm.to_string_keyed(&interner);
242 match sk {
243 StringKeyedValue::Attrs(map) => {
244 assert_eq!(map.get("key"), Some(&StringKeyedValue::Int(99)));
245 }
246 _ => panic!("expected Attrs"),
247 }
248 }
249}