mod block; mod html; mod inline; mod lex; mod span; mod tree; use span::Span; pub struct Block; const EOF: char = '\0'; #[derive(Debug, PartialEq, Eq)] pub enum ListType { Unordered, Ordered, } #[derive(Debug, PartialEq, Eq)] pub enum TagKind<'s> { Paragraph, Heading { level: u8 }, Table, TableRow, TableCell, RawBlock { format: &'s str }, CodeBlock { language: &'s str }, Blockquote, Div, UnorderedList, OrderedList { start: usize }, ListItem, DescriptionList, DescriptionItem, Footnote { tag: &'s str }, } #[derive(Debug, PartialEq, Eq)] pub enum Event2<'s> { Start(TagKind<'s>), End(TagKind<'s>), Blankline, } #[derive(Debug, PartialEq, Eq)] pub enum Event { Start(block::Block), End, Inline(inline::Event), Blankline, } pub struct Parser<'s> { src: &'s str, tree: block::Tree, parser: Option>, inline_start: usize, } impl<'s> Parser<'s> { #[must_use] pub fn new(src: &'s str) -> Self { Self { src, tree: block::parse(src), parser: None, inline_start: 0, } } } impl<'s> Iterator for Parser<'s> { type Item = Event; fn next(&mut self) -> Option { while let Some(parser) = &mut self.parser { // inside leaf block, with inline content if let Some(mut inline) = parser.next() { inline.span = inline.span.translate(self.inline_start); return Some(Event::Inline(inline)); } else if let Some(ev) = self.tree.next() { match ev.kind { tree::EventKind::Element(atom) => { assert_eq!(atom, block::Atom::Inline); parser.parse(ev.span.of(self.src)); self.inline_start = ev.span.start(); } tree::EventKind::Exit => { self.parser = None; return Some(Event::End); } tree::EventKind::Enter(..) => unreachable!(), } } } self.tree.next().map(|ev| match ev.kind { tree::EventKind::Element(atom) => { assert_eq!(atom, block::Atom::Blankline); Event::Blankline } tree::EventKind::Enter(block) => { if matches!(block, block::Block::Leaf(..)) { self.parser = Some(inline::Parser::new()); } Event::Start(block) } tree::EventKind::Exit => Event::End, }) } } #[cfg(test)] mod test { use super::Event::*; use crate::block::Block::*; use crate::block::Container::*; use crate::block::Leaf::*; use crate::inline::Atom::*; use crate::inline::EventKind::*; use crate::inline::Node::*; macro_rules! test_parse { ($($st:ident,)? $src:expr $(,$($token:expr),* $(,)?)?) => { #[allow(unused)] let actual = super::Parser::new($src).collect::>(); let expected = &[$($($token),*,)?]; assert_eq!(actual, expected, "\n\n{}\n\n", $src); }; } #[test] fn para() { test_parse!( "para", Start(Leaf(Paragraph)), Inline(Node(Str).span(0, 4)), End ); test_parse!( "pa ra", Start(Leaf(Paragraph)), Inline(Node(Str).span(0, 9)), End ); test_parse!( "para0\n\npara1", Start(Leaf(Paragraph)), Inline(Node(Str).span(0, 6)), End, Blankline, Start(Leaf(Paragraph)), Inline(Node(Str).span(7, 12)), End, ); } }