tree: add tree branch reference

allow multiple independent iterators for the same underlying tree

safety: not very good, but should work because original tree is kept in
Parser and branches are only used during its lifetime
This commit is contained in:
Noah Hellman 2023-01-18 21:33:55 +01:00
parent 4a7967812e
commit c7b3aa560b
3 changed files with 39 additions and 31 deletions

View file

@ -418,7 +418,7 @@ mod test {
macro_rules! test_parse { macro_rules! test_parse {
($src:expr $(,$($event:expr),* $(,)?)?) => { ($src:expr $(,$($event:expr),* $(,)?)?) => {
let t = super::TreeParser::new($src).parse(); let t = super::TreeParser::new($src).parse();
let actual = t.map(|ev| (ev.kind, ev.span.of($src))).collect::<Vec<_>>(); let actual = t.root().map(|ev| (ev.kind, ev.span.of($src))).collect::<Vec<_>>();
let expected = &[$($($event),*,)?]; let expected = &[$($($event),*,)?];
assert_eq!(actual, expected, "\n\n{}\n\n", $src); assert_eq!(actual, expected, "\n\n{}\n\n", $src);
}; };

View file

@ -265,10 +265,12 @@ impl<'s> Container<'s> {
pub struct Parser<'s> { pub struct Parser<'s> {
src: &'s str, src: &'s str,
tree: block::Tree, tree: tree::Branch<block::Node, block::Atom>,
inlines: span::InlineSpans<'s>, inlines: span::InlineSpans<'s>,
inline_parser: Option<inline::Parser<span::InlineCharsIter<'s>>>, inline_parser: Option<inline::Parser<span::InlineCharsIter<'s>>>,
link_definitions: std::collections::HashMap<&'s str, String>, link_definitions: std::collections::HashMap<&'s str, String>,
_tree_data: block::Tree,
} }
impl<'s> Parser<'s> { impl<'s> Parser<'s> {
@ -277,27 +279,30 @@ impl<'s> Parser<'s> {
let tree = block::parse(src); let tree = block::parse(src);
let link_definitions = { let link_definitions = {
let mut tree = tree.clone(); // TODO avoid clone let mut branch = tree.root();
let mut defs = std::collections::HashMap::new(); let mut defs = std::collections::HashMap::new();
while let Some(e) = tree.next() { while let Some(e) = branch.next() {
if let tree::EventKind::Enter(block::Node::Leaf(block::Leaf::LinkDefinition)) = if let tree::EventKind::Enter(block::Node::Leaf(block::Leaf::LinkDefinition)) =
e.kind e.kind
{ {
let tag = e.span.of(src); let tag = e.span.of(src);
// TODO borrow url string if single inline // TODO borrow url string if single inline
let url = tree.inlines().map(|sp| sp.of(src).trim()).collect(); let url = branch.take_inlines().map(|sp| sp.of(src).trim()).collect();
defs.insert(tag, url); defs.insert(tag, url);
} }
} }
defs defs
}; };
let branch = tree.root();
Self { Self {
src, src,
tree, tree: branch,
inlines: span::InlineSpans::new(src), inlines: span::InlineSpans::new(src),
inline_parser: None, inline_parser: None,
link_definitions, link_definitions,
_tree_data: tree,
} }
} }
} }
@ -423,12 +428,12 @@ impl<'s> Parser<'s> {
if matches!(c, block::Node::Leaf(block::Leaf::LinkDefinition)) => if matches!(c, block::Node::Leaf(block::Leaf::LinkDefinition)) =>
{ {
// ignore link definitions // ignore link definitions
self.tree.inlines().last(); self.tree.take_inlines().last();
continue; continue;
} }
tree::EventKind::Enter(c) => match c { tree::EventKind::Enter(c) => match c {
block::Node::Leaf(l) => { block::Node::Leaf(l) => {
self.inlines.set_spans(self.tree.inlines()); self.inlines.set_spans(self.tree.take_inlines());
self.inline_parser = Some(inline::Parser::new(self.inlines.chars())); self.inline_parser = Some(inline::Parser::new(self.inlines.chars()));
let container = Container::from_leaf_block(content, l); let container = Container::from_leaf_block(content, l);
Event::Start(container, attributes) Event::Start(container, attributes)

View file

@ -14,12 +14,7 @@ pub struct Event<C, A> {
pub span: Span, pub span: Span,
} }
#[derive(Clone)] pub struct Tree<C, A>(Box<[Node<C, A>]>);
pub struct Tree<C, A> {
nodes: Vec<Node<C, A>>,
branch: Vec<NodeIndex>,
head: Option<NodeIndex>,
}
#[derive(Clone)] #[derive(Clone)]
pub struct Inlines<'t, C, A> { pub struct Inlines<'t, C, A> {
@ -35,28 +30,34 @@ impl<'t, C, A> Iterator for Inlines<'t, C, A> {
} }
impl<C, A> Tree<C, A> { impl<C, A> Tree<C, A> {
fn new(nodes: Vec<Node<C, A>>) -> Self { pub fn root(&self) -> Branch<C, A> {
let head = nodes[NodeIndex::root().index()].next; let head = self.0[NodeIndex::root().index()].next;
Self { // SAFETY: tree must outlive the branch
let nodes = unsafe { std::mem::transmute::<&[Node<C, A>], &'static [Node<C, A>]>(&self.0) };
Branch {
nodes, nodes,
branch: Vec::new(), branch: Vec::new(),
head, head,
} }
} }
pub fn inlines(&self) -> Inlines<C, A> {
let start = self.nodes[self.head.unwrap().index()].next.unwrap().index();
let end = start + self.spans().count();
Inlines {
iter: self.nodes[start..end].iter(),
}
} }
pub fn spans(&self) -> impl Iterator<Item = Span> + '_ { #[derive(Clone)]
let mut head = self.head; pub struct Branch<C: 'static, A: 'static> {
nodes: &'static [Node<C, A>],
branch: Vec<NodeIndex>,
head: Option<NodeIndex>,
}
impl<C, A> Branch<C, A> {
/// Retrieve all inlines until the end of the current container. Panics if any upcoming node is
/// not an inline node.
pub fn take_inlines(&mut self) -> impl Iterator<Item = Span> + '_ {
let mut head = self.head.take();
std::iter::from_fn(move || { std::iter::from_fn(move || {
head.take().map(|h| { head.take().map(|h| {
let n = &self.nodes[h.index()]; let n = &self.nodes[h.index()];
assert!(matches!(n.kind, NodeKind::Inline));
head = n.next; head = n.next;
n.span n.span
}) })
@ -64,7 +65,7 @@ impl<C, A> Tree<C, A> {
} }
} }
impl<C: Clone, A: Clone> Iterator for Tree<C, A> { impl<C: Clone, A: Clone> Iterator for Branch<C, A> {
type Item = Event<C, A>; type Item = Event<C, A>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -188,7 +189,7 @@ impl<C: Clone, A: Clone> Builder<C, A> {
} }
pub(super) fn finish(self) -> Tree<C, A> { pub(super) fn finish(self) -> Tree<C, A> {
Tree::new(self.nodes) Tree(self.nodes.into_boxed_slice())
} }
fn add_node(&mut self, node: Node<C, A>) { fn add_node(&mut self, node: Node<C, A>) {
@ -220,13 +221,15 @@ impl<C: Clone, A: Clone> Builder<C, A> {
} }
} }
impl<C: std::fmt::Debug + Clone, A: std::fmt::Debug + Clone> std::fmt::Debug for Builder<C, A> { impl<C: std::fmt::Debug + Clone + 'static, A: std::fmt::Debug + Clone + 'static> std::fmt::Debug
for Builder<C, A>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.clone().finish().fmt(f) self.clone().finish().root().fmt(f)
} }
} }
impl<C: std::fmt::Debug + Clone, A: std::fmt::Debug + Clone> std::fmt::Debug for Tree<C, A> { impl<C: std::fmt::Debug + Clone, A: std::fmt::Debug + Clone> std::fmt::Debug for Branch<C, A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const INDENT: &str = " "; const INDENT: &str = " ";
let mut level = 0; let mut level = 0;