diff --git a/tamer/src/ir/xir/tree.rs b/tamer/src/ir/xir/tree.rs index da1a9e79..dbf2978a 100644 --- a/tamer/src/ir/xir/tree.rs +++ b/tamer/src/ir/xir/tree.rs @@ -393,7 +393,7 @@ pub enum Stack { /// An attribute is awaiting its value, /// after which it will be attached to an element. - EleAttrName(Element, QName, Span), + EleAttrName(ElementStack, QName, Span), } impl Default for Stack { @@ -476,10 +476,8 @@ impl Stack { /// value via [`Stack::close_attr`]. fn open_attr(self, name: QName, span: Span) -> Result { Ok(match self { - // TODO: this drops the parent stack! - Self::BuddingElement(ElementStack { element, pstack }) => { - assert!(pstack.is_none(), "TODO: child element attributes"); - Self::EleAttrName(element, name, span) + Self::BuddingElement(ele_stack) => { + Self::EleAttrName(ele_stack, name, span) } _ => todo! {}, @@ -490,18 +488,15 @@ impl Stack { /// element. fn close_attr(self, value: AttrValue, span: Span) -> Result { Ok(match self { - Self::EleAttrName(mut element, name, open_span) => { - element.attrs.push(Attr { + Self::EleAttrName(mut ele_stack, name, open_span) => { + // TODO: Demeter + ele_stack.element.attrs.push(Attr { name, value, span: (open_span, span), }); - // TODO: ...see (parent stack lost)! - Stack::BuddingElement(ElementStack { - element, - pstack: None, - }) + Stack::BuddingElement(ele_stack) } _ => todo! {}, }) @@ -737,6 +732,8 @@ mod test { Span::from_byte_interval((0, 0), "test case, 1".intern()); static ref S2: Span = Span::from_byte_interval((0, 0), "test case, 2".intern()); + static ref S3: Span = + Span::from_byte_interval((0, 0), "test case, 3".intern()); } mod tree { @@ -928,6 +925,45 @@ mod test { assert_eq!(sut.next(), None); } + // Ensures that attributes do not cause the parent context to be lost. + #[test] + fn element_with_child_with_attributes() { + let parent = "parent".unwrap_into(); + let child = "child".unwrap_into(); + let attr = "attr".unwrap_into(); + let value = AttrValue::Escaped("attr value".into()); + + let toks = std::array::IntoIter::new([ + Token::::Open(parent, *S), + Token::::Open(child, *S), + Token::::AttrName(attr, *S), + Token::::AttrValue(value, *S2), + Token::::Close(None, *S3), + Token::::Close(Some(parent), *S3), + ]); + + let expected = Element { + name: parent, + attrs: AttrList::new(), + children: vec![Tree::Element(Element { + name: child, + attrs: AttrList::from([Attr { + name: attr, + value, + span: (*S, *S2), + }]), + children: vec![], + span: (*S, *S3), + })], + span: (*S, *S3), + }; + + let mut sut = parser_from(toks); + + assert_eq!(sut.next(), Some(Ok(Tree::Element(expected)))); + assert_eq!(sut.next(), None); + } + #[test] fn parser_from_filters_incomplete() { let name = ("ns", "elem").unwrap_into();