1use alloc::collections::BTreeMap;
2use indexmap::IndexSet;
3
4use crate::document::interpreter_sink::InterpreterSink;
5use crate::map::PartialNodeMap;
6use crate::prelude_internal::*;
7use crate::value::PartialObjectKey;
8
9#[derive(Debug, Default, Clone)]
14struct BlockScope {
15 last_pushes: BTreeMap<NodeId, NodeId>,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Scope {
23 id: usize,
24 stack_depth: usize,
25 path_depth: usize,
26}
27
28#[derive(Debug, PartialEq, thiserror::Error, Clone)]
29pub enum ScopeError {
30 #[error("Cannot end scope at root")]
31 CannotEndAtRoot,
32 #[error("Scope must be ended in LIFO order (most recent first)")]
33 NotMostRecentScope,
34}
35
36pub struct DocumentConstructor {
37 document: EureDocument,
38 path: Vec<PathSegment>,
40 stack: Vec<NodeId>,
42 scope_counter: usize,
44 outstanding_scopes: Vec<usize>,
46 hole_bound: Vec<bool>,
48 unbound_nodes: IndexSet<NodeId>,
50 block_scope_stack: Vec<BlockScope>,
54}
55
56impl Default for DocumentConstructor {
57 fn default() -> Self {
58 let document = EureDocument::default();
59 let root = document.get_root_id();
60 Self {
61 document,
62 path: vec![],
63 stack: vec![root],
64 hole_bound: vec![false],
65 scope_counter: 0,
66 outstanding_scopes: vec![],
67 unbound_nodes: IndexSet::new(),
68 block_scope_stack: vec![BlockScope::default()],
69 }
70 }
71}
72
73impl DocumentConstructor {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn current_node_id(&self) -> NodeId {
79 *self.stack.last().expect("Stack should never be empty")
80 }
81
82 pub fn current_node(&self) -> &Node {
83 self.document.node(self.current_node_id())
84 }
85
86 pub fn current_node_mut(&mut self) -> &mut Node {
87 self.document.node_mut(self.current_node_id())
88 }
89
90 pub fn current_path(&self) -> &[PathSegment] {
91 &self.path
92 }
93
94 pub fn document(&self) -> &EureDocument {
95 &self.document
96 }
97
98 pub fn document_mut(&mut self) -> &mut EureDocument {
99 &mut self.document
100 }
101
102 pub fn finish(mut self) -> EureDocument {
103 for node_id in self.unbound_nodes {
104 let node = self.document.node_mut(node_id);
105 if node.content.is_hole() {
106 node.content = NodeValue::Map(Default::default());
107 }
108 }
109 let root_id = self.document.get_root_id();
111 let root_node = self.document.node_mut(root_id);
112 if root_node.content.is_hole() && !self.hole_bound[0] {
113 root_node.content = NodeValue::Map(Default::default());
114 }
115 self.document
116 }
117}
118
119impl DocumentConstructor {
120 pub fn begin_scope(&mut self) -> Scope {
123 let id = self.scope_counter;
124 self.scope_counter += 1;
125 self.outstanding_scopes.push(id);
126 Scope {
127 id,
128 stack_depth: self.stack.len(),
129 path_depth: self.path.len(),
130 }
131 }
132
133 pub fn end_scope(&mut self, scope: Scope) -> Result<(), ScopeError> {
136 if self.outstanding_scopes.last() != Some(&scope.id) {
138 return Err(ScopeError::NotMostRecentScope);
139 }
140 if scope.stack_depth < 1 {
141 return Err(ScopeError::CannotEndAtRoot);
142 }
143 self.outstanding_scopes.pop();
144 for i in scope.stack_depth..self.stack.len() {
145 let hole_bound = self.hole_bound[i];
146 if !hole_bound && self.document.node(self.stack[i]).content.is_hole() {
147 self.unbound_nodes.insert(self.stack[i]);
148 }
149 }
150 self.stack.truncate(scope.stack_depth);
151 self.hole_bound.truncate(scope.stack_depth);
152 self.path.truncate(scope.path_depth);
153 Ok(())
154 }
155
156 pub fn navigate(&mut self, segment: PathSegment) -> Result<NodeId, InsertError> {
168 let current = self.current_node_id();
169
170 if let PathSegment::ArrayIndex(ArrayIndexKind::Current) = &segment {
172 self.document
174 .node(current)
175 .as_array()
176 .ok_or_else(|| InsertError {
177 kind: InsertErrorKind::ExpectedArray,
178 path: EurePath::from_iter(self.path.iter().cloned()),
179 })?;
180 let child_id = self
181 .block_scope_stack
182 .last()
183 .expect("block scope stack is never empty")
184 .last_pushes
185 .get(¤t)
186 .copied()
187 .ok_or_else(|| InsertError {
188 kind: InsertErrorKind::ArrayCurrentOutOfScope {
189 array_node_id: current,
190 },
191 path: EurePath::from_iter(self.path.iter().cloned()),
192 })?;
193 self.stack.push(child_id);
194 self.hole_bound.push(false);
195 self.path.push(segment);
196 return Ok(child_id);
197 }
198
199 let node_mut = self
200 .document
201 .resolve_child_by_segment(segment.clone(), current)
202 .map_err(|e| InsertError {
203 kind: e,
204 path: EurePath::from_iter(self.path.iter().cloned()),
205 })?;
206 let node_id = node_mut.node_id;
207
208 if let PathSegment::ArrayIndex(ArrayIndexKind::Push) = &segment {
210 self.block_scope_stack
211 .last_mut()
212 .expect("block scope stack is never empty")
213 .last_pushes
214 .insert(current, node_id);
215 }
216
217 self.stack.push(node_id);
218 self.hole_bound.push(false);
219 self.path.push(segment);
220 Ok(node_id)
221 }
222
223 pub fn navigate_partial_map_entry(
229 &mut self,
230 key: PartialObjectKey,
231 ) -> Result<NodeId, InsertError> {
232 let current = self.current_node_id();
233 let existing = self
234 .document
235 .node(current)
236 .as_partial_map()
237 .and_then(|pm| pm.find(&key))
238 .copied();
239
240 let node_id = if let Some(node_id) = existing {
241 node_id
242 } else {
243 self.document
244 .add_partial_map_child(key.clone(), current)
245 .map_err(|kind| InsertError {
246 kind,
247 path: EurePath::from_iter(self.path.iter().cloned()),
248 })?
249 .node_id
250 };
251
252 let segment = PathSegment::from_partial_object_key(key);
253
254 self.stack.push(node_id);
255 self.hole_bound.push(false);
256 self.path.push(segment);
257 Ok(node_id)
258 }
259
260 pub fn require_hole(&self) -> Result<(), InsertError> {
263 let node = self.current_node();
264 if !node.content.is_hole() {
265 return Err(InsertError {
266 kind: InsertErrorKind::BindingTargetHasValue,
267 path: EurePath::from_iter(self.path.iter().cloned()),
268 });
269 }
270 Ok(())
271 }
272
273 pub fn bind_hole(&mut self, label: Option<Identifier>) -> Result<(), InsertError> {
275 if !self.current_node().content.is_hole() {
276 return Err(InsertError {
277 kind: InsertErrorKind::BindingTargetHasValue,
278 path: EurePath::from_iter(self.current_path().iter().cloned()),
279 });
280 }
281 self.hole_bound[self.stack.len() - 1] = true;
282 self.unbound_nodes.swap_remove(&self.current_node_id());
283 self.current_node_mut().content = NodeValue::Hole(label);
284 Ok(())
285 }
286
287 pub fn bind_primitive(&mut self, value: PrimitiveValue) -> Result<(), InsertError> {
289 let node = self.current_node_mut();
290 if !node.content.is_hole() {
291 return Err(InsertError {
292 kind: InsertErrorKind::BindingTargetHasValue,
293 path: EurePath::from_iter(self.current_path().iter().cloned()),
294 });
295 }
296 node.content = NodeValue::Primitive(value);
297 Ok(())
298 }
299
300 pub fn bind_from(&mut self, value: impl Into<PrimitiveValue>) -> Result<(), InsertError> {
305 self.bind_primitive(value.into())
306 }
307
308 pub fn bind_empty_map(&mut self) -> Result<(), InsertError> {
310 let node = self.current_node_mut();
311 if !node.content.is_hole() {
312 return Err(InsertError {
313 kind: InsertErrorKind::BindingTargetHasValue,
314 path: EurePath::from_iter(self.current_path().iter().cloned()),
315 });
316 }
317 node.content = NodeValue::Map(Default::default());
318 Ok(())
319 }
320
321 pub fn bind_empty_partial_map(&mut self) -> Result<(), InsertError> {
323 let node = self.current_node_mut();
324 if !node.content.is_hole() {
325 return Err(InsertError {
326 kind: InsertErrorKind::BindingTargetHasValue,
327 path: EurePath::from_iter(self.current_path().iter().cloned()),
328 });
329 }
330 node.content = NodeValue::PartialMap(PartialNodeMap::new());
331 Ok(())
332 }
333
334 pub fn bind_empty_array(&mut self) -> Result<(), InsertError> {
336 let node = self.current_node_mut();
337 if !node.content.is_hole() {
338 return Err(InsertError {
339 kind: InsertErrorKind::BindingTargetHasValue,
340 path: EurePath::from_iter(self.current_path().iter().cloned()),
341 });
342 }
343 node.content = NodeValue::Array(Default::default());
344 Ok(())
345 }
346
347 pub fn bind_empty_tuple(&mut self) -> Result<(), InsertError> {
349 let node = self.current_node_mut();
350 if !node.content.is_hole() {
351 return Err(InsertError {
352 kind: InsertErrorKind::BindingTargetHasValue,
353 path: EurePath::from_iter(self.current_path().iter().cloned()),
354 });
355 }
356 node.content = NodeValue::Tuple(Default::default());
357 Ok(())
358 }
359
360 fn begin_block_scope(&mut self) {
362 self.block_scope_stack.push(BlockScope::default());
363 }
364
365 fn end_block_scope(&mut self) {
368 debug_assert!(
369 self.block_scope_stack.len() > 1,
370 "attempted to pop the root block scope"
371 );
372 if self.block_scope_stack.len() > 1 {
373 self.block_scope_stack.pop();
374 }
375 }
376
377 pub fn begin_eure_block(&mut self) {
386 self.begin_block_scope();
387 }
388
389 pub fn set_block_value(&mut self) -> Result<(), InsertError> {
391 Ok(())
392 }
393
394 pub fn end_eure_block(&mut self) -> Result<(), InsertError> {
396 self.end_block_scope();
397 Ok(())
398 }
399
400 pub fn begin_binding(&mut self) {}
402
403 pub fn end_binding_value(&mut self) -> Result<(), InsertError> {
405 Ok(())
406 }
407
408 pub fn end_binding_block(&mut self) -> Result<(), InsertError> {
410 Ok(())
411 }
412
413 pub fn begin_section(&mut self) {}
415
416 pub fn begin_section_items(&mut self) {
418 self.begin_block_scope();
419 }
420
421 pub fn end_section_items(&mut self) -> Result<(), InsertError> {
423 self.end_block_scope();
424 Ok(())
425 }
426
427 pub fn end_section_block(&mut self) -> Result<(), InsertError> {
429 Ok(())
430 }
431}
432
433impl InterpreterSink for DocumentConstructor {
434 type Error = InsertError;
435 type Scope = Scope;
436
437 fn begin_scope(&mut self) -> Self::Scope {
438 DocumentConstructor::begin_scope(self)
439 }
440
441 fn end_scope(&mut self, scope: Self::Scope) -> Result<(), Self::Error> {
442 DocumentConstructor::end_scope(self, scope).map_err(|e| InsertError {
443 kind: InsertErrorKind::ScopeError(e),
444 path: EurePath::from_iter(self.current_path().iter().cloned()),
445 })
446 }
447
448 fn navigate(&mut self, segment: PathSegment) -> Result<NodeId, Self::Error> {
449 DocumentConstructor::navigate(self, segment)
450 }
451
452 fn require_hole(&self) -> Result<(), Self::Error> {
453 DocumentConstructor::require_hole(self)
454 }
455
456 fn bind_primitive(&mut self, value: PrimitiveValue) -> Result<(), Self::Error> {
457 DocumentConstructor::bind_primitive(self, value)
458 }
459
460 fn bind_hole(&mut self, label: Option<Identifier>) -> Result<(), Self::Error> {
461 DocumentConstructor::bind_hole(self, label)
462 }
463
464 fn bind_empty_map(&mut self) -> Result<(), Self::Error> {
465 DocumentConstructor::bind_empty_map(self)
466 }
467
468 fn bind_empty_array(&mut self) -> Result<(), Self::Error> {
469 DocumentConstructor::bind_empty_array(self)
470 }
471
472 fn bind_empty_tuple(&mut self) -> Result<(), Self::Error> {
473 DocumentConstructor::bind_empty_tuple(self)
474 }
475
476 fn current_node_id(&self) -> NodeId {
477 DocumentConstructor::current_node_id(self)
478 }
479
480 fn current_path(&self) -> &[PathSegment] {
481 DocumentConstructor::current_path(self)
482 }
483
484 fn document(&self) -> &EureDocument {
485 DocumentConstructor::document(self)
486 }
487
488 fn document_mut(&mut self) -> &mut EureDocument {
489 DocumentConstructor::document_mut(self)
490 }
491
492 fn begin_eure_block(&mut self) {
493 DocumentConstructor::begin_eure_block(self)
494 }
495
496 fn end_eure_block(&mut self) -> Result<(), Self::Error> {
497 DocumentConstructor::end_eure_block(self)
498 }
499
500 fn begin_section_items(&mut self) {
501 DocumentConstructor::begin_section_items(self)
502 }
503
504 fn end_section_items(&mut self) -> Result<(), Self::Error> {
505 DocumentConstructor::end_section_items(self)
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512 use crate::identifier::IdentifierParser;
513 use crate::value::{PartialObjectKey, Tuple};
514
515 fn create_identifier(s: &str) -> Identifier {
516 let parser = IdentifierParser::init();
517 parser.parse(s).unwrap()
518 }
519
520 #[test]
521 fn test_new_initializes_at_root() {
522 let constructor = DocumentConstructor::new();
523 let root_id = constructor.document().get_root_id();
524
525 assert_eq!(constructor.current_node_id(), root_id);
526 assert_eq!(constructor.current_path(), &[]);
527 }
528
529 #[test]
530 fn test_current_node_returns_root_initially() {
531 let constructor = DocumentConstructor::new();
532
533 let node = constructor.current_node();
534 assert!(node.content.is_hole());
535 }
536
537 #[test]
538 fn test_navigate_single_ident() {
539 let mut constructor = DocumentConstructor::new();
540
541 let identifier = create_identifier("field");
542 let segment = PathSegment::Ident(identifier.clone());
543
544 let node_id = constructor
545 .navigate(segment.clone())
546 .expect("Failed to navigate");
547
548 assert_eq!(constructor.current_node_id(), node_id);
549 assert_eq!(constructor.current_path(), &[segment]);
550 }
551
552 #[test]
553 fn test_navigate_multiple_times() {
554 let mut constructor = DocumentConstructor::new();
555
556 let id1 = create_identifier("field1");
557 let id2 = create_identifier("field2");
558
559 constructor
560 .navigate(PathSegment::Ident(id1.clone()))
561 .expect("Failed to navigate first");
562
563 let node_id2 = constructor
564 .navigate(PathSegment::Extension(id2.clone()))
565 .expect("Failed to navigate second");
566
567 assert_eq!(constructor.current_node_id(), node_id2);
568 assert_eq!(
569 constructor.current_path(),
570 &[PathSegment::Ident(id1), PathSegment::Extension(id2)]
571 );
572 }
573
574 #[test]
575 fn test_navigate_error_propagates() {
576 let mut constructor = DocumentConstructor::new();
578 let identifier = create_identifier("field");
580 constructor
581 .navigate(PathSegment::Ident(identifier))
582 .expect("Failed to navigate");
583 let node_id = constructor.current_node_id();
585 constructor.document_mut().node_mut(node_id).content =
586 NodeValue::Primitive(PrimitiveValue::Null);
587
588 let result = constructor.navigate(PathSegment::TupleIndex(0));
589
590 assert_eq!(
591 result.map_err(|e| e.kind),
592 Err(InsertErrorKind::ExpectedTuple)
593 );
594 }
595
596 #[test]
597 fn test_scope_success() {
598 let mut constructor = DocumentConstructor::new();
599 let root_id = constructor.document().get_root_id();
600
601 let identifier = create_identifier("field");
602 let token = constructor.begin_scope();
603 let _node_id = constructor
604 .navigate(PathSegment::Ident(identifier.clone()))
605 .expect("Failed to navigate");
606
607 let result = constructor.end_scope(token);
609 assert_eq!(result, Ok(()));
610
611 assert_eq!(constructor.current_node_id(), root_id);
613 assert_eq!(constructor.current_path(), &[]);
614 }
615
616 #[test]
617 fn test_scope_lifo_enforcement() {
618 let mut constructor = DocumentConstructor::new();
619
620 let id1 = create_identifier("field1");
621 let id2 = create_identifier("field2");
622
623 let token1 = constructor.begin_scope();
624 constructor
625 .navigate(PathSegment::Ident(id1))
626 .expect("Failed to navigate");
627
628 let token2 = constructor.begin_scope();
629 constructor
630 .navigate(PathSegment::Extension(id2))
631 .expect("Failed to navigate");
632
633 let result = constructor.end_scope(token1);
635 assert_eq!(result, Err(ScopeError::NotMostRecentScope));
636
637 constructor
639 .end_scope(token2)
640 .expect("Failed to end scope 2");
641 constructor
642 .end_scope(token1)
643 .expect("Failed to end scope 1");
644 }
645
646 #[test]
647 fn test_scope_with_multiple_navigations() {
648 let mut constructor = DocumentConstructor::new();
649 let root_id = constructor.document().get_root_id();
650
651 let id1 = create_identifier("level1");
652 let id2 = create_identifier("level2");
653 let id3 = create_identifier("level3");
654
655 let token = constructor.begin_scope();
656
657 let node_id1 = constructor
659 .navigate(PathSegment::Ident(id1.clone()))
660 .expect("Failed to navigate level1");
661
662 let node_id2 = constructor
663 .navigate(PathSegment::Extension(id2.clone()))
664 .expect("Failed to navigate level2");
665
666 let node_id3 = constructor
667 .navigate(PathSegment::Extension(id3.clone()))
668 .expect("Failed to navigate level3");
669
670 assert_eq!(constructor.current_node_id(), node_id3);
672 assert_eq!(
673 constructor.current_path(),
674 &[
675 PathSegment::Ident(id1.clone()),
676 PathSegment::Extension(id2.clone()),
677 PathSegment::Extension(id3)
678 ]
679 );
680
681 constructor.end_scope(token).expect("Failed to end scope");
683 assert_eq!(constructor.current_node_id(), root_id);
684 assert_eq!(constructor.current_path(), &[]);
685
686 let _ = constructor.document().node(node_id1);
688 let _ = constructor.document().node(node_id2);
689 let _ = constructor.document().node(node_id3);
690 }
691
692 #[test]
693 fn test_nested_scopes() {
694 let mut constructor = DocumentConstructor::new();
695 let root_id = constructor.document().get_root_id();
696
697 let id1 = create_identifier("a");
698 let id2 = create_identifier("b");
699 let id3 = create_identifier("c");
700
701 let token_outer = constructor.begin_scope();
703 let node_a = constructor
704 .navigate(PathSegment::Ident(id1.clone()))
705 .expect("Failed to navigate a");
706
707 let token_inner = constructor.begin_scope();
709 let _node_b = constructor
710 .navigate(PathSegment::Extension(id2.clone()))
711 .expect("Failed to navigate b");
712 let _node_c = constructor
713 .navigate(PathSegment::Extension(id3.clone()))
714 .expect("Failed to navigate c");
715
716 constructor
718 .end_scope(token_inner)
719 .expect("Failed to end inner scope");
720 assert_eq!(constructor.current_node_id(), node_a);
721 assert_eq!(constructor.current_path(), &[PathSegment::Ident(id1)]);
722
723 constructor
725 .end_scope(token_outer)
726 .expect("Failed to end outer scope");
727 assert_eq!(constructor.current_node_id(), root_id);
728 assert_eq!(constructor.current_path(), &[]);
729 }
730
731 #[test]
732 fn test_require_hole_success() {
733 let mut constructor = DocumentConstructor::new();
734
735 let identifier = create_identifier("field");
736 constructor
737 .navigate(PathSegment::Ident(identifier))
738 .expect("Failed to navigate");
739
740 let result = constructor.require_hole();
742 assert_eq!(result, Ok(()));
743 }
744
745 #[test]
746 fn test_require_hole_fails_when_bound() {
747 let mut constructor = DocumentConstructor::new();
748
749 let identifier = create_identifier("field");
750 let node_id = constructor
751 .navigate(PathSegment::Ident(identifier))
752 .expect("Failed to navigate");
753
754 constructor.document_mut().node_mut(node_id).content =
756 NodeValue::Primitive(PrimitiveValue::Bool(true));
757
758 let result = constructor.require_hole();
760 assert_eq!(
761 result.unwrap_err().kind,
762 InsertErrorKind::BindingTargetHasValue
763 );
764 }
765
766 #[test]
767 fn test_bind_primitive_success() {
768 let mut constructor = DocumentConstructor::new();
769 let identifier = create_identifier("field");
770
771 let node_id = constructor
773 .navigate(PathSegment::Ident(identifier))
774 .expect("Failed to navigate");
775
776 let result = constructor.bind_primitive(PrimitiveValue::Bool(true));
778 assert_eq!(result, Ok(()));
779
780 let node = constructor.document().node(node_id);
782 assert!(matches!(
783 node.content,
784 NodeValue::Primitive(PrimitiveValue::Bool(true))
785 ));
786 }
787
788 #[test]
789 fn test_bind_primitive_already_bound() {
790 let mut constructor = DocumentConstructor::new();
791 let identifier = create_identifier("field");
792
793 let node_id = constructor
795 .navigate(PathSegment::Ident(identifier.clone()))
796 .expect("Failed to navigate");
797
798 constructor.document_mut().node_mut(node_id).content =
800 NodeValue::Primitive(PrimitiveValue::Null);
801
802 let result = constructor.bind_primitive(PrimitiveValue::Bool(false));
804
805 assert_eq!(
806 result.unwrap_err().kind,
807 InsertErrorKind::BindingTargetHasValue
808 );
809
810 let node = constructor.document().node(node_id);
812 assert!(matches!(
813 node.content,
814 NodeValue::Primitive(PrimitiveValue::Null)
815 ));
816 }
817
818 #[test]
819 fn test_finish_replaces_uninitialized_root_with_null() {
820 let constructor = DocumentConstructor::new();
821
822 let root_id = constructor.document().get_root_id();
824 assert!(constructor.document().node(root_id).content.is_hole());
825
826 let document = constructor.finish();
828 let root_node = document.node(document.get_root_id());
829 assert_eq!(root_node.content, NodeValue::Map(Default::default()));
830 }
831
832 #[test]
833 fn test_finish_preserves_initialized_root() {
834 let mut constructor = DocumentConstructor::new();
835
836 constructor
838 .bind_primitive(PrimitiveValue::Bool(true))
839 .expect("Failed to bind");
840
841 let document = constructor.finish();
843 let root_node = document.node(document.get_root_id());
844 assert!(matches!(
845 root_node.content,
846 NodeValue::Primitive(PrimitiveValue::Bool(true))
847 ));
848 }
849
850 #[test]
851 fn test_finish_preserves_partial_map_root() {
852 let mut constructor = DocumentConstructor::new();
853
854 constructor
855 .navigate_partial_map_entry(PartialObjectKey::Hole(Some(create_identifier("x"))))
856 .unwrap();
857 constructor
858 .bind_primitive(PrimitiveValue::Integer(1.into()))
859 .unwrap();
860
861 let document = constructor.finish();
862 assert!(matches!(
863 document.node(document.get_root_id()).content,
864 NodeValue::PartialMap(_)
865 ));
866 }
867
868 #[test]
869 fn test_navigate_partial_map_entry_does_not_reuse_tuple_with_anonymous_hole() {
870 let mut constructor = DocumentConstructor::new();
871 let scope = constructor.begin_scope();
872
873 let key = PartialObjectKey::Tuple(Tuple(vec![
874 PartialObjectKey::Number(1.into()),
875 PartialObjectKey::Hole(None),
876 ]));
877
878 let first = constructor.navigate_partial_map_entry(key.clone()).unwrap();
879 constructor.end_scope(scope).unwrap();
880
881 let second_scope = constructor.begin_scope();
882 let second = constructor.navigate_partial_map_entry(key).unwrap();
883
884 assert_ne!(first, second);
885 constructor.end_scope(second_scope).unwrap();
886 }
887
888 #[test]
889 fn test_navigate_reuses_labeled_hole_key_segment() {
890 let mut constructor = DocumentConstructor::new();
891 let label = create_identifier("x");
892
893 let scope = constructor.begin_scope();
894 let first = constructor
895 .navigate(PathSegment::HoleKey(Some(label.clone())))
896 .unwrap();
897 constructor.end_scope(scope).unwrap();
898
899 let scope = constructor.begin_scope();
900 let second = constructor
901 .navigate(PathSegment::HoleKey(Some(label)))
902 .unwrap();
903
904 assert_eq!(first, second);
905 constructor.end_scope(scope).unwrap();
906 }
907
908 #[test]
909 fn test_typical_binding_pattern() {
910 let mut constructor = DocumentConstructor::new();
912
913 let id_a = create_identifier("a");
914 let id_b = create_identifier("b");
915 let id_c = create_identifier("c");
916
917 let token = constructor.begin_scope();
918 constructor
919 .navigate(PathSegment::Ident(id_a.clone()))
920 .unwrap();
921 constructor
922 .navigate(PathSegment::Extension(id_b.clone()))
923 .unwrap();
924 let node_c = constructor
925 .navigate(PathSegment::Extension(id_c.clone()))
926 .unwrap();
927 constructor.require_hole().unwrap();
928 constructor
929 .bind_primitive(PrimitiveValue::Bool(true))
930 .unwrap();
931 constructor.end_scope(token).unwrap();
932
933 let node = constructor.document().node(node_c);
935 assert!(matches!(
936 node.content,
937 NodeValue::Primitive(PrimitiveValue::Bool(true))
938 ));
939 }
940
941 #[test]
955 fn test_array_current_merges_with_preceding_push_root_scope() {
956 let mut constructor = DocumentConstructor::new();
958 let users = create_identifier("users");
959 let x = create_identifier("x");
960 let y = create_identifier("y");
961
962 let t1 = constructor.begin_scope();
964 constructor
965 .navigate(PathSegment::Ident(users.clone()))
966 .unwrap();
967 let pushed = constructor
968 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
969 .unwrap();
970 let x_node = constructor.navigate(PathSegment::Ident(x.clone())).unwrap();
971 constructor.require_hole().unwrap();
972 constructor
973 .bind_primitive(PrimitiveValue::Integer(1.into()))
974 .unwrap();
975 constructor.end_scope(t1).unwrap();
976
977 let t2 = constructor.begin_scope();
979 constructor
980 .navigate(PathSegment::Ident(users.clone()))
981 .unwrap();
982 let current = constructor
983 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
984 .unwrap();
985 let y_node = constructor.navigate(PathSegment::Ident(y.clone())).unwrap();
986 constructor.require_hole().unwrap();
987 constructor
988 .bind_primitive(PrimitiveValue::Integer(2.into()))
989 .unwrap();
990 constructor.end_scope(t2).unwrap();
991
992 assert_eq!(pushed, current, "[^] must resolve to the last [] push");
993
994 let element = constructor.document().node(pushed);
995 let map = element.as_map().expect("element should be a map");
996 let resolved_x = map
997 .get_node_id(&ObjectKey::String(x.to_string()))
998 .expect("x should exist");
999 let resolved_y = map
1000 .get_node_id(&ObjectKey::String(y.to_string()))
1001 .expect("y should exist");
1002 assert_eq!(resolved_x, x_node);
1003 assert_eq!(resolved_y, y_node);
1004 }
1005
1006 #[test]
1007 fn test_array_current_merges_inside_block_scope() {
1008 let mut constructor = DocumentConstructor::new();
1010 let posts = create_identifier("posts");
1011 let title = create_identifier("title");
1012 let body = create_identifier("body");
1013
1014 constructor.begin_eure_block();
1017
1018 let t1 = constructor.begin_scope();
1019 constructor
1020 .navigate(PathSegment::Ident(posts.clone()))
1021 .unwrap();
1022 let pushed = constructor
1023 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1024 .unwrap();
1025 constructor
1026 .navigate(PathSegment::Ident(title.clone()))
1027 .unwrap();
1028 constructor.require_hole().unwrap();
1029 constructor
1030 .bind_primitive(PrimitiveValue::Text(Text::plaintext("a")))
1031 .unwrap();
1032 constructor.end_scope(t1).unwrap();
1033
1034 let t2 = constructor.begin_scope();
1035 constructor
1036 .navigate(PathSegment::Ident(posts.clone()))
1037 .unwrap();
1038 let current = constructor
1039 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1040 .unwrap();
1041 constructor
1042 .navigate(PathSegment::Ident(body.clone()))
1043 .unwrap();
1044 constructor.require_hole().unwrap();
1045 constructor
1046 .bind_primitive(PrimitiveValue::Text(Text::plaintext("b")))
1047 .unwrap();
1048 constructor.end_scope(t2).unwrap();
1049
1050 constructor.end_eure_block().unwrap();
1051
1052 assert_eq!(pushed, current);
1053 let map = constructor.document().node(pushed).as_map().unwrap();
1054 assert!(
1055 map.get_node_id(&ObjectKey::String(title.to_string()))
1056 .is_some()
1057 );
1058 assert!(
1059 map.get_node_id(&ObjectKey::String(body.to_string()))
1060 .is_some()
1061 );
1062 }
1063
1064 #[test]
1065 fn test_array_current_cannot_reach_across_block_scope() {
1066 let mut constructor = DocumentConstructor::new();
1069 let users = create_identifier("users");
1070 let x = create_identifier("x");
1071
1072 let t1 = constructor.begin_scope();
1074 constructor
1075 .navigate(PathSegment::Ident(users.clone()))
1076 .unwrap();
1077 constructor
1078 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1079 .unwrap();
1080 constructor.navigate(PathSegment::Ident(x.clone())).unwrap();
1081 constructor.require_hole().unwrap();
1082 constructor
1083 .bind_primitive(PrimitiveValue::Integer(1.into()))
1084 .unwrap();
1085 constructor.end_scope(t1).unwrap();
1086
1087 constructor.begin_eure_block();
1089 let t2 = constructor.begin_scope();
1090 constructor
1091 .navigate(PathSegment::Ident(users.clone()))
1092 .unwrap();
1093 let err = constructor
1094 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1095 .unwrap_err();
1096 assert!(matches!(
1097 err.kind,
1098 InsertErrorKind::ArrayCurrentOutOfScope { .. }
1099 ));
1100 constructor.end_scope(t2).unwrap();
1102 constructor.end_eure_block().unwrap();
1103 }
1104
1105 #[test]
1106 fn test_array_current_after_inner_block_refers_to_outer_last_push() {
1107 let mut constructor = DocumentConstructor::new();
1111 let users = create_identifier("users");
1112 let nested = create_identifier("nested");
1113 let x = create_identifier("x");
1114 let y = create_identifier("y");
1115
1116 let t1 = constructor.begin_scope();
1118 constructor
1119 .navigate(PathSegment::Ident(users.clone()))
1120 .unwrap();
1121 let first = constructor
1122 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1123 .unwrap();
1124 constructor.navigate(PathSegment::Ident(x.clone())).unwrap();
1125 constructor.require_hole().unwrap();
1126 constructor
1127 .bind_primitive(PrimitiveValue::Integer(1.into()))
1128 .unwrap();
1129 constructor.end_scope(t1).unwrap();
1130
1131 constructor.begin_eure_block();
1134 let inner_scope = constructor.begin_scope();
1135 constructor
1136 .navigate(PathSegment::Ident(nested.clone()))
1137 .unwrap();
1138 constructor
1139 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1140 .unwrap();
1141 constructor.require_hole().unwrap();
1142 constructor.bind_primitive(PrimitiveValue::Null).unwrap();
1143 constructor.end_scope(inner_scope).unwrap();
1144 constructor.end_eure_block().unwrap();
1145
1146 let t2 = constructor.begin_scope();
1148 constructor
1149 .navigate(PathSegment::Ident(users.clone()))
1150 .unwrap();
1151 let second = constructor
1152 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1153 .unwrap();
1154 constructor.navigate(PathSegment::Ident(x.clone())).unwrap();
1155 constructor.require_hole().unwrap();
1156 constructor
1157 .bind_primitive(PrimitiveValue::Integer(2.into()))
1158 .unwrap();
1159 constructor.end_scope(t2).unwrap();
1160
1161 let t3 = constructor.begin_scope();
1163 constructor
1164 .navigate(PathSegment::Ident(users.clone()))
1165 .unwrap();
1166 let current = constructor
1167 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1168 .unwrap();
1169 constructor.navigate(PathSegment::Ident(y.clone())).unwrap();
1170 constructor.require_hole().unwrap();
1171 constructor
1172 .bind_primitive(PrimitiveValue::Integer(3.into()))
1173 .unwrap();
1174 constructor.end_scope(t3).unwrap();
1175
1176 assert_ne!(first, second);
1177 assert_eq!(
1178 current, second,
1179 "[^] must refer to the second root-level push"
1180 );
1181 }
1182
1183 #[test]
1184 fn test_array_current_without_prior_push_errors() {
1185 let mut constructor = DocumentConstructor::new();
1189 let users = create_identifier("users");
1190 let x = create_identifier("x");
1191
1192 let t1 = constructor.begin_scope();
1195 constructor
1196 .navigate(PathSegment::Ident(users.clone()))
1197 .unwrap();
1198 constructor
1199 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Specific(0)))
1200 .unwrap();
1201 constructor.navigate(PathSegment::Ident(x.clone())).unwrap();
1202 constructor.require_hole().unwrap();
1203 constructor
1204 .bind_primitive(PrimitiveValue::Integer(1.into()))
1205 .unwrap();
1206 constructor.end_scope(t1).unwrap();
1207
1208 let t2 = constructor.begin_scope();
1210 constructor
1211 .navigate(PathSegment::Ident(users.clone()))
1212 .unwrap();
1213 let err = constructor
1214 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1215 .unwrap_err();
1216 assert!(
1217 matches!(err.kind, InsertErrorKind::ArrayCurrentOutOfScope { .. }),
1218 "expected ArrayCurrentOutOfScope, got {:?}",
1219 err.kind
1220 );
1221 constructor.end_scope(t2).unwrap();
1222 }
1223
1224 #[test]
1225 fn test_array_current_nested_arrays() {
1226 let mut constructor = DocumentConstructor::new();
1229 let orgs = create_identifier("orgs");
1230 let teams = create_identifier("teams");
1231 let members = create_identifier("members");
1232 let name = create_identifier("name");
1233 let age = create_identifier("age");
1234
1235 let t1 = constructor.begin_scope();
1236 constructor
1237 .navigate(PathSegment::Ident(orgs.clone()))
1238 .unwrap();
1239 let org = constructor
1240 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1241 .unwrap();
1242 constructor
1243 .navigate(PathSegment::Ident(teams.clone()))
1244 .unwrap();
1245 let team = constructor
1246 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1247 .unwrap();
1248 constructor
1249 .navigate(PathSegment::Ident(members.clone()))
1250 .unwrap();
1251 let member1 = constructor
1252 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1253 .unwrap();
1254 constructor
1255 .navigate(PathSegment::Ident(name.clone()))
1256 .unwrap();
1257 constructor.require_hole().unwrap();
1258 constructor
1259 .bind_primitive(PrimitiveValue::Text(Text::plaintext("Ada")))
1260 .unwrap();
1261 constructor.end_scope(t1).unwrap();
1262
1263 let t2 = constructor.begin_scope();
1264 constructor
1265 .navigate(PathSegment::Ident(orgs.clone()))
1266 .unwrap();
1267 let org_current = constructor
1268 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1269 .unwrap();
1270 constructor
1271 .navigate(PathSegment::Ident(teams.clone()))
1272 .unwrap();
1273 let team_current = constructor
1274 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1275 .unwrap();
1276 constructor
1277 .navigate(PathSegment::Ident(members.clone()))
1278 .unwrap();
1279 let member2 = constructor
1280 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1281 .unwrap();
1282 constructor
1283 .navigate(PathSegment::Ident(age.clone()))
1284 .unwrap();
1285 constructor.require_hole().unwrap();
1286 constructor
1287 .bind_primitive(PrimitiveValue::Integer(30.into()))
1288 .unwrap();
1289 constructor.end_scope(t2).unwrap();
1290
1291 assert_eq!(org_current, org);
1292 assert_eq!(team_current, team);
1293 assert_ne!(member1, member2, "the inner [] should create a new member");
1294
1295 let members_node = {
1297 let team_map = constructor.document().node(team).as_map().unwrap();
1298 team_map
1299 .get_node_id(&ObjectKey::String(members.to_string()))
1300 .expect("members array")
1301 };
1302 let arr = constructor
1303 .document()
1304 .node(members_node)
1305 .as_array()
1306 .unwrap();
1307 assert_eq!(arr.len(), 2);
1308 }
1309
1310 #[test]
1311 fn test_array_current_followed_by_push_into_child() {
1312 let mut constructor = DocumentConstructor::new();
1315 let users = create_identifier("users");
1316 let name = create_identifier("name");
1317 let posts = create_identifier("posts");
1318 let title = create_identifier("title");
1319
1320 let t1 = constructor.begin_scope();
1321 constructor
1322 .navigate(PathSegment::Ident(users.clone()))
1323 .unwrap();
1324 let user = constructor
1325 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1326 .unwrap();
1327 constructor
1328 .navigate(PathSegment::Ident(name.clone()))
1329 .unwrap();
1330 constructor.require_hole().unwrap();
1331 constructor
1332 .bind_primitive(PrimitiveValue::Text(Text::plaintext("Ada")))
1333 .unwrap();
1334 constructor.end_scope(t1).unwrap();
1335
1336 let t2 = constructor.begin_scope();
1337 constructor
1338 .navigate(PathSegment::Ident(users.clone()))
1339 .unwrap();
1340 let same_user = constructor
1341 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1342 .unwrap();
1343 constructor
1344 .navigate(PathSegment::Ident(posts.clone()))
1345 .unwrap();
1346 constructor
1347 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Push))
1348 .unwrap();
1349 constructor
1350 .navigate(PathSegment::Ident(title.clone()))
1351 .unwrap();
1352 constructor.require_hole().unwrap();
1353 constructor
1354 .bind_primitive(PrimitiveValue::Text(Text::plaintext("hello")))
1355 .unwrap();
1356 constructor.end_scope(t2).unwrap();
1357
1358 assert_eq!(same_user, user);
1359 }
1360
1361 #[test]
1362 fn test_array_current_on_non_array_errors_as_expected_array() {
1363 let mut constructor = DocumentConstructor::new();
1366 let items = create_identifier("items");
1367
1368 let t1 = constructor.begin_scope();
1369 constructor
1370 .navigate(PathSegment::Ident(items.clone()))
1371 .unwrap();
1372 constructor.require_hole().unwrap();
1373 constructor
1374 .bind_primitive(PrimitiveValue::Integer(42.into()))
1375 .unwrap();
1376 constructor.end_scope(t1).unwrap();
1377
1378 let t2 = constructor.begin_scope();
1379 constructor
1380 .navigate(PathSegment::Ident(items.clone()))
1381 .unwrap();
1382 let err = constructor
1383 .navigate(PathSegment::ArrayIndex(ArrayIndexKind::Current))
1384 .unwrap_err();
1385 assert!(
1386 matches!(err.kind, InsertErrorKind::ExpectedArray),
1387 "expected ExpectedArray, got {:?}",
1388 err.kind
1389 );
1390 constructor.end_scope(t2).unwrap();
1391 }
1392}