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:
parent
4a7967812e
commit
c7b3aa560b
3 changed files with 39 additions and 31 deletions
|
@ -418,7 +418,7 @@ mod test {
|
|||
macro_rules! test_parse {
|
||||
($src:expr $(,$($event:expr),* $(,)?)?) => {
|
||||
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),*,)?];
|
||||
assert_eq!(actual, expected, "\n\n{}\n\n", $src);
|
||||
};
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -265,10 +265,12 @@ impl<'s> Container<'s> {
|
|||
|
||||
pub struct Parser<'s> {
|
||||
src: &'s str,
|
||||
tree: block::Tree,
|
||||
tree: tree::Branch<block::Node, block::Atom>,
|
||||
inlines: span::InlineSpans<'s>,
|
||||
inline_parser: Option<inline::Parser<span::InlineCharsIter<'s>>>,
|
||||
link_definitions: std::collections::HashMap<&'s str, String>,
|
||||
|
||||
_tree_data: block::Tree,
|
||||
}
|
||||
|
||||
impl<'s> Parser<'s> {
|
||||
|
@ -277,27 +279,30 @@ impl<'s> Parser<'s> {
|
|||
let tree = block::parse(src);
|
||||
|
||||
let link_definitions = {
|
||||
let mut tree = tree.clone(); // TODO avoid clone
|
||||
let mut branch = tree.root();
|
||||
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)) =
|
||||
e.kind
|
||||
{
|
||||
let tag = e.span.of(src);
|
||||
// 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
|
||||
};
|
||||
|
||||
let branch = tree.root();
|
||||
|
||||
Self {
|
||||
src,
|
||||
tree,
|
||||
tree: branch,
|
||||
inlines: span::InlineSpans::new(src),
|
||||
inline_parser: None,
|
||||
link_definitions,
|
||||
_tree_data: tree,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -423,12 +428,12 @@ impl<'s> Parser<'s> {
|
|||
if matches!(c, block::Node::Leaf(block::Leaf::LinkDefinition)) =>
|
||||
{
|
||||
// ignore link definitions
|
||||
self.tree.inlines().last();
|
||||
self.tree.take_inlines().last();
|
||||
continue;
|
||||
}
|
||||
tree::EventKind::Enter(c) => match c {
|
||||
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()));
|
||||
let container = Container::from_leaf_block(content, l);
|
||||
Event::Start(container, attributes)
|
||||
|
|
49
src/tree.rs
49
src/tree.rs
|
@ -14,12 +14,7 @@ pub struct Event<C, A> {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Tree<C, A> {
|
||||
nodes: Vec<Node<C, A>>,
|
||||
branch: Vec<NodeIndex>,
|
||||
head: Option<NodeIndex>,
|
||||
}
|
||||
pub struct Tree<C, A>(Box<[Node<C, A>]>);
|
||||
|
||||
#[derive(Clone)]
|
||||
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> {
|
||||
fn new(nodes: Vec<Node<C, A>>) -> Self {
|
||||
let head = nodes[NodeIndex::root().index()].next;
|
||||
Self {
|
||||
pub fn root(&self) -> Branch<C, A> {
|
||||
let head = self.0[NodeIndex::root().index()].next;
|
||||
// SAFETY: tree must outlive the branch
|
||||
let nodes = unsafe { std::mem::transmute::<&[Node<C, A>], &'static [Node<C, A>]>(&self.0) };
|
||||
Branch {
|
||||
nodes,
|
||||
branch: Vec::new(),
|
||||
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> + '_ {
|
||||
let mut head = self.head;
|
||||
#[derive(Clone)]
|
||||
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 || {
|
||||
head.take().map(|h| {
|
||||
let n = &self.nodes[h.index()];
|
||||
assert!(matches!(n.kind, NodeKind::Inline));
|
||||
head = n.next;
|
||||
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>;
|
||||
|
||||
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> {
|
||||
Tree::new(self.nodes)
|
||||
Tree(self.nodes.into_boxed_slice())
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
const INDENT: &str = " ";
|
||||
let mut level = 0;
|
||||
|
|
Loading…
Reference in a new issue