200 lines
4.5 KiB
Rust
200 lines
4.5 KiB
Rust
|
use elements::*;
|
||
|
use headline::*;
|
||
|
use objects::*;
|
||
|
|
||
|
#[cfg_attr(test, derive(PartialEq))]
|
||
|
#[derive(Copy, Clone, Debug)]
|
||
|
pub enum Container {
|
||
|
Block,
|
||
|
Bold,
|
||
|
Drawer,
|
||
|
Headline { beg: usize, end: usize },
|
||
|
Italic,
|
||
|
LatexEnv,
|
||
|
List,
|
||
|
Paragraph,
|
||
|
Section { end: usize },
|
||
|
StrikeThrough,
|
||
|
Table,
|
||
|
Underline,
|
||
|
}
|
||
|
|
||
|
#[cfg_attr(test, derive(PartialEq, Debug))]
|
||
|
pub enum Event<'a> {
|
||
|
StartHeadline(Headline<'a>),
|
||
|
EndHeadline,
|
||
|
|
||
|
StartSection,
|
||
|
EndSection,
|
||
|
|
||
|
Paragraph,
|
||
|
BlockStart,
|
||
|
BlockEnd,
|
||
|
DynBlockStart,
|
||
|
DynBlockEnd,
|
||
|
ListStart,
|
||
|
ListEnd,
|
||
|
ParagraphStart,
|
||
|
ParagraphEnd,
|
||
|
|
||
|
AffKeywords,
|
||
|
|
||
|
Call,
|
||
|
|
||
|
Clock,
|
||
|
|
||
|
Comment,
|
||
|
|
||
|
TableStart,
|
||
|
TableEnd,
|
||
|
TableCell,
|
||
|
|
||
|
LatexEnv,
|
||
|
StrikeThrough,
|
||
|
FnDef(FnDef<'a>),
|
||
|
Keyword(Keyword<'a>),
|
||
|
Rule,
|
||
|
Cookie(Cookie<'a>),
|
||
|
FnRef(FnRef<'a>),
|
||
|
InlineCall(InlineCall<'a>),
|
||
|
InlineSrc(InlineSrc<'a>),
|
||
|
Link(Link<'a>),
|
||
|
Macros(Macros<'a>),
|
||
|
RadioTarget(RadioTarget<'a>),
|
||
|
Snippet(Snippet<'a>),
|
||
|
Target(Target<'a>),
|
||
|
Bold(&'a str),
|
||
|
Verbatim(&'a str),
|
||
|
Italic(&'a str),
|
||
|
Strike(&'a str),
|
||
|
Underline(&'a str),
|
||
|
Code(&'a str),
|
||
|
|
||
|
Text(&'a str),
|
||
|
}
|
||
|
|
||
|
pub struct Parser<'a> {
|
||
|
text: &'a str,
|
||
|
stack: Vec<Container>,
|
||
|
off: usize,
|
||
|
}
|
||
|
|
||
|
impl<'a> Parser<'a> {
|
||
|
pub fn new(text: &'a str) -> Parser<'a> {
|
||
|
Parser {
|
||
|
text,
|
||
|
stack: Vec::new(),
|
||
|
off: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn start_section_or_headline(&mut self, tail: &'a str) -> Event<'a> {
|
||
|
let end = Headline::find_level(tail, std::usize::MAX);
|
||
|
if end != 0 {
|
||
|
self.stack.push(Container::Section {
|
||
|
end: self.off + end,
|
||
|
});
|
||
|
Event::StartSection
|
||
|
} else {
|
||
|
self.start_headline(tail)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn end_section(&mut self) -> Event<'a> {
|
||
|
self.stack.pop();
|
||
|
Event::EndSection
|
||
|
}
|
||
|
|
||
|
fn start_headline(&mut self, tail: &'a str) -> Event<'a> {
|
||
|
let (hdl, off, end) = Headline::parse(tail);
|
||
|
self.stack.push(Container::Headline {
|
||
|
beg: self.off + off,
|
||
|
end: self.off + end,
|
||
|
});
|
||
|
self.off += off;
|
||
|
Event::StartHeadline(hdl)
|
||
|
}
|
||
|
|
||
|
fn end_headline(&mut self) -> Event<'a> {
|
||
|
self.stack.pop();
|
||
|
Event::EndHeadline
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl<'a> Iterator for Parser<'a> {
|
||
|
type Item = Event<'a>;
|
||
|
|
||
|
fn next(&mut self) -> Option<Event<'a>> {
|
||
|
let tail = &self.text[self.off..];
|
||
|
|
||
|
if self.stack.is_empty() {
|
||
|
if self.off >= self.text.len() {
|
||
|
None
|
||
|
} else {
|
||
|
Some(self.start_section_or_headline(tail))
|
||
|
}
|
||
|
} else {
|
||
|
let last = *self.stack.last_mut()?;
|
||
|
|
||
|
Some(match last {
|
||
|
Container::Headline { beg, end } => {
|
||
|
if self.off >= end {
|
||
|
self.end_headline()
|
||
|
} else if self.off == beg {
|
||
|
self.start_section_or_headline(tail)
|
||
|
} else {
|
||
|
self.start_headline(tail)
|
||
|
}
|
||
|
}
|
||
|
Container::Section { end } => {
|
||
|
if self.off >= end {
|
||
|
self.end_section()
|
||
|
} else {
|
||
|
match Element::find_elem(&self.text[self.off..end]) {
|
||
|
(Element::Paragraph(_), off) => {
|
||
|
self.off += off;
|
||
|
Event::Paragraph
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
_ => unimplemented!(),
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn parse() {
|
||
|
use self::Event::*;
|
||
|
|
||
|
let expected = vec![
|
||
|
StartHeadline(Headline::new(1, None, None, "Title 1", None)),
|
||
|
StartSection,
|
||
|
Paragraph,
|
||
|
EndSection,
|
||
|
StartHeadline(Headline::new(2, None, None, "Title 2", None)),
|
||
|
StartSection,
|
||
|
Paragraph,
|
||
|
EndSection,
|
||
|
EndHeadline,
|
||
|
EndHeadline,
|
||
|
StartHeadline(Headline::new(1, None, None, "Title 3", None)),
|
||
|
StartSection,
|
||
|
Paragraph,
|
||
|
EndSection,
|
||
|
EndHeadline,
|
||
|
StartHeadline(Headline::new(1, None, None, "Title 4 ", None)),
|
||
|
StartSection,
|
||
|
Paragraph,
|
||
|
EndSection,
|
||
|
EndHeadline,
|
||
|
];
|
||
|
|
||
|
assert_eq!(
|
||
|
Parser::new("* Title 1\nSection 1\n** Title 2\nSection 2\n* Title 3\nSection 3\n* Title 4 \nSection 4")
|
||
|
.collect::<Vec<_>>(),
|
||
|
expected
|
||
|
);
|
||
|
}
|