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 {
|
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);
|
||||||
};
|
};
|
||||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -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)
|
||||||
|
|
49
src/tree.rs
49
src/tree.rs
|
@ -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> {
|
#[derive(Clone)]
|
||||||
let start = self.nodes[self.head.unwrap().index()].next.unwrap().index();
|
pub struct Branch<C: 'static, A: 'static> {
|
||||||
let end = start + self.spans().count();
|
nodes: &'static [Node<C, A>],
|
||||||
Inlines {
|
branch: Vec<NodeIndex>,
|
||||||
iter: self.nodes[start..end].iter(),
|
head: Option<NodeIndex>,
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spans(&self) -> impl Iterator<Item = Span> + '_ {
|
impl<C, A> Branch<C, A> {
|
||||||
let mut head = self.head;
|
/// 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;
|
||||||
|
|
Loading…
Reference in a new issue