feat: block parsing

This commit is contained in:
PoiScript 2019-01-11 00:57:08 +08:00
parent 55e6d67fae
commit 79477b812e
5 changed files with 442 additions and 20 deletions

View file

@ -16,7 +16,7 @@
## Elements ## Elements
- [ ] Babel Call - [ ] Babel Call
- [ ] Blocks - [x] Blocks
- [ ] Clock, Diary Sexp and Planning - [ ] Clock, Diary Sexp and Planning
- [x] Comments - [x] Comments
- [ ] Fixed Width Areas - [ ] Fixed Width Areas

View file

@ -1,14 +1,24 @@
pub enum BlockStart { #[cfg_attr(test, derive(PartialEq, Debug))]
name: BlockName, pub struct Block;
impl Block {
pub fn parse(src: &str) -> Option<(usize, usize, usize, usize)> {
if src.len() < 17 || !src[0..8].eq_ignore_ascii_case("#+BEGIN_") {
return None;
}
let args = eol!(src);
let name = until_while!(src, 8, |c| c == b' ' || c == b'\n', |c: u8| c
.is_ascii_alphabetic());
// TODO: ignore case match
let content = src.find(&format!("\n#+END_{}", &src[8..name]))?;
let end = eol!(src, content + 1);
Some((name, args, content, end + 1))
}
} }
pub enum BlockName { #[test]
Center, fn parse() {
Comment, // TODO: testing
Example,
Export,
Quote,
Src,
Verbose,
Special
} }

View file

View file

@ -1,7 +1,9 @@
pub mod block;
pub mod fn_def; pub mod fn_def;
pub mod keyword; pub mod keyword;
pub mod rule; pub mod rule;
pub use self::block::Block;
pub use self::fn_def::FnDef; pub use self::fn_def::FnDef;
pub use self::keyword::Keyword; pub use self::keyword::Keyword;
pub use self::rule::Rule; pub use self::rule::Rule;
@ -17,6 +19,42 @@ pub enum Element<'a> {
Keyword(Keyword<'a>), Keyword(Keyword<'a>),
FnDef(FnDef<'a>), FnDef(FnDef<'a>),
CenterBlock {
args: Option<&'a str>,
content_end: usize,
end: usize,
},
QuoteBlock {
args: Option<&'a str>,
content_end: usize,
end: usize,
},
SpecialBlock {
args: Option<&'a str>,
name: &'a str,
content_end: usize,
end: usize,
},
CommentBlock {
content: &'a str,
args: Option<&'a str>,
},
ExampleBlock {
content: &'a str,
args: Option<&'a str>,
},
ExportBlock {
content: &'a str,
args: Option<&'a str>,
},
SrcBlock {
content: &'a str,
args: Option<&'a str>,
},
VerseBlock {
content: &'a str,
args: Option<&'a str>,
},
Rule, Rule,
Comment(&'a str), Comment(&'a str),
} }
@ -105,8 +143,305 @@ impl<'a> Element<'a> {
} }
if bytes[pos] == b'#' { if bytes[pos] == b'#' {
// Keyword
if bytes[pos + 1] == b'+' { if bytes[pos + 1] == b'+' {
if let Some((name, args, content, end)) = Block::parse(&src[pos..]) {
// TODO: use macros
return match &src[pos + 8..pos + name] {
block_name if block_name.eq_ignore_ascii_case("CENTER") => {
if pos == start {
(
pos + args,
Some(Element::CenterBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content_end: content + 1,
end,
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::CenterBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content_end: content + 1,
end,
},
pos + args,
)),
)
}
}
block_name if block_name.eq_ignore_ascii_case("QUOTE") => {
if pos == start {
(
pos + args,
Some(Element::QuoteBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args].trim())
},
content_end: content + 1,
end,
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::QuoteBlock {
args: if name == args {
None
} else {
Some(
&src[pos + name..pos + args].trim(),
)
},
content_end: content + 1,
end,
},
args + pos,
)),
)
}
}
block_name if block_name.eq_ignore_ascii_case("COMMENT") => {
if pos == start {
(
args,
Some(Element::CommentBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::CommentBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
},
args,
)),
)
}
}
block_name if block_name.eq_ignore_ascii_case("EXAMPLE") => {
if pos == start {
(
args,
Some(Element::ExampleBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::ExampleBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
},
args,
)),
)
}
}
block_name if block_name.eq_ignore_ascii_case("EXPORT") => {
if pos == start {
(
args,
Some(Element::ExportBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::ExportBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
},
args,
)),
)
}
}
block_name if block_name.eq_ignore_ascii_case("SRC") => {
if pos == start {
(
args,
Some(Element::SrcBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::SrcBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
},
args,
)),
)
}
}
block_name if block_name.eq_ignore_ascii_case("VERSE") => {
if pos == start {
(
args,
Some(Element::VerseBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::VerseBlock {
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args])
},
content: &src[pos + args..pos + content],
},
args,
)),
)
}
}
block_name => {
if pos == start {
(
pos + args,
Some(Element::SpecialBlock {
name: block_name,
args: if name == args {
None
} else {
Some(&src[pos + name..pos + args].trim())
},
content_end: content + 1,
end,
}),
None,
)
} else {
(
start,
Some(Element::Paragraph {
end: if pos == start { end } else { end - 1 },
trailing: pos - 1,
}),
Some((
Element::SpecialBlock {
name: block_name,
args: if name == args {
None
} else {
Some(
&src[pos + name..pos + args].trim(),
)
},
content_end: content + 1,
end,
},
pos + args,
)),
)
}
}
};
}
if let Some((kw, off)) = Keyword::parse(&src[pos..]) { if let Some((kw, off)) = Keyword::parse(&src[pos..]) {
return if pos == start { return if pos == start {
(off, Some(Element::Keyword(kw)), None) (off, Some(Element::Keyword(kw)), None)

View file

@ -9,8 +9,10 @@ pub enum Container {
Section { end: usize }, Section { end: usize },
Paragraph { end: usize, trailing: usize }, Paragraph { end: usize, trailing: usize },
CenterBlock { content_end: usize, end: usize },
QuoteBlock { content_end: usize, end: usize },
SpecialBlock { content_end: usize, end: usize },
Block,
Drawer, Drawer,
LatexEnv, LatexEnv,
List, List,
@ -33,8 +35,37 @@ pub enum Event<'a> {
StartParagraph, StartParagraph,
EndParagraph, EndParagraph,
BlockStart, StartCenterBlock,
BlockEnd, EndCenterBlock,
StartQuoteBlock,
EndQuoteBlock,
StartSpecialBlock {
name: &'a str,
args: Option<&'a str>,
},
EndSpecialBlock,
CommentBlock {
content: &'a str,
args: Option<&'a str>,
},
ExampleBlock {
content: &'a str,
args: Option<&'a str>,
},
ExportBlock {
content: &'a str,
args: Option<&'a str>,
},
SrcBlock {
content: &'a str,
args: Option<&'a str>,
},
VerseBlock {
content: &'a str,
args: Option<&'a str>,
},
DynBlockStart, DynBlockStart,
DynBlockEnd, DynBlockEnd,
ListStart, ListStart,
@ -136,11 +167,30 @@ impl<'a> Parser<'a> {
self.off += off; self.off += off;
if let Some(ele) = ele { if let Some(ele) = ele {
if let Element::Paragraph { end, trailing } = ele { match ele {
self.stack.push(Container::Paragraph { Element::Paragraph { end, trailing } => self.stack.push(Container::Paragraph {
end: end + self.off - off, end: end + self.off - off,
trailing: trailing + self.off - off, trailing: trailing + self.off - off,
}); }),
Element::QuoteBlock {
end, content_end, ..
} => self.stack.push(Container::QuoteBlock {
content_end: content_end + self.off - off,
end: end + self.off - off,
}),
Element::CenterBlock {
end, content_end, ..
} => self.stack.push(Container::CenterBlock {
content_end: content_end + self.off - off,
end: end + self.off - off,
}),
Element::SpecialBlock {
end, content_end, ..
} => self.stack.push(Container::SpecialBlock {
content_end: content_end + self.off - off,
end: end + self.off - off,
}),
_ => (),
} }
ele.into() ele.into()
} else { } else {
@ -187,6 +237,9 @@ impl<'a> Parser<'a> {
Container::Headline { .. } => Event::EndHeadline, Container::Headline { .. } => Event::EndHeadline,
Container::Italic { .. } => Event::EndItalic, Container::Italic { .. } => Event::EndItalic,
Container::Bold { .. } => Event::EndBold, Container::Bold { .. } => Event::EndBold,
Container::CenterBlock { .. } => Event::EndCenterBlock,
Container::QuoteBlock { .. } => Event::EndQuoteBlock,
Container::SpecialBlock { .. } => Event::EndSpecialBlock,
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
@ -205,7 +258,7 @@ impl<'a> Iterator for Parser<'a> {
Some(self.start_section_or_headline(tail)) Some(self.start_section_or_headline(tail))
} }
} else { } else {
let last = *self.stack.last_mut()?; let last = *self.stack.last_mut().unwrap();
Some(match last { Some(match last {
Container::Headline { beg, end } => { Container::Headline { beg, end } => {
@ -217,6 +270,22 @@ impl<'a> Iterator for Parser<'a> {
self.start_headline(tail) self.start_headline(tail)
} }
} }
Container::CenterBlock {
content_end, end, ..
}
| Container::QuoteBlock {
content_end, end, ..
}
| Container::SpecialBlock {
content_end, end, ..
} => {
if self.off >= content_end {
self.off = end;
self.end()
} else {
self.next_ele(content_end)
}
}
Container::Section { end } => { Container::Section { end } => {
if self.off >= end { if self.off >= end {
self.end() self.end()
@ -280,6 +349,14 @@ impl<'a> From<Element<'a>> for Event<'a> {
Element::Keyword(kw) => Event::Keyword(kw), Element::Keyword(kw) => Event::Keyword(kw),
Element::Paragraph { .. } => Event::StartParagraph, Element::Paragraph { .. } => Event::StartParagraph,
Element::Rule => Event::Rule, Element::Rule => Event::Rule,
Element::CenterBlock { .. } => Event::StartCenterBlock,
Element::QuoteBlock { .. } => Event::StartQuoteBlock,
Element::SpecialBlock { name, args, .. } => Event::StartSpecialBlock { name, args },
Element::CommentBlock { args, content } => Event::CommentBlock { args, content },
Element::ExampleBlock { args, content } => Event::ExampleBlock { args, content },
Element::ExportBlock { args, content } => Event::ExportBlock { args, content },
Element::SrcBlock { args, content } => Event::SrcBlock { args, content },
Element::VerseBlock { args, content } => Event::VerseBlock { args, content },
} }
} }
} }