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().shape_by_symbol(symbol))
135 .or_else(|| cx.registry().codec_by_symbol(symbol))
136 .or_else(|| cx.registry().number_domain_by_symbol(symbol))
137 .cloned()
138}
139
140fn table_from_datum_entries(cx: &mut Cx, entries: Vec<(Datum, Datum)>) -> Result<Value> {
141 if entries
142 .iter()
143 .any(|(key, _)| !matches!(key, Datum::Symbol(_)))
144 {
145 return cx.factory().expr(Expr::from(Datum::Map(entries)));
146 }
147 let table_entries = entries
148 .into_iter()
149 .map(|(key, value)| {
150 let Datum::Symbol(symbol) = key else {
151 return Err(Error::Eval(
152 "non-symbol map key reached table conversion".to_owned(),
153 ));
154 };
155 Ok((symbol, value_from_datum(cx, value)?))
156 })
157 .collect::<Result<Vec<_>>>()?;
158 cx.factory().table(table_entries)
159}
160
161#[cfg(test)]
162mod tests {
163 use std::sync::Arc;
164
165 use super::*;
166 use crate::{ClassRef, Object};
167
168 use crate::testing::bare_cx as cx;
169
170 struct OpaqueValue(&'static str);
172
173 impl Object for OpaqueValue {
174 fn display(&self, _cx: &mut Cx) -> Result<String> {
175 Ok(format!("#<opaque {}>", self.0))
176 }
177
178 fn as_any(&self) -> &dyn std::any::Any {
179 self
180 }
181 }
182
183 impl crate::ObjectCompat for OpaqueValue {
184 fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
185 cx.factory().nil()
186 }
187 }
188
189 #[test]
190 fn ref_resolver_returns_symbol_ref_for_registered_function_value() {
191 let mut cx = cx();
192 let symbol = Symbol::qualified("test", "registered-fn");
193 let value = cx.factory().string("registered".to_owned()).unwrap();
194 cx.registry_mut()
195 .register_function_value(symbol.clone(), value.clone())
196 .unwrap();
197 let mut resolver = TemporaryRefResolver::new();
198
199 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
200
201 assert_eq!(reference, Ref::Symbol(symbol));
202 }
203
204 #[test]
205 fn ref_resolver_returns_content_ref_for_pure_value() {
206 let mut cx = cx();
207 let value = cx.factory().string("pure".to_owned()).unwrap();
208 let mut resolver = TemporaryRefResolver::new();
209
210 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
211
212 assert!(matches!(reference, Ref::Content(_)), "expected content ref");
213 let Ref::Content(id) = reference else {
214 return;
215 };
216 assert!(cx.datum_store().contains(&id));
217 let resolved = resolver.resolve_ref(&mut cx, &Ref::Content(id)).unwrap();
218 assert_eq!(
219 resolved,
220 ResolvedRef::Datum(Datum::String("pure".to_owned()))
221 );
222 }
223
224 #[test]
225 fn ref_resolver_returns_handle_ref_for_opaque_value() {
226 let mut cx = cx();
227 let value = cx.factory().opaque(Arc::new(OpaqueValue("one"))).unwrap();
228 let mut resolver = TemporaryRefResolver::new();
229
230 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
231
232 assert!(matches!(reference, Ref::Handle(_)));
233 }
234
235 #[test]
236 fn ref_resolver_reuses_handle_for_same_value() {
237 let mut cx = cx();
238 let value = cx.factory().opaque(Arc::new(OpaqueValue("same"))).unwrap();
239 let mut resolver = TemporaryRefResolver::new();
240
241 let first = resolver.ref_for_value(&mut cx, &value).unwrap();
242 let second = resolver.ref_for_value(&mut cx, &value).unwrap();
243
244 assert_eq!(first, second);
245 }
246
247 #[test]
248 fn ref_resolver_resolves_allocated_handle_to_value() {
249 let mut cx = cx();
250 let value = cx.factory().opaque(Arc::new(OpaqueValue("value"))).unwrap();
251 let mut resolver = TemporaryRefResolver::new();
252 let reference = resolver.ref_for_value(&mut cx, &value).unwrap();
253
254 let resolved = resolver.resolve_ref(&mut cx, &reference).unwrap();
255
256 assert_eq!(resolved, ResolvedRef::Value(value));
257 }
258
259 #[test]
260 fn ref_resolver_assigns_distinct_handles_to_distinct_opaque_values() {
261 let mut cx = cx();
262 let first_value = cx.factory().opaque(Arc::new(OpaqueValue("first"))).unwrap();
263 let second_value = cx
264 .factory()
265 .opaque(Arc::new(OpaqueValue("second")))
266 .unwrap();
267 let mut resolver = TemporaryRefResolver::new();
268
269 let first = resolver.ref_for_value(&mut cx, &first_value).unwrap();
270 let second = resolver.ref_for_value(&mut cx, &second_value).unwrap();
271
272 assert_ne!(first, second);
273 assert!(matches!(first, Ref::Handle(_)));
274 assert!(matches!(second, Ref::Handle(_)));
275 }
276
277 #[test]
278 fn ref_resolver_reports_unknown_content_as_missing() {
279 let mut cx = cx();
280 let resolver = TemporaryRefResolver::new();
281 let reference = Ref::Content(crate::ContentId::from_bytes(
282 Symbol::qualified("core", "sha256"),
283 [4; 32],
284 ));
285
286 let resolved = resolver.resolve_ref(&mut cx, &reference).unwrap();
287
288 assert_eq!(resolved, ResolvedRef::Missing(reference));
289 }
290
291 #[test]
292 fn ref_resolver_resolves_interned_content_as_datum() {
293 let mut cx = cx();
294 let datum = Datum::String("stored".to_owned());
295 let id = cx.datum_store_mut().intern(datum.clone()).unwrap();
296 let resolver = TemporaryRefResolver::new();
297
298 let resolved = resolver.resolve_ref(&mut cx, &Ref::Content(id)).unwrap();
299
300 assert_eq!(resolved, ResolvedRef::Datum(datum));
301 }
302}