tamer: ir::xir::tree: Initial child element parsing

This modifies the tree parser to handle child elements.  It's mostly
proof-of-concept code; the next commit will clean it up a bit so that it's
largely self-documenting.
main
Mike Gerwitz 2021-09-15 11:18:17 -04:00
parent 51507ccdad
commit 366ecca8ea
1 changed files with 108 additions and 12 deletions

View File

@ -274,6 +274,24 @@ pub struct Attr<Ix: SymbolIndexSize> {
span: (Span, Span),
}
/// A [`Stack`] representing an element and its (optional) parent's stack.
///
/// Storing the parent of an [`Element`] allows it to be manipulated on the
/// [`Stack`] using the usual operations,
/// while maintaining the context needed to later add it as a child to
/// its parent once the element is completed.
///
/// This is used to represent a [`Stack::BuddingElement`].
/// This type exists because enum variants are not their own types,
/// but we want to nest _only_ element stacks,
/// not any type of stack.
#[derive(Debug, Eq, PartialEq)]
pub struct ElementStack<Ix: SymbolIndexSize>(
Element<Ix>,
/// Parent element stack, if any.
Option<Box<ElementStack<Ix>>>,
);
/// The state and typed stack of the XIR parser stack machine.
///
/// Since all possible states of the stack are known statically,
@ -299,7 +317,7 @@ pub enum Stack<Ix: SymbolIndexSize> {
///
/// (This is a tree IR,
/// so here's a plant pun).
BuddingElement(Element<Ix>),
BuddingElement(ElementStack<Ix>),
/// A standalone attribute is awaiting its value.
///
@ -377,16 +395,40 @@ impl<Ix: SymbolIndexSize> ParserState<Ix> {
pub fn parse_token(&mut self, tok: Token<Ix>) -> Result<Parsed<Ix>, Ix> {
match (tok, take(&mut self.stack)) {
(Token::Open(name, span), Stack::Empty) => {
self.stack = Stack::BuddingElement(Element {
name,
attrs: AttrList::new(),
children: vec![],
span: (span, span),
});
self.stack = Stack::BuddingElement(ElementStack(
Element {
name,
attrs: AttrList::new(),
children: vec![],
span: (span, span),
},
None,
));
Ok(Parsed::Incomplete)
}
(Token::Close(balance_name, span), Stack::BuddingElement(ele)) => {
(Token::Open(name, span), Stack::BuddingElement(pstack)) => {
self.stack = Stack::BuddingElement(ElementStack(
Element {
name,
attrs: AttrList::new(),
children: vec![],
span: (span, span),
},
// Set aside the current stack to be restored later on,
// after we're done parsing the child.
Some(Box::new(pstack)),
));
Ok(Parsed::Incomplete)
}
(
Token::Close(balance_name, span),
Stack::BuddingElement(ElementStack(ele, pstack)),
) => {
// Note that self-closing with children is syntactically
// invalid and is expected to never make it into a XIR
// stream to begin with, so we don't check for it.
if let Some(name) = balance_name {
if name != ele.name {
return Err(ParseError::UnbalancedTagName {
@ -396,10 +438,20 @@ impl<Ix: SymbolIndexSize> ParserState<Ix> {
}
}
Ok(Parsed::Object(Tree::Element(Element {
let tree = Tree::Element(Element {
span: (ele.span.0, span),
..ele
})))
});
match pstack {
Some(mut stack) => {
stack.0.children.push(tree);
self.stack = Stack::BuddingElement(*stack);
Ok(Parsed::Incomplete)
}
None => Ok(Parsed::Object(tree)),
}
}
(Token::AttrName(name, span), Stack::Empty) => {
@ -407,7 +459,10 @@ impl<Ix: SymbolIndexSize> ParserState<Ix> {
Ok(Parsed::Incomplete)
}
(Token::AttrName(name, span), Stack::BuddingElement(ele)) => {
(
Token::AttrName(name, span),
Stack::BuddingElement(ElementStack(ele, _)),
) => {
self.stack = Stack::EleAttrName(ele, name, span);
Ok(Parsed::Incomplete)
}
@ -421,7 +476,7 @@ impl<Ix: SymbolIndexSize> ParserState<Ix> {
value,
span: (sname, span),
});
self.stack = Stack::BuddingElement(ele);
self.stack = Stack::BuddingElement(ElementStack(ele, None));
Ok(Parsed::Incomplete)
}
@ -722,6 +777,47 @@ mod test {
assert_eq!(sut.next(), None);
}
#[test]
fn element_with_empty_sibling_children() {
let parent = "parent".unwrap_into();
let childa = "childa".unwrap_into();
let childb = "childb".unwrap_into();
let toks = std::array::IntoIter::new([
Token::<Ix>::Open(parent, *S),
Token::<Ix>::Open(childa, *S),
Token::<Ix>::Close(None, *S2),
Token::<Ix>::Open(childb, *S),
Token::<Ix>::Close(None, *S2),
Token::<Ix>::Close(Some(parent), *S2),
]);
let expected = Element {
name: parent,
attrs: AttrList::new(),
children: vec![
Tree::Element(Element {
name: childa,
attrs: AttrList::new(),
children: vec![],
span: (*S, *S2),
}),
Tree::Element(Element {
name: childb,
attrs: AttrList::new(),
children: vec![],
span: (*S, *S2),
}),
],
span: (*S, *S2),
};
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();