
This is to give paragraph priority over keyword in parsing so that affiliated keywords for paragraphs will be parsed as such.
99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
use crate::error::CustomError;
|
|
use crate::error::MyError;
|
|
use crate::error::Res;
|
|
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::bytes::complete::tag_no_case;
|
|
use nom::bytes::complete::take_while;
|
|
use nom::character::complete::line_ending;
|
|
use nom::character::complete::space0;
|
|
use nom::combinator::consumed;
|
|
use nom::combinator::eof;
|
|
use nom::combinator::recognize;
|
|
use nom::multi::many_till;
|
|
use nom::sequence::tuple;
|
|
|
|
use super::Context;
|
|
use crate::parser::element_parser::element;
|
|
use crate::parser::exiting::ExitClass;
|
|
use crate::parser::parser_context::ContextElement;
|
|
use crate::parser::parser_context::ExitMatcherNode;
|
|
use crate::parser::parser_with_context::parser_with_context;
|
|
use crate::parser::util::blank_line;
|
|
use crate::parser::util::exit_matcher_parser;
|
|
use crate::parser::util::get_consumed;
|
|
use crate::parser::util::immediate_in_section;
|
|
|
|
use crate::parser::util::start_of_line;
|
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
|
use crate::parser::Drawer;
|
|
use crate::parser::Element;
|
|
use crate::parser::Paragraph;
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Drawer<'s>> {
|
|
if immediate_in_section(context, "drawer") {
|
|
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
|
"Cannot nest objects of the same element",
|
|
))));
|
|
}
|
|
start_of_line(context, input)?;
|
|
let (remaining, _leading_whitespace) = space0(input)?;
|
|
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
|
|
tag(":"),
|
|
name,
|
|
tag(":"),
|
|
recognize(tuple((space0, line_ending))),
|
|
))(remaining)?;
|
|
|
|
let parser_context = context
|
|
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
|
.with_additional_node(ContextElement::Context("drawer"))
|
|
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
|
class: ExitClass::Alpha,
|
|
exit_matcher: &drawer_end,
|
|
}));
|
|
|
|
let element_matcher = parser_with_context!(element(true))(&parser_context);
|
|
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
|
let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) {
|
|
Ok((remaining, (whitespace, (_children, _exit_contents)))) => (
|
|
remaining,
|
|
vec![Element::Paragraph(Paragraph::of_text(whitespace))],
|
|
),
|
|
Err(_) => {
|
|
let (remaining, (children, _exit_contents)) =
|
|
many_till(element_matcher, exit_matcher)(remaining)?;
|
|
(remaining, children)
|
|
}
|
|
};
|
|
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
|
|
|
let source = get_consumed(input, remaining);
|
|
|
|
Ok((
|
|
remaining,
|
|
Drawer {
|
|
source,
|
|
name: drawer_name,
|
|
children,
|
|
},
|
|
))
|
|
}
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> {
|
|
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
|
|
}
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
fn drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
|
|
start_of_line(context, input)?;
|
|
recognize(tuple((
|
|
space0,
|
|
tag_no_case(":end:"),
|
|
space0,
|
|
alt((line_ending, eof)),
|
|
)))(input)
|
|
}
|