2019-08-06 07:03:16 +01:00
|
|
|
// parser related functions
|
2019-08-04 10:46:10 +01:00
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
use std::borrow::Cow;
|
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
use indextree::{Arena, NodeId};
|
|
|
|
use jetscii::bytes;
|
2019-08-09 15:15:06 +01:00
|
|
|
use memchr::{memchr, memchr_iter};
|
2019-08-04 10:46:10 +01:00
|
|
|
use nom::{
|
2019-08-09 15:15:06 +01:00
|
|
|
bytes::complete::take_while1,
|
|
|
|
character::complete::{line_ending, not_line_ending},
|
|
|
|
combinator::{map, opt, recognize, verify},
|
2019-08-05 15:23:32 +01:00
|
|
|
error::ErrorKind,
|
2019-08-09 15:15:06 +01:00
|
|
|
error_position,
|
|
|
|
multi::{many0_count, many1_count},
|
|
|
|
sequence::terminated,
|
|
|
|
Err, IResult,
|
2019-08-04 10:46:10 +01:00
|
|
|
};
|
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
use crate::config::ParseConfig;
|
|
|
|
use crate::elements::*;
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub trait ElementArena<'a> {
|
|
|
|
fn push_element<T: Into<Element<'a>>>(&mut self, element: T, parent: NodeId) -> NodeId;
|
|
|
|
fn insert_before_last_child<T: Into<Element<'a>>>(
|
|
|
|
&mut self,
|
|
|
|
element: T,
|
|
|
|
parent: NodeId,
|
|
|
|
) -> NodeId;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> ElementArena<'a> for Arena<Element<'a>> {
|
|
|
|
fn push_element<T: Into<Element<'a>>>(&mut self, element: T, parent: NodeId) -> NodeId {
|
|
|
|
let node = self.new_node(element.into());
|
|
|
|
parent.append(node, self);
|
|
|
|
node
|
|
|
|
}
|
|
|
|
|
|
|
|
fn insert_before_last_child<T: Into<Element<'a>>>(
|
|
|
|
&mut self,
|
|
|
|
element: T,
|
|
|
|
parent: NodeId,
|
|
|
|
) -> NodeId {
|
|
|
|
if let Some(child) = self[parent].last_child() {
|
|
|
|
let node = self.new_node(element.into());
|
|
|
|
child.insert_before(node, self);
|
|
|
|
node
|
|
|
|
} else {
|
|
|
|
self.push_element(element, parent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2019-08-06 07:03:16 +01:00
|
|
|
pub enum Container<'a> {
|
|
|
|
// List
|
|
|
|
List {
|
|
|
|
content: &'a str,
|
|
|
|
node: NodeId,
|
|
|
|
indent: usize,
|
|
|
|
},
|
|
|
|
// Block, List Item
|
|
|
|
Block {
|
|
|
|
content: &'a str,
|
|
|
|
node: NodeId,
|
|
|
|
},
|
|
|
|
// Pargraph, Inline Markup
|
|
|
|
Inline {
|
|
|
|
content: &'a str,
|
|
|
|
node: NodeId,
|
|
|
|
},
|
|
|
|
// Headline
|
|
|
|
Headline {
|
|
|
|
content: &'a str,
|
|
|
|
node: NodeId,
|
|
|
|
},
|
|
|
|
// Document
|
|
|
|
Document {
|
|
|
|
content: &'a str,
|
|
|
|
node: NodeId,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_headline_content<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
config: &ParseConfig,
|
2019-08-09 15:15:06 +01:00
|
|
|
) {
|
2019-08-06 14:10:57 +01:00
|
|
|
let (tail, (title, content)) = Title::parse(content, config).unwrap();
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(title, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
parse_section_and_headlines(arena, tail, parent, containers);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_section_and_headlines<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
let content = skip_empty_lines(content);
|
|
|
|
if content.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut last_end = 0;
|
|
|
|
for i in memchr_iter(b'\n', content.as_bytes()) {
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Ok((mut tail, headline_content)) = parse_headline(&content[last_end..]) {
|
2019-08-06 07:03:16 +01:00
|
|
|
if last_end != 0 {
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(Element::Section, parent);
|
|
|
|
let content = &content[0..last_end];
|
|
|
|
containers.push(Container::Block { content, node });
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
|
|
|
|
let node = arena.push_element(Element::Headline, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Headline {
|
|
|
|
content: headline_content,
|
|
|
|
node,
|
|
|
|
});
|
2019-08-09 15:15:06 +01:00
|
|
|
|
|
|
|
while let Ok((new_tail, content)) = parse_headline(tail) {
|
|
|
|
debug_assert_ne!(tail, new_tail);
|
|
|
|
let node = arena.push_element(Element::Headline, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Headline { content, node });
|
|
|
|
tail = new_tail;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
last_end = i + 1;
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(Element::Section, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_blocks<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
let mut tail = skip_empty_lines(content);
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(new_tail) = parse_block(content, arena, parent, containers) {
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = skip_empty_lines(new_tail);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut text = tail;
|
|
|
|
let mut pos = 0;
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
macro_rules! insert_paragraph {
|
|
|
|
($content:expr) => {
|
|
|
|
let node = arena.insert_before_last_child(Element::Paragraph, parent);
|
|
|
|
containers.push(Container::Inline {
|
|
|
|
content: $content,
|
|
|
|
node,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
while !tail.is_empty() {
|
|
|
|
let i = memchr(b'\n', tail.as_bytes())
|
|
|
|
.map(|i| i + 1)
|
|
|
|
.unwrap_or_else(|| tail.len());
|
|
|
|
if tail.as_bytes()[0..i].iter().all(u8::is_ascii_whitespace) {
|
2019-08-09 15:15:06 +01:00
|
|
|
debug_assert_ne!(tail, skip_empty_lines(&tail[i..]));
|
|
|
|
insert_paragraph!(&text[0..pos].trim_end_matches('\n'));
|
|
|
|
pos = 0;
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = skip_empty_lines(&tail[i..]);
|
|
|
|
text = tail;
|
2019-08-09 15:15:06 +01:00
|
|
|
} else if let Some(new_tail) = parse_block(tail, arena, parent, containers) {
|
|
|
|
debug_assert_ne!(tail, new_tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
if pos != 0 {
|
2019-08-09 15:15:06 +01:00
|
|
|
insert_paragraph!(&text[0..pos].trim_end_matches('\n'));
|
2019-08-06 07:03:16 +01:00
|
|
|
pos = 0;
|
|
|
|
}
|
|
|
|
tail = skip_empty_lines(new_tail);
|
|
|
|
text = tail;
|
|
|
|
} else {
|
2019-08-09 15:15:06 +01:00
|
|
|
debug_assert_ne!(tail, &tail[i..]);
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = &tail[i..];
|
|
|
|
pos += i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !text.is_empty() {
|
2019-08-09 15:15:06 +01:00
|
|
|
insert_paragraph!(&text[0..pos].trim_end_matches('\n'));
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_block<'a, T: ElementArena<'a>>(
|
2019-08-06 07:03:16 +01:00
|
|
|
contents: &'a str,
|
2019-08-09 15:15:06 +01:00
|
|
|
arena: &mut T,
|
|
|
|
parent: NodeId,
|
2019-08-06 07:03:16 +01:00
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
) -> Option<&'a str> {
|
|
|
|
if let Ok((tail, (fn_def, content))) = FnDef::parse(contents) {
|
|
|
|
let node = arena.push_element(fn_def, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
} else if let Some((tail, list, content)) = List::parse(contents) {
|
|
|
|
let indent = list.indent;
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(list, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::List {
|
|
|
|
content,
|
|
|
|
node,
|
|
|
|
indent,
|
|
|
|
});
|
2019-08-09 15:15:06 +01:00
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
let contents = contents.trim_start();
|
2019-08-06 07:03:16 +01:00
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
match contents.as_bytes().get(0)? {
|
|
|
|
b'C' => {
|
|
|
|
if let Ok((tail, clock)) = Clock::parse(contents) {
|
|
|
|
arena.push_element(clock, parent);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'\'' => {
|
|
|
|
// TODO: LaTeX environment
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'-' => {
|
|
|
|
if let Ok((tail, _)) = parse_rule(contents) {
|
|
|
|
arena.push_element(Element::Rule, parent);
|
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b':' => {
|
|
|
|
if let Ok((tail, (drawer, content))) = Drawer::parse(contents) {
|
|
|
|
let node = arena.push_element(drawer, parent);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, value)) = parse_fixed_width(contents) {
|
|
|
|
arena.push_element(
|
|
|
|
Element::FixedWidth {
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
b'|' => {
|
|
|
|
if let Some(tail) = parse_table(arena, contents, containers, parent) {
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b'#' => {
|
|
|
|
if let Ok((tail, (name, args, content))) = parse_block_element(contents) {
|
|
|
|
match_block(
|
|
|
|
arena,
|
|
|
|
parent,
|
|
|
|
containers,
|
|
|
|
name.into(),
|
|
|
|
args.map(Into::into),
|
|
|
|
content,
|
|
|
|
);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, (dyn_block, content))) = DynBlock::parse(contents) {
|
|
|
|
let node = arena.push_element(dyn_block, parent);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, (key, optional, value))) = parse_keyword(contents) {
|
|
|
|
if (&*key).eq_ignore_ascii_case("CALL") {
|
|
|
|
arena.push_element(
|
|
|
|
BabelCall {
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
arena.push_element(
|
|
|
|
Keyword {
|
|
|
|
key: key.into(),
|
|
|
|
optional: optional.map(Into::into),
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, value)) = parse_comment(contents) {
|
|
|
|
arena.push_element(
|
|
|
|
Element::Comment {
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn match_block<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
name: Cow<'a, str>,
|
|
|
|
args: Option<Cow<'a, str>>,
|
|
|
|
content: &'a str,
|
|
|
|
) {
|
|
|
|
match &*name.to_uppercase() {
|
|
|
|
"CENTER" => {
|
|
|
|
let node = arena.push_element(CenterBlock { parameters: args }, parent);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
"QUOTE" => {
|
|
|
|
let node = arena.push_element(QuoteBlock { parameters: args }, parent);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
"COMMENT" => {
|
|
|
|
arena.push_element(
|
|
|
|
CommentBlock {
|
|
|
|
data: args,
|
|
|
|
contents: content.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"EXAMPLE" => {
|
|
|
|
arena.push_element(
|
|
|
|
ExampleBlock {
|
|
|
|
data: args,
|
|
|
|
contents: content.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"EXPORT" => {
|
|
|
|
arena.push_element(
|
|
|
|
ExportBlock {
|
|
|
|
data: args.unwrap_or_default(),
|
|
|
|
contents: content.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"SRC" => {
|
|
|
|
let (language, arguments) = match &args {
|
|
|
|
Some(Cow::Borrowed(args)) => {
|
|
|
|
let (language, arguments) =
|
|
|
|
args.split_at(args.find(' ').unwrap_or_else(|| args.len()));
|
|
|
|
(language.into(), arguments.into())
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
None => (Cow::Borrowed(""), Cow::Borrowed("")),
|
|
|
|
_ => unreachable!("`parse_block_element` returns `Some(Cow::Borrowed)` or `None`"),
|
|
|
|
};
|
|
|
|
arena.push_element(
|
|
|
|
SourceBlock {
|
|
|
|
arguments,
|
|
|
|
language,
|
|
|
|
contents: content.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
"VERSE" => {
|
|
|
|
let node = arena.push_element(VerseBlock { parameters: args }, parent);
|
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let node = arena.push_element(
|
|
|
|
SpecialBlock {
|
|
|
|
parameters: args,
|
|
|
|
name,
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_inlines<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
content: &'a str,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
let mut tail = content;
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(new_tail) = parse_inline(tail, arena, containers, parent) {
|
2019-08-06 07:03:16 +01:00
|
|
|
tail = new_tail;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut text = tail;
|
|
|
|
let mut pos = 0;
|
|
|
|
|
|
|
|
let bs = bytes!(b'@', b'<', b'[', b' ', b'(', b'{', b'\'', b'"', b'\n');
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
macro_rules! insert_text {
|
|
|
|
($value:expr) => {
|
|
|
|
arena.insert_before_last_child(Element::Text { value: $value }, parent);
|
|
|
|
pos = 0;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! update_tail {
|
|
|
|
($new_tail:ident) => {
|
|
|
|
debug_assert_ne!(tail, $new_tail);
|
|
|
|
tail = $new_tail;
|
|
|
|
text = $new_tail;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-08-06 07:03:16 +01:00
|
|
|
while let Some(off) = bs.find(tail.as_bytes()) {
|
|
|
|
match tail.as_bytes()[off] {
|
|
|
|
b'{' => {
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(new_tail) = parse_inline(&tail[off..], arena, containers, parent) {
|
2019-08-06 07:03:16 +01:00
|
|
|
if pos != 0 {
|
2019-08-09 15:15:06 +01:00
|
|
|
insert_text!(&text[0..pos + off]);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
update_tail!(new_tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
continue;
|
2019-08-09 15:15:06 +01:00
|
|
|
} else if let Some(new_tail) =
|
|
|
|
parse_inline(&tail[off + 1..], arena, containers, parent)
|
2019-08-06 07:03:16 +01:00
|
|
|
{
|
2019-08-09 15:15:06 +01:00
|
|
|
insert_text!(&text[0..pos + off + 1]);
|
|
|
|
update_tail!(new_tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b' ' | b'(' | b'\'' | b'"' | b'\n' => {
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(new_tail) = parse_inline(&tail[off + 1..], arena, containers, parent) {
|
|
|
|
insert_text!(&text[0..pos + off + 1]);
|
|
|
|
update_tail!(new_tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Some(new_tail) = parse_inline(&tail[off..], arena, containers, parent) {
|
2019-08-06 07:03:16 +01:00
|
|
|
if pos != 0 {
|
2019-08-09 15:15:06 +01:00
|
|
|
insert_text!(&text[0..pos + off]);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
update_tail!(new_tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tail = &tail[off + 1..];
|
|
|
|
pos += off + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !text.is_empty() {
|
2019-08-09 15:15:06 +01:00
|
|
|
arena.push_element(Element::Text { value: text }, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_inline<'a, T: ElementArena<'a>>(
|
2019-08-06 07:03:16 +01:00
|
|
|
contents: &'a str,
|
2019-08-09 15:15:06 +01:00
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
parent: NodeId,
|
|
|
|
) -> Option<&'a str> {
|
2019-08-06 07:03:16 +01:00
|
|
|
if contents.len() < 3 {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let bytes = contents.as_bytes();
|
|
|
|
match bytes[0] {
|
2019-08-09 15:15:06 +01:00
|
|
|
b'@' => {
|
|
|
|
if let Ok((tail, snippet)) = Snippet::parse(contents) {
|
|
|
|
arena.push_element(snippet, parent);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b'{' => {
|
|
|
|
if let Ok((tail, macros)) = Macros::parse(contents) {
|
|
|
|
arena.push_element(macros, parent);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b'<' => {
|
|
|
|
if let Ok((tail, (radio, _content))) = RadioTarget::parse(contents) {
|
|
|
|
arena.push_element(radio, parent);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, target)) = Target::parse(contents) {
|
|
|
|
arena.push_element(target, parent);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, timestamp)) = Timestamp::parse_active(contents) {
|
|
|
|
arena.push_element(timestamp, parent);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, timestamp)) = Timestamp::parse_diary(contents) {
|
|
|
|
arena.push_element(timestamp, parent);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
2019-08-06 07:03:16 +01:00
|
|
|
b'[' => {
|
2019-08-09 15:15:06 +01:00
|
|
|
if let Ok((tail, fn_ref)) = FnRef::parse(contents) {
|
|
|
|
arena.push_element(fn_ref, parent);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, link)) = Link::parse(contents) {
|
|
|
|
arena.push_element(link, parent);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, cookie)) = Cookie::parse(contents) {
|
|
|
|
arena.push_element(cookie, parent);
|
|
|
|
return Some(tail);
|
|
|
|
} else if let Ok((tail, timestamp)) = Timestamp::parse_inactive(contents) {
|
|
|
|
arena.push_element(timestamp, parent);
|
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
b'*' => {
|
|
|
|
if let Some((tail, content)) = parse_emphasis(contents, b'*') {
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(Element::Bold, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
b'+' => {
|
|
|
|
if let Some((tail, content)) = parse_emphasis(contents, b'+') {
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(Element::Strike, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
b'/' => {
|
|
|
|
if let Some((tail, content)) = parse_emphasis(contents, b'/') {
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(Element::Italic, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
b'_' => {
|
|
|
|
if let Some((tail, content)) = parse_emphasis(contents, b'_') {
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(Element::Underline, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline { content, node });
|
2019-08-09 15:15:06 +01:00
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b'=' => {
|
|
|
|
if let Some((tail, value)) = parse_emphasis(contents, b'=') {
|
|
|
|
arena.push_element(
|
|
|
|
Element::Verbatim {
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b'~' => {
|
|
|
|
if let Some((tail, value)) = parse_emphasis(contents, b'~') {
|
|
|
|
arena.push_element(
|
|
|
|
Element::Code {
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b's' => {
|
|
|
|
if let Ok((tail, inline_src)) = InlineSrc::parse(contents) {
|
|
|
|
arena.push_element(inline_src, parent);
|
|
|
|
return Some(tail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
b'c' => {
|
|
|
|
if let Ok((tail, inline_call)) = InlineCall::parse(contents) {
|
|
|
|
arena.push_element(inline_call, parent);
|
|
|
|
return Some(tail);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
_ => (),
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
|
|
|
|
None
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_list_items<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
mut contents: &'a str,
|
|
|
|
indent: usize,
|
|
|
|
parent: NodeId,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
|
|
|
) {
|
|
|
|
while !contents.is_empty() {
|
|
|
|
let (tail, list_item, content) = ListItem::parse(contents, indent);
|
2019-08-09 15:15:06 +01:00
|
|
|
let node = arena.push_element(list_item, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Block { content, node });
|
|
|
|
contents = tail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_table<'a, T: ElementArena<'a>>(
|
|
|
|
arena: &mut T,
|
2019-08-06 07:03:16 +01:00
|
|
|
contents: &'a str,
|
|
|
|
containers: &mut Vec<Container<'a>>,
|
2019-08-09 15:15:06 +01:00
|
|
|
parent: NodeId,
|
|
|
|
) -> Option<&'a str> {
|
2019-08-06 07:03:16 +01:00
|
|
|
if contents.trim_start().starts_with('|') {
|
2019-08-09 15:15:06 +01:00
|
|
|
let table_node = arena.push_element(Table::Org { tblfm: None }, parent);
|
2019-08-06 07:03:16 +01:00
|
|
|
|
|
|
|
let mut last_end = 0;
|
|
|
|
for start in memchr_iter(b'\n', contents.as_bytes()) {
|
|
|
|
let line = contents[last_end..start].trim();
|
|
|
|
match TableRow::parse(line) {
|
|
|
|
Some(TableRow::Standard) => {
|
2019-08-09 15:15:06 +01:00
|
|
|
let row_node = arena.push_element(TableRow::Standard, table_node);
|
2019-08-06 07:03:16 +01:00
|
|
|
for cell in line[1..].split_terminator('|') {
|
2019-08-09 15:15:06 +01:00
|
|
|
let cell_node = arena.push_element(Element::TableCell, row_node);
|
2019-08-06 07:03:16 +01:00
|
|
|
containers.push(Container::Inline {
|
|
|
|
content: cell.trim(),
|
|
|
|
node: cell_node,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(TableRow::Rule) => {
|
2019-08-09 15:15:06 +01:00
|
|
|
arena.push_element(TableRow::Rule, table_node);
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
None => return Some(&contents[last_end..]),
|
2019-08-06 07:03:16 +01:00
|
|
|
}
|
|
|
|
last_end = start + 1;
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
Some("")
|
|
|
|
} else if let Ok((tail, value)) = parse_table_el(contents) {
|
|
|
|
arena.push_element(
|
|
|
|
Table::TableEl {
|
|
|
|
value: value.into(),
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
);
|
|
|
|
Some(tail)
|
2019-08-06 07:03:16 +01:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn line(input: &str) -> IResult<&str, &str> {
|
|
|
|
terminated(not_line_ending, opt(line_ending))(input)
|
2019-08-04 10:46:10 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn eol(input: &str) -> IResult<&str, &str> {
|
|
|
|
verify(line, |s: &str| s.trim().is_empty())(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn take_lines_while(predicate: impl Fn(&str) -> bool) -> impl Fn(&str) -> IResult<&str, &str> {
|
|
|
|
move |input| {
|
|
|
|
recognize(many0_count(verify(
|
|
|
|
|s: &str| {
|
|
|
|
// repeat until eof
|
|
|
|
if s.is_empty() {
|
|
|
|
Err(Err::Error(error_position!(s, ErrorKind::Eof)))
|
|
|
|
} else {
|
|
|
|
line(s)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|s: &str| predicate(s),
|
|
|
|
)))(input)
|
2019-08-04 10:46:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn take_lines_while1(predicate: impl Fn(&str) -> bool) -> impl Fn(&str) -> IResult<&str, &str> {
|
2019-08-04 10:46:10 +01:00
|
|
|
move |input| {
|
2019-08-09 15:15:06 +01:00
|
|
|
recognize(many1_count(verify(
|
|
|
|
|s: &str| {
|
|
|
|
// repeat until eof
|
|
|
|
if s.is_empty() {
|
|
|
|
Err(Err::Error(error_position!(s, ErrorKind::Eof)))
|
|
|
|
} else {
|
|
|
|
line(s)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|s: &str| predicate(s),
|
|
|
|
)))(input)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn skip_empty_lines(input: &str) -> &str {
|
|
|
|
take_lines_while(|line| line.trim().is_empty())(input)
|
|
|
|
.map(|(tail, _)| tail)
|
|
|
|
.unwrap_or(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_headline(input: &str) -> IResult<&str, &str> {
|
|
|
|
let (input_, level) = get_headline_level(input)?;
|
|
|
|
map(
|
|
|
|
take_lines_while(move |line| {
|
|
|
|
if let Ok((_, l)) = get_headline_level(line) {
|
|
|
|
l.len() > level.len()
|
|
|
|
} else {
|
|
|
|
true
|
2019-08-04 10:46:10 +01:00
|
|
|
}
|
2019-08-09 15:15:06 +01:00
|
|
|
}),
|
|
|
|
move |s: &str| &input[0..level.len() + s.len()],
|
|
|
|
)(input_)
|
|
|
|
}
|
2019-08-04 10:46:10 +01:00
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn get_headline_level(input: &str) -> IResult<&str, &str> {
|
|
|
|
let (input, stars) = take_while1(|c: char| c == '*')(input)?;
|
|
|
|
if input.is_empty() || input.starts_with(' ') || input.starts_with('\n') {
|
|
|
|
Ok((input, stars))
|
|
|
|
} else {
|
|
|
|
Err(Err::Error(error_position!(input, ErrorKind::Tag)))
|
2019-08-04 10:46:10 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-05 15:23:32 +01:00
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_fixed_width(input: &str) -> IResult<&str, &str> {
|
|
|
|
take_lines_while1(|line| line == ":" || line.starts_with(": "))(input)
|
2019-08-05 15:23:32 +01:00
|
|
|
}
|
|
|
|
|
2019-08-09 15:15:06 +01:00
|
|
|
pub fn parse_comment(input: &str) -> IResult<&str, &str> {
|
|
|
|
take_lines_while1(|line| line == "#" || line.starts_with("# "))(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn take_one_word(input: &str) -> IResult<&str, &str> {
|
|
|
|
take_while1(|c: char| !c.is_ascii_whitespace())(input)
|
2019-08-05 15:23:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_skip_empty_lines() {
|
|
|
|
assert_eq!(skip_empty_lines("foo"), "foo");
|
|
|
|
assert_eq!(skip_empty_lines(" foo"), " foo");
|
|
|
|
assert_eq!(skip_empty_lines(" \nfoo\n"), "foo\n");
|
|
|
|
assert_eq!(skip_empty_lines(" \n\n\nfoo\n"), "foo\n");
|
|
|
|
assert_eq!(skip_empty_lines(" \n \n\nfoo\n"), "foo\n");
|
|
|
|
assert_eq!(skip_empty_lines(" \n \n\n foo\n"), " foo\n");
|
|
|
|
}
|