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 }
100}
101
102fn synth_primitive(prim: &PrimitiveValue) -> SynthType {
104 match prim {
105 PrimitiveValue::Null => SynthType::Null,
106 PrimitiveValue::Bool(_) => SynthType::Boolean,
107 PrimitiveValue::Integer(_) => SynthType::Integer,
108 PrimitiveValue::F32(_) | PrimitiveValue::F64(_) => SynthType::Float,
109 PrimitiveValue::Text(text) => SynthType::Text(synth_text_language(&text.language)),
110 }
111}
112
113fn synth_text_language(lang: &Language) -> Option<String> {
115 match lang {
116 Language::Implicit => None,
117 Language::Plaintext => Some("plaintext".to_string()),
118 Language::Other(lang) => Some(lang.to_string()),
119 }
120}
121
122fn object_key_to_field_name(key: &ObjectKey) -> String {
127 match key {
128 ObjectKey::String(s) => s.clone(),
129 ObjectKey::Number(n) => n.to_string(),
130 ObjectKey::Tuple(t) => format!("{:?}", t), }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use eure_document::document::node::{NodeArray, NodeMap};
138 use eure_document::eure;
139 use eure_document::text::Text;
140 use eure_document::value::ObjectKey;
141 use num_bigint::BigInt;
142
143 #[test]
144 fn test_synth_primitives() {
145 let doc = EureDocument::new_primitive(PrimitiveValue::Null);
146 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Null);
147
148 let doc = EureDocument::new_primitive(PrimitiveValue::Bool(true));
149 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Boolean);
150
151 let doc = EureDocument::new_primitive(PrimitiveValue::Integer(BigInt::from(42)));
152 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Integer);
153
154 let doc = EureDocument::new_primitive(PrimitiveValue::F64(2.5));
155 assert_eq!(synth(&doc, doc.get_root_id()), SynthType::Float);
156
157 let doc = EureDocument::new_primitive(PrimitiveValue::Text(Text::plaintext("hello")));
158 assert_eq!(
159 synth(&doc, doc.get_root_id()),
160 SynthType::Text(Some("plaintext".to_string()))
161 );
162 }
163
164 #[test]
165 fn test_synth_empty_array() {
166 let doc = eure!({ arr = [] });
167 let root = doc.node(doc.get_root_id());
168 let arr_id = root
169 .as_map()
170 .unwrap()
171 .get_node_id(&ObjectKey::String("arr".into()))
172 .unwrap();
173 assert_eq!(
174 synth(&doc, arr_id),
175 SynthType::Array(Box::new(SynthType::Any))
176 );
177 }
178
179 #[test]
180 fn test_synth_homogeneous_array() {
181 let doc = eure!({ arr = [1, 2, 3] });
182 let root = doc.node(doc.get_root_id());
183 let arr_id = root
184 .as_map()
185 .unwrap()
186 .get_node_id(&ObjectKey::String("arr".into()))
187 .unwrap();
188 assert_eq!(
189 synth(&doc, arr_id),
190 SynthType::Array(Box::new(SynthType::Integer))
191 );
192 }
193
194 #[test]
195 fn test_synth_heterogeneous_array() {
196 let doc = eure!({ arr = [1, "hello"] });
197 let root = doc.node(doc.get_root_id());
198 let arr_id = root
199 .as_map()
200 .unwrap()
201 .get_node_id(&ObjectKey::String("arr".into()))
202 .unwrap();
203 assert_eq!(
204 synth(&doc, arr_id),
205 SynthType::Array(Box::new(SynthType::Union(SynthUnion {
206 variants: vec![
207 SynthType::Integer,
208 SynthType::Text(Some("plaintext".to_string()))
209 ]
210 })))
211 );
212 }
213
214 #[test]
215 fn test_synth_tuple() {
216 let doc = eure!({ tup = (1, "hello", true) });
217 let root = doc.node(doc.get_root_id());
218 let tup_id = root
219 .as_map()
220 .unwrap()
221 .get_node_id(&ObjectKey::String("tup".into()))
222 .unwrap();
223 assert_eq!(
224 synth(&doc, tup_id),
225 SynthType::Tuple(vec![
226 SynthType::Integer,
227 SynthType::Text(Some("plaintext".to_string())),
228 SynthType::Boolean,
229 ])
230 );
231 }
232
233 #[test]
234 fn test_synth_record() {
235 let doc = eure!({
236 name = "Alice"
237 age = 30
238 });
239 let ty = synth(&doc, doc.get_root_id());
240 let expected = SynthType::Record(SynthRecord::new([
241 (
242 "name".to_string(),
243 SynthField::required(SynthType::Text(Some("plaintext".to_string()))),
244 ),
245 ("age".to_string(), SynthField::required(SynthType::Integer)),
246 ]));
247 assert_eq!(ty, expected);
248 }
249
250 #[test]
251 fn test_synth_array_of_records_union() {
252 let mut doc = EureDocument::new_empty();
254
255 let a1_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
257 1,
258 ))));
259 let rec1_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([(
260 ObjectKey::String("a".into()),
261 a1_id,
262 )])));
263
264 let a2_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
266 "x",
267 ))));
268 let b2_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
269 "y",
270 ))));
271 let rec2_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([
272 (ObjectKey::String("a".into()), a2_id),
273 (ObjectKey::String("b".into()), b2_id),
274 ])));
275
276 let arr_id = doc.create_node(NodeValue::Array(NodeArray::from_vec(vec![
278 rec1_id, rec2_id,
279 ])));
280
281 let ty = synth(&doc, arr_id);
282
283 let expected = SynthType::Array(Box::new(SynthType::Union(SynthUnion {
285 variants: vec![
286 SynthType::Record(SynthRecord::new([(
287 "a".to_string(),
288 SynthField::required(SynthType::Integer),
289 )])),
290 SynthType::Record(SynthRecord::new([
291 (
292 "a".to_string(),
293 SynthField::required(SynthType::Text(Some("plaintext".to_string()))),
294 ),
295 (
296 "b".to_string(),
297 SynthField::required(SynthType::Text(Some("plaintext".to_string()))),
298 ),
299 ])),
300 ],
301 })));
302 assert_eq!(ty, expected);
303 }
304
305 #[test]
306 fn test_synth_nested() {
307 let doc = eure!({
308 items = [1, 2]
309 meta {
310 count = 2
311 }
312 });
313 let ty = synth(&doc, doc.get_root_id());
314 let expected = SynthType::Record(SynthRecord::new([
315 (
316 "items".to_string(),
317 SynthField::required(SynthType::Array(Box::new(SynthType::Integer))),
318 ),
319 (
320 "meta".to_string(),
321 SynthField::required(SynthType::Record(SynthRecord::new([(
322 "count".to_string(),
323 SynthField::required(SynthType::Integer),
324 )]))),
325 ),
326 ]));
327 assert_eq!(ty, expected);
328 }
329
330 #[test]
331 fn test_synth_hole_absorbed() {
332 let mut doc = EureDocument::new_empty();
334 let i1 = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
335 1,
336 ))));
337 let hole = doc.create_node(NodeValue::Hole(None));
338 let i3 = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
339 3,
340 ))));
341 let arr_id = doc.create_node(NodeValue::Array(NodeArray::from_vec(vec![i1, hole, i3])));
342
343 assert_eq!(
344 synth(&doc, arr_id),
345 SynthType::Array(Box::new(SynthType::Integer))
346 );
347 }
348
349 #[test]
350 fn test_synth_same_shape_records_merge() {
351 let mut doc = EureDocument::new_empty();
353
354 let a1_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(
355 1,
356 ))));
357 let rec1_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([(
358 ObjectKey::String("a".into()),
359 a1_id,
360 )])));
361
362 let a2_id = doc.create_node(NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
363 "x",
364 ))));
365 let rec2_id = doc.create_node(NodeValue::Map(NodeMap::from_iter([(
366 ObjectKey::String("a".into()),
367 a2_id,
368 )])));
369
370 let arr_id = doc.create_node(NodeValue::Array(NodeArray::from_vec(vec![
371 rec1_id, rec2_id,
372 ])));
373
374 let expected = SynthType::Array(Box::new(SynthType::Record(SynthRecord::new([(
376 "a".to_string(),
377 SynthField::required(SynthType::Union(SynthUnion {
378 variants: vec![
379 SynthType::Integer,
380 SynthType::Text(Some("plaintext".to_string())),
381 ],
382 })),
383 )]))));
384 assert_eq!(synth(&doc, arr_id), expected);
385 }
386}