1mod types;
37mod unify;
38
39pub use types::*;
40pub use unify::unify;
41
42use eure_document::document::node::NodeValue;
43use eure_document::document::{EureDocument, NodeId};
44use eure_document::text::Language;
45use eure_document::value::{ObjectKey, PrimitiveValue};
46
47pub fn synth(doc: &EureDocument, node_id: NodeId) -> SynthType {
61 let node = doc.node(node_id);
62
63 match &node.content {
64 NodeValue::Hole(ident) => SynthType::Hole(ident.clone()),
65
66 NodeValue::Primitive(prim) => synth_primitive(prim),
67
68 NodeValue::Array(arr) => {
69 if arr.is_empty() {
70 SynthType::Array(Box::new(SynthType::Any))
71 } else {
72 let element_types: Vec<_> = arr.iter().map(|&id| synth(doc, id)).collect();
73 let unified = element_types
74 .into_iter()
75 .reduce(unify)
76 .unwrap_or(SynthType::Any);
77 SynthType::Array(Box::new(unified))
78 }
79 }
80
81 NodeValue::Tuple(tuple) => {
82 let element_types: Vec<_> = tuple.iter().map(|&id| synth(doc, id)).collect();
83 SynthType::Tuple(element_types)
84 }
85
86 NodeValue::Map(map) => {
87 if map.is_empty() {
88 SynthType::Record(SynthRecord::empty())
89 } else {
90 let mut fields = Vec::with_capacity(map.len());
91 for (key, &value_id) in map.iter() {
92 let field_name = object_key_to_field_name(key);
93 let field_type = synth(doc, value_id);
94 fields.push((field_name, SynthField::required(field_type)));
95 }
96 SynthType::Record(SynthRecord::new(fields))
97 }
98 }
99 NodeValue::PartialMap(_) => SynthType::Any,
100 }
101}
102
103fn synth_primitive(prim: &PrimitiveValue) -> SynthType {
105 match prim {
106 PrimitiveValue::Null => SynthType::Null,
107 PrimitiveValue::Bool(_) => SynthType::Boolean,
108 PrimitiveValue::Integer(_) => SynthType::Integer,
109 PrimitiveValue::F32(_) | PrimitiveValue::F64(_) => SynthType::Float,
110 PrimitiveValue::Text(text) => SynthType::Text(synth_text_language(&text.language)),
111 }
112}
113
114fn synth_text_language(lang: &Language) -> Option<String> {
116 match lang {
117 Language::Implicit => None,
118 Language::Plaintext => Some("plaintext".to_string()),
119 Language::Other(lang) => Some(lang.to_string()),
120 }
121}
122
123fn object_key_to_field_name(key: &ObjectKey) -> String {
128 match key {
129 ObjectKey::String(s) => s.clone(),
130 ObjectKey::Number(n) => n.to_string(),
131 ObjectKey::Tuple(t) => format!("{:?}", t), }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use eure_document::document::node::{NodeArray, NodeMap};
139 use eure_document::eure;
140 use eure_document::text::Text;
141 use eure_document::value::ObjectKey;
142 use num_bigint::BigInt;
143
144 #[test]
145 fn test_synth_primitives() {
146 let doc = EureDocument::new_primitive(PrimitiveValue::Null);
147 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Null);
148
149 let doc = EureDocument::new_primitive(PrimitiveValue::Bool(true));
150 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Boolean);
151
152 let doc = EureDocument::new_primitive(PrimitiveValue::Integer(BigInt::from(42)));
153 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Integer);
154
155 let doc = EureDocument::new_primitive(PrimitiveValue::F64(2.5));
156 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Float);
157
158 let doc = EureDocument::new_primitive(PrimitiveValue::Text(Text::plaintext("hello")));
159 assert_eq!(
160 synth(&doc, doc.get_root_id()),
161 SynthType::Text(Some("plaintext".to_string()))
162 );
163 }
164
165 #[test]
166 fn test_synth_empty_array() {
167 let doc = eure!({ arr = [] });
168 let root = doc.node(doc.get_root_id());
169 let arr_id = root
170 .as_map()
171 .unwrap()
172 .get_node_id(&ObjectKey::String("arr".into()))
173 .unwrap();
174 assert_eq!(
175 synth(&doc, arr_id),
176 SynthType::Array(Box::new(SynthType::Any))
177 );
178 }
179
180 #[test]
181 fn test_synth_homogeneous_array() {
182 let doc = eure!({ arr = [1, 2, 3] });
183 let root = doc.node(doc.get_root_id());
184 let arr_id = root
185 .as_map()
186 .unwrap()
187 .get_node_id(&ObjectKey::String("arr".into()))
188 .unwrap();
189 assert_eq!(
190 synth(&doc, arr_id),
191 SynthType::Array(Box::new(SynthType::Integer))
192 );
193 }
194
195 #[test]
196 fn test_synth_heterogeneous_array() {
197 let doc = eure!({ arr = [1, "hello"] });
198 let root = doc.node(doc.get_root_id());
199 let arr_id = root
200 .as_map()
201 .unwrap()
202 .get_node_id(&ObjectKey::String("arr".into()))
203 .unwrap();
204 assert_eq!(
205 synth(&doc, arr_id),
206 SynthType::Array(Box::new(SynthType::Union(SynthUnion {
207 variants: vec![
208 SynthType::Integer,
209 SynthType::Text(Some("plaintext".to_string()))
210 ]
211 })))
212 );
213 }
214
215 #[test]
216 fn test_synth_tuple() {
217 let doc = eure!({ tup = (1, "hello", true) });
218 let root = doc.node(doc.get_root_id());
219 let tup_id = root
220 .as_map()
221 .unwrap()
222 .get_node_id(&ObjectKey::String("tup".into()))
223 .unwrap();
224 assert_eq!(
225 synth(&doc, tup_id),
226 SynthType::Tuple(vec![
227 SynthType::Integer,
228 SynthType::Text(Some("plaintext".to_string())),
229 SynthType::Boolean,
230 ])
231 );
232 }
233
234 #[test]
235 fn test_synth_record() {
236 let doc = eure!({
237 name = "Alice"
238 age = 30
239 });
240 let ty = synth(&doc, doc.get_root_id());
241 let expected = SynthType::Record(SynthRecord::new([
242 (
243 "name".to_string(),
244 SynthField::required(SynthType::Text(Some("plaintext".to_string()))),
245 ),
246 ("age".to_string(), SynthField::required(SynthType::Integer)),
247 ]));
248 assert_eq!(ty, expected);
249 }
250
251 #[test]
252 fn test_synth_array_of_records_union() {
253 let mut doc = EureDocument::new_empty();
255
256 let a1_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
258 1,
259 ))));
260 let rec1_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([(
261 ObjectKey::String("a".into()),
262 a1_id,
263 )])));
264
265 let a2_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
267 "x",
268 ))));
269 let b2_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
270 "y",
271 ))));
272 let rec2_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([
273 (ObjectKey::String("a".into()), a2_id),
274 (ObjectKey::String("b".into()), b2_id),
275 ])));
276
277 let arr_id = doc.create_node(NodeValue::Array(NodeArray::from_vec(vec![
279 rec1_id, rec2_id,
280 ])));
281
282 let ty = synth(&doc, arr_id);
283
284 let expected = SynthType::Array(Box::new(SynthType::Union(SynthUnion {
286 variants: vec![
287 SynthType::Record(SynthRecord::new([(
288 "a".to_string(),
289 SynthField::required(SynthType::Integer),
290 )])),
291 SynthType::Record(SynthRecord::new([
292 (
293 "a".to_string(),
294 SynthField::required(SynthType::Text(Some("plaintext".to_string()))),
295 ),
296 (
297 "b".to_string(),
298 SynthField::required(SynthType::Text(Some("plaintext".to_string()))),
299 ),
300 ])),
301 ],
302 })));
303 assert_eq!(ty, expected);
304 }
305
306 #[test]
307 fn test_synth_nested() {
308 let doc = eure!({
309 items = [1, 2]
310 meta {
311 count = 2
312 }
313 });
314 let ty = synth(&doc, doc.get_root_id());
315 let expected = SynthType::Record(SynthRecord::new([
316 (
317 "items".to_string(),
318 SynthField::required(SynthType::Array(Box::new(SynthType::Integer))),
319 ),
320 (
321 "meta".to_string(),
322 SynthField::required(SynthType::Record(SynthRecord::new([(
323 "count".to_string(),
324 SynthField::required(SynthType::Integer),
325 )]))),
326 ),
327 ]));
328 assert_eq!(ty, expected);
329 }
330
331 #[test]
332 fn test_synth_hole_absorbed() {
333 let mut doc = EureDocument::new_empty();
335 let i1 = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
336 1,
337 ))));
338 let hole = doc.create_node(NodeValue::Hole(None));
339 let i3 = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
340 3,
341 ))));
342 let arr_id = doc.create_node(NodeValue::Array(NodeArray::from_vec(vec![i1, hole, i3])));
343
344 assert_eq!(
345 synth(&doc, arr_id),
346 SynthType::Array(Box::new(SynthType::Integer))
347 );
348 }
349
350 #[test]
351 fn test_synth_same_shape_records_merge() {
352 let mut doc = EureDocument::new_empty();
354
355 let a1_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
356 1,
357 ))));
358 let rec1_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([(
359 ObjectKey::String("a".into()),
360 a1_id,
361 )])));
362
363 let a2_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
364 "x",
365 ))));
366 let rec2_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([(
367 ObjectKey::String("a".into()),
368 a2_id,
369 )])));
370
371 let arr_id = doc.create_node(NodeValue::Array(NodeArray::from_vec(vec![
372 rec1_id, rec2_id,
373 ])));
374
375 let expected = SynthType::Array(Box::new(SynthType::Record(SynthRecord::new([(
377 "a".to_string(),
378 SynthField::required(SynthType::Union(SynthUnion {
379 variants: vec![
380 SynthType::Integer,
381 SynthType::Text(Some("plaintext".to_string())),
382 ],
383 })),
384 )]))));
385 assert_eq!(synth(&doc, arr_id), expected);
386 }
387}