1use crate::{
7 datum::Datum,
8 datum_store::DatumStore,
9 env::Cx,
10 error::{Error, Result},
11 expr::Expr,
12 handle_store::HandleStore,
13 id::Symbol,
14 object::is_default_object_header,
15 ref_id::{Coordinate, Ref},
16 value::Value,
17};
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum ResolvedRef {
22 Symbol(Symbol),
24 Datum(Datum),
26 Value(Value),
28 Coordinate(Coordinate),
30 Missing(Ref),
32}
33
34pub trait RefResolver {
39 fn ref_for_value(&mut self, cx: &mut Cx, value: &Value) -> Result<Ref>;
41 fn resolve_ref(&self, cx: &mut Cx, reference: &Ref) -> Result<ResolvedRef>;
43}
44
45#[derive(Default)]
48pub struct TemporaryRefResolver;
49
50impl TemporaryRefResolver {
51 pub fn new() -> Self {
53 Self
54 }
55}
56
57impl RefResolver for TemporaryRefResolver {
58 fn ref_for_value(&mut self, cx: &mut Cx, value: &Value) -> Result<Ref> {
59 if let Some(symbol) = cx.registry().export_symbol_for_value(value) {
60 return Ok(Ref::Symbol(symbol));
61 }
62
63 if let Some(datum) = value.object().snapshot(cx)? {
64 let id = cx.datum_store_mut().intern(datum)?;
65 return Ok(Ref::Content(id));
66 }
67
68 let header = value.object().header();
69 if !is_default_object_header(header) {
70 return Ok(header.id.clone());
71 }
72
73 let handle = cx.handles_mut().intern(value.clone());
74 Ok(Ref::Handle(handle))
75 }
76
77 fn resolve_ref(&self, cx: &mut Cx, reference: &Ref) -> Result<ResolvedRef> {
78 Ok(match reference {
79 Ref::Symbol(symbol) => ResolvedRef::Symbol(symbol.clone()),
80 Ref::Coord(coordinate) => ResolvedRef::Coordinate(coordinate.clone()),
81 Ref::Handle(handle) => cx.handles().get(handle).cloned().map_or_else(
82 || ResolvedRef::Missing(reference.clone()),
83 ResolvedRef::Value,
84 ),
85 Ref::Content(id) => cx.datum_store().get(id)?.cloned().map_or_else(
86 || ResolvedRef::Missing(reference.clone()),
87 ResolvedRef::Datum,
88 ),
89 })
90 }
91}
92
93pub fn value_from_ref(cx: &mut Cx, reference: &Ref) -> Result<Value> {
96 match TemporaryRefResolver::new().resolve_ref(cx, reference)? {
97 ResolvedRef::Symbol(symbol) => value_from_symbol(cx, &symbol)
98 .ok_or_else(|| Error::Eval(format!("unresolved symbol ref {symbol}"))),
99 ResolvedRef::Datum(datum) => value_from_datum(cx, datum),
100 ResolvedRef::Value(value) => Ok(value),
101 ResolvedRef::Coordinate(_) | ResolvedRef::Missing(_) => {
102 Err(Error::Eval(format!("unresolved value ref {reference:?}")))
103 }
104 }
105}
106
107pub fn value_from_datum(cx: &mut Cx, datum: Datum) -> Result<Value> {
110 match datum {
111 Datum::Nil => cx.factory().nil(),
112 Datum::Bool(value) => cx.factory().bool(value),
113 Datum::Number(number) => cx.factory().number_literal(number.domain, number.canonical),
114 Datum::Symbol(symbol) => cx.factory().symbol(symbol),
115 Datum::String(value) => cx.factory().string(value),
116 Datum::Bytes(value) => cx.factory().bytes(value),
117 Datum::List(items) | Datum::Vector(items) | Datum::Set(items) => {
118 let values = items
119 .into_iter()
120 .map(|item| value_from_datum(cx, item))
121 .collect::<Result<Vec<_>>>()?;
122 cx.factory().list(values)
123 }
124 Datum::Map(entries) => table_from_datum_entries(cx, entries),
125 Datum::Node { tag, fields } => cx.factory().expr(Expr::from(Datum::Node { tag, fields })),
126 }
127}
128
129fn value_from_symbol(cx: &Cx, symbol: &Symbol) -> Option<Value> {
130 cx.registry()
131 .value_by_symbol(symbol)
132 .or_else(|| cx.registry().function_by_symbol(symbol))
133 .or_else(|| cx.registry().class_by_symbol(symbol))
134 .or_else(|| cx.registry().macro_by_symbol(symbol))
135 .or_else(|| cx.registry().shape_by_symbol(symbol))
136 .or_else(|| cx.registry().codec_by_symbol(symbol))
137 .or_else(|| cx.registry().number_domain_by_symbol(symbol))
138 .or_else(|| cx.registry().site_by_symbol(symbol))
139 .cloned()
140}
141
142fn table_from_datum_entries(cx: &mut Cx, entries: Vec<(Datum, Datum)>) -> Result<Value> {
143 if entries
144 .iter()
145 .any(|(key, _)| !matches!(key, Datum::Symbol(_)))
146 {
147 return cx.factory().expr(Expr::from(Datum::Map(entries)));
148 }
149 let table_entries = entries
150 .into_iter()
151 .map(|(key, value)| {
152 let Datum::Symbol(symbol) = key else {
153 return Err(Error::Eval(
154 "non-symbol map key reached table conversion".to_owned(),
155 ));
156 };
157 Ok((symbol, value_from_datum(cx, value)?))
158 })
159 .collect::<Result<Vec<_>>>()?;
160 cx.factory().table(table_entries)
161}
162
163#[cfg(test)]
164mod tests {
165 use std::sync::Arc;
166
167 use super::*;
168 use crate::{ClassRef, Object};
169
170 use crate::testing::bare_cx as cx;
171
172 struct OpaqueValue(&'static str);
174
175 impl Object for OpaqueValue {
176 fn display(&self, _cx: &mut Cx) -> Result<String> {
177 Ok(format!("#<opaque {}>", self.0))
178 }
179
180 fn as_any(&self) -> &dyn std::any::Any {
181 self
182 }
183 }
184
185 impl crate::ObjectCompat for OpaqueValue {
186 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
187 cx.factory().nil()
188 }
189 }
190
191 #[test]
192 fn ref_resolver_returns_symbol_ref_for_registered_function_value() {
193 let mut cx = cx();
194 let symbol = Symbol::qualified("test", "registered-fn");
195 let value = cx.factory().string("registered".to_owned()).unwrap();
196 cx.registry_mut()
197 .register_function_value(symbol.clone(), value.clone())
198 .unwrap();
199 let mut resolver = TemporaryRefResolver::new();
200
201 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
202
203 assert_eq!(reference, Ref::Symbol(symbol));
204 }
205
206 #[test]
207 fn macro_export_round_trips_through_symbol_ref() {
208 let mut cx = cx();
209 let symbol = Symbol::qualified("macro", "fixture");
210 let value = cx.factory().bool(true).unwrap();
211 cx.registry_mut()
212 .register_macro_value(symbol.clone(), value.clone())
213 .unwrap();
214 let mut resolver = TemporaryRefResolver::new();
215
216 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
217 let resolved = value_from_ref(&mut cx, &reference).unwrap();
218
219 assert_eq!(reference, Ref::Symbol(symbol));
220 assert_eq!(resolved, value);
221 }
222
223 #[test]
224 fn site_export_round_trips_through_symbol_ref() {
225 let mut cx = cx();
226 let symbol = Symbol::qualified("model", "local");
227 let value = cx.factory().bool(true).unwrap();
228 cx.registry_mut()
229 .register_site_value(symbol.clone(), value.clone())
230 .unwrap();
231 let mut resolver = TemporaryRefResolver::new();
232
233 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
234 let resolved = value_from_ref(&mut cx, &reference).unwrap();
235
236 assert_eq!(reference, Ref::Symbol(symbol));
237 assert_eq!(resolved, value);
238 }
239
240 #[test]
241 fn ref_resolver_returns_content_ref_for_pure_value() {
242 let mut cx = cx();
243 let value = cx.factory().string("pure".to_owned()).unwrap();
244 let mut resolver = TemporaryRefResolver::new();
245
246 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
247
248 assert!(matches!(reference, Ref::Content(_)), "expected content ref");
249 let Ref::Content(id) = reference else {
250 return;
251 };
252 assert!(cx.datum_store().contains(&id));
253 let resolved = resolver.resolve_ref(&mut cx, &Ref::Content(id)).unwrap();
254 assert_eq!(
255 resolved,
256 ResolvedRef::Datum(Datum::String("pure".to_owned()))
257 );
258 }
259
260 #[test]
261 fn ref_resolver_returns_handle_ref_for_opaque_value() {
262 let mut cx = cx();
263 let value = cx.factory().opaque(Arc::new(OpaqueValue("one"))).unwrap();
264 let mut resolver = TemporaryRefResolver::new();
265
266 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
267
268 assert!(matches!(reference, Ref::Handle(_)));
269 }
270
271 #[test]
272 fn ref_resolver_reuses_handle_for_same_value() {
273 let mut cx = cx();
274 let value = cx.factory().opaque(Arc::new(OpaqueValue("same"))).unwrap();
275 let mut resolver = TemporaryRefResolver::new();
276
277 let first = resolver.ref_for_value(&mut cx, &value).unwrap();
278 let second = resolver.ref_for_value(&mut cx, &value).unwrap();
279
280 assert_eq!(first, second);
281 }
282
283 #[test]
284 fn ref_resolver_resolves_allocated_handle_to_value() {
285 let mut cx = cx();
286 let value = cx.factory().opaque(Arc::new(OpaqueValue("value"))).unwrap();
287 let mut resolver = TemporaryRefResolver::new();
288 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
289
290 let resolved = resolver.resolve_ref(&mut cx, &reference).unwrap();
291
292 assert_eq!(resolved, ResolvedRef::Value(value));
293 }
294
295 #[test]
296 fn ref_resolver_assigns_distinct_handles_to_distinct_opaque_values() {
297 let mut cx = cx();
298 let first_value = cx.factory().opaque(Arc::new(OpaqueValue("first"))).unwrap();
299 let second_value = cx
300 .factory()
301 .opaque(Arc::new(OpaqueValue("second")))
302 .unwrap();
303 let mut resolver = TemporaryRefResolver::new();
304
305 let first = resolver.ref_for_value(&mut cx, &first_value).unwrap();
306 let second = resolver.ref_for_value(&mut cx, &second_value).unwrap();
307
308 assert_ne!(first, second);
309 assert!(matches!(first, Ref::Handle(_)));
310 assert!(matches!(second, Ref::Handle(_)));
311 }
312
313 #[test]
314 fn ref_resolver_reports_unknown_content_as_missing() {
315 let mut cx = cx();
316 let resolver = TemporaryRefResolver::new();
317 let reference = Ref::Content(crate::ContentId::from_bytes(
318 Symbol::qualified("core", "sha256"),
319 [4; 32],
320 ));
321
322 let resolved = resolver.resolve_ref(&mut cx, &reference).unwrap();
323
324 assert_eq!(resolved, ResolvedRef::Missing(reference));
325 }
326
327 #[test]
328 fn ref_resolver_resolves_interned_content_as_datum() {
329 let mut cx = cx();
330 let datum = Datum::String("stored".to_owned());
331 let id = cx.datum_store_mut().intern(datum.clone()).unwrap();
332 let resolver = TemporaryRefResolver::new();
333
334 let resolved = resolver.resolve_ref(&mut cx, &Ref::Content(id)).unwrap();
335
336 assert_eq!(resolved, ResolvedRef::Datum(datum));
337 }
338}