Merge branch 'wrapped_input'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded

This commit is contained in:
Tom Alexander 2023-08-24 19:40:55 -04:00
commit 533ef2a9a8
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
46 changed files with 1661 additions and 875 deletions

View File

@ -4,6 +4,7 @@ use nom::IResult;
pub type Res<T, U> = IResult<T, U, CustomError<T>>; pub type Res<T, U> = IResult<T, U, CustomError<T>>;
// TODO: MyError probably shouldn't be based on the same type as the input type since it's used exclusively with static strings right now.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum CustomError<I> { pub enum CustomError<I> {
MyError(MyError<I>), MyError(MyError<I>),

View File

@ -4,6 +4,7 @@ use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@ -16,7 +17,10 @@ use crate::parser::util::get_consumed;
use crate::parser::AngleLink; use crate::parser::AngleLink;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn angle_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, AngleLink<'s>> { pub fn angle_link<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, AngleLink<'s>> {
let (remaining, _) = tag("<")(input)?; let (remaining, _) = tag("<")(input)?;
let (remaining, proto) = protocol(context, remaining)?; let (remaining, proto) = protocol(context, remaining)?;
let (remaining, _separator) = tag(":")(remaining)?; let (remaining, _separator) = tag(":")(remaining)?;
@ -26,15 +30,18 @@ pub fn angle_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
Ok(( Ok((
remaining, remaining,
AngleLink { AngleLink {
source, source: source.into(),
link_type: proto, link_type: proto.into(),
path, path: path.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_angle<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn path_angle<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -48,6 +55,9 @@ fn path_angle<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_angle_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn path_angle_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag(">")(input) tag(">")(input)
} }

View File

@ -11,6 +11,7 @@ use nom::multi::many_till;
use nom::multi::separated_list1; use nom::multi::separated_list1;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
@ -29,7 +30,10 @@ use crate::parser::util::get_consumed;
use crate::parser::Object; use crate::parser::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn citation<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Citation<'s>> { pub fn citation<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Citation<'s>> {
// TODO: Despite being a standard object, citations cannot exist inside the global prefix/suffix for other citations because citations must contain something that matches @key which is forbidden inside the global prefix/suffix. This TODO is to evaluate if its worth putting in an explicit check for this (which can be easily accomplished by checking the output of `get_bracket_depth()`). I suspect its not worth it because I expect, outside of intentionally crafted inputs, this parser will exit immediately inside a citation since it is unlikely to find the "[cite" substring inside a citation global prefix/suffix. // TODO: Despite being a standard object, citations cannot exist inside the global prefix/suffix for other citations because citations must contain something that matches @key which is forbidden inside the global prefix/suffix. This TODO is to evaluate if its worth putting in an explicit check for this (which can be easily accomplished by checking the output of `get_bracket_depth()`). I suspect its not worth it because I expect, outside of intentionally crafted inputs, this parser will exit immediately inside a citation since it is unlikely to find the "[cite" substring inside a citation global prefix/suffix.
let (remaining, _) = tag_no_case("[cite")(input)?; let (remaining, _) = tag_no_case("[cite")(input)?;
let (remaining, _) = opt(citestyle)(remaining)?; let (remaining, _) = opt(citestyle)(remaining)?;
@ -44,11 +48,16 @@ pub fn citation<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str
let (remaining, _) = tag("]")(remaining)?; let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Citation { source })) Ok((
remaining,
Citation {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn citestyle<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> { fn citestyle<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tuple((tag("/"), style))(input)?; let (remaining, _) = tuple((tag("/"), style))(input)?;
let (remaining, _) = opt(tuple((tag("/"), variant)))(remaining)?; let (remaining, _) = opt(tuple((tag("/"), variant)))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
@ -56,14 +65,14 @@ fn citestyle<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn style<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> { fn style<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(many1(verify(anychar, |c| { recognize(many1(verify(anychar, |c| {
c.is_alphanumeric() || "_-".contains(*c) c.is_alphanumeric() || "_-".contains(*c)
})))(input) })))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn variant<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> { fn variant<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(many1(verify(anychar, |c| { recognize(many1(verify(anychar, |c| {
c.is_alphanumeric() || "_-/".contains(*c) c.is_alphanumeric() || "_-/".contains(*c)
})))(input) })))(input)
@ -72,8 +81,8 @@ fn variant<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_prefix<'r, 's>( fn global_prefix<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@ -96,12 +105,15 @@ fn global_prefix<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn global_prefix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation."); .expect("This function should only be called from inside a citation.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@ -116,7 +128,7 @@ fn global_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@ -130,8 +142,8 @@ fn global_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_suffix<'r, 's>( fn global_suffix<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@ -153,12 +165,15 @@ fn global_suffix<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn global_suffix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation."); .expect("This function should only be called from inside a citation.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@ -173,7 +188,7 @@ fn global_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@ -188,24 +203,21 @@ fn global_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::source::Source; use crate::parser::source::Source;
#[test] #[test]
fn citation_simple() { fn citation_simple() {
let input = "[cite:@foo]"; let input = OrgSource::new("[cite:@foo]");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let paragraph_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let paragraph_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph"); let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph");
let first_paragraph = match first_paragraph { let first_paragraph = match first_paragraph {
crate::parser::Element::Paragraph(paragraph) => paragraph, crate::parser::Element::Paragraph(paragraph) => paragraph,
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "[cite:@foo]"); assert_eq!(first_paragraph.get_source(), "[cite:@foo]");
assert_eq!(first_paragraph.children.len(), 1); assert_eq!(first_paragraph.children.len(), 1);
assert_eq!( assert_eq!(

View File

@ -10,6 +10,7 @@ use nom::multi::many_till;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
@ -28,21 +29,26 @@ use crate::parser::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn citation_reference<'r, 's>( pub fn citation_reference<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, CitationReference<'s>> { ) -> Res<OrgSource<'s>, CitationReference<'s>> {
let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?; let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?;
let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?; let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?;
let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?; let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, CitationReference { source })) Ok((
remaining,
CitationReference {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn citation_reference_key<'r, 's>( pub fn citation_reference_key<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, source) = recognize(tuple(( let (remaining, source) = recognize(tuple((
tag("@"), tag("@"),
many1(verify( many1(verify(
@ -59,7 +65,10 @@ pub fn citation_reference_key<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_prefix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec<Object<'s>>> { fn key_prefix<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@ -81,7 +90,10 @@ fn key_prefix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_suffix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec<Object<'s>>> { fn key_suffix<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@ -114,12 +126,15 @@ pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r Citatio
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn key_prefix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation reference."); .expect("This function should only be called from inside a citation reference.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@ -134,7 +149,7 @@ fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@ -146,12 +161,15 @@ fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn key_suffix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation reference."); .expect("This function should only be called from inside a citation reference.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@ -166,7 +184,7 @@ fn key_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }

View File

@ -11,6 +11,7 @@ use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
@ -19,8 +20,11 @@ use crate::parser::util::start_of_line;
use crate::parser::Clock; use crate::parser::Clock;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn clock<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Clock<'s>> { pub fn clock<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Clock<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _clock) = tag_no_case("clock:")(remaining)?; let (remaining, _clock) = tag_no_case("clock:")(remaining)?;
let (remaining, _gap_whitespace) = space1(remaining)?; let (remaining, _gap_whitespace) = space1(remaining)?;
@ -31,14 +35,19 @@ pub fn clock<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, C
))(remaining)?; ))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Clock { source })) Ok((
remaining,
Clock {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_timestamp_range_duration<'r, 's>( fn inactive_timestamp_range_duration<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple(( recognize(tuple((
tag("["), tag("["),
is_not("\r\n]"), is_not("\r\n]"),
@ -50,14 +59,17 @@ fn inactive_timestamp_range_duration<'r, 's>(
space1, space1,
digit1, digit1,
tag(":"), tag(":"),
verify(digit1, |mm: &str| mm.len() == 2), verify(digit1, |mm: &OrgSource<'_>| mm.len() == 2),
space0, space0,
alt((line_ending, eof)), alt((line_ending, eof)),
)))(input) )))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_timestamp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn inactive_timestamp<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple(( recognize(tuple((
tag("["), tag("["),
is_not("\r\n]"), is_not("\r\n]"),

View File

@ -11,6 +11,7 @@ use nom::multi::many0;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::get_consumed; use super::util::get_consumed;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
@ -24,10 +25,13 @@ use crate::parser::util::start_of_line;
use crate::parser::Comment; use crate::parser::Comment;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn comment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Comment<'s>> { pub fn comment<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Comment<'s>> {
if immediate_in_section(context, "comment") { if immediate_in_section(context, "comment") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
let parser_context = context.with_additional_node(ContextElement::Context("comment")); let parser_context = context.with_additional_node(ContextElement::Context("comment"));
@ -38,12 +42,20 @@ pub fn comment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?; many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Comment { source })) Ok((
remaining,
Comment {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn comment_line<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn comment_line<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
let (remaining, _indent) = space0(input)?; let (remaining, _indent) = space0(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple(( let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple((
tag("#"), tag("#"),
@ -57,22 +69,21 @@ fn comment_line<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
#[test] #[test]
fn require_space_after_hash() { fn require_space_after_hash() {
let input = "# Comment line let input = OrgSource::new(
"# Comment line
#not a comment #not a comment
# Comment again"; # Comment again",
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let comment_matcher = parser_with_context!(comment)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let comment_matcher = parser_with_context!(comment)(&document_context);
let (remaining, first_comment) = comment_matcher(input).expect("Parse first comment"); let (remaining, first_comment) = comment_matcher(input).expect("Parse first comment");
assert_eq!( assert_eq!(
remaining, Into::<&str>::into(remaining),
r#"#not a comment r#"#not a comment
# Comment again"# # Comment again"#
); );

View File

@ -6,6 +6,7 @@ use nom::combinator::eof;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::sexp::sexp; use super::sexp::sexp;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
@ -14,8 +15,11 @@ use crate::parser::util::start_of_line;
use crate::parser::DiarySexp; use crate::parser::DiarySexp;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn diary_sexp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, DiarySexp<'s>> { pub fn diary_sexp<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, DiarySexp<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _clock) = tag("%%")(remaining)?; let (remaining, _clock) = tag("%%")(remaining)?;
let (remaining, _gap_whitespace) = space0(remaining)?; let (remaining, _gap_whitespace) = space0(remaining)?;
@ -24,5 +28,10 @@ pub fn diary_sexp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?; recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, DiarySexp { source })) Ok((
remaining,
DiarySexp {
source: source.into(),
},
))
} }

View File

@ -16,6 +16,8 @@ use nom::sequence::tuple;
use super::element::Element; use super::element::Element;
use super::object::Object; use super::object::Object;
use super::org_source::convert_error;
use super::org_source::OrgSource;
use super::parser_with_context::parser_with_context; use super::parser_with_context::parser_with_context;
use super::source::Source; use super::source::Source;
use super::token::AllTokensIterator; use super::token::AllTokensIterator;
@ -95,9 +97,10 @@ impl<'s> Source<'s> for Heading<'s> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn document(input: &str) -> Res<&str, Document> { pub fn document(input: &str) -> Res<&str, Document> {
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let wrapped_input = OrgSource::new(input);
initial_context.with_additional_node(ContextElement::DocumentRoot(input)); let (remaining, document) = _document(&initial_context, wrapped_input)
let (remaining, document) = _document(&document_context, input)?; .map(|(rem, out)| (Into::<&str>::into(rem), out))
.map_err(convert_error)?;
{ {
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets. // If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
let all_radio_targets: Vec<&Vec<Object<'_>>> = document let all_radio_targets: Vec<&Vec<Object<'_>>> = document
@ -113,17 +116,22 @@ pub fn document(input: &str) -> Res<&str, Document> {
.map(|rt| &rt.children) .map(|rt| &rt.children)
.collect(); .collect();
if !all_radio_targets.is_empty() { if !all_radio_targets.is_empty() {
let document_context = document_context let initial_context = initial_context
.with_additional_node(ContextElement::RadioTarget(all_radio_targets)); .with_additional_node(ContextElement::RadioTarget(all_radio_targets));
let (remaining, document) = _document(&document_context, input)?; let (remaining, document) = _document(&initial_context, wrapped_input)
return Ok((remaining, document)); .map(|(rem, out)| (Into::<&str>::into(rem), out))
.map_err(convert_error)?;
return Ok((remaining.into(), document));
} }
} }
Ok((remaining, document)) Ok((remaining.into(), document))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Document<'s>> { fn _document<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Document<'s>> {
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context); let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
let heading_matcher = parser_with_context!(heading)(context); let heading_matcher = parser_with_context!(heading)(context);
let (remaining, _blank_lines) = many0(blank_line)(input)?; let (remaining, _blank_lines) = many0(blank_line)(input)?;
@ -133,7 +141,7 @@ pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
Ok(( Ok((
remaining, remaining,
Document { Document {
source, source: source.into(),
zeroth_section, zeroth_section,
children, children,
}, },
@ -141,7 +149,10 @@ pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Section<'s>> { fn zeroth_section<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Section<'s>> {
// TODO: The zeroth section is specialized so it probably needs its own parser // TODO: The zeroth section is specialized so it probably needs its own parser
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
@ -182,11 +193,20 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Section { source, children })) Ok((
remaining,
Section {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn section<'r, 's>(context: Context<'r, 's>, mut input: &'s str) -> Res<&'s str, Section<'s>> { fn section<'r, 's>(
context: Context<'r, 's>,
mut input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Section<'s>> {
// TODO: The zeroth section is specialized so it probably needs its own parser // TODO: The zeroth section is specialized so it probably needs its own parser
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
@ -223,17 +243,29 @@ fn section<'r, 's>(context: Context<'r, 's>, mut input: &'s str) -> Res<&'s str,
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Section { source, children })) Ok((
remaining,
Section {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn section_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn section_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let headline_matcher = parser_with_context!(headline)(context); let headline_matcher = parser_with_context!(headline)(context);
recognize(headline_matcher)(input) recognize(headline_matcher)(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Heading<'s>> { fn heading<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Heading<'s>> {
not(|i| context.check_exit_matcher(i))(input)?; not(|i| context.check_exit_matcher(i))(input)?;
let (remaining, (star_count, _ws, title)) = headline(context, input)?; let (remaining, (star_count, _ws, title)) = headline(context, input)?;
let section_matcher = parser_with_context!(section)(context); let section_matcher = parser_with_context!(section)(context);
@ -249,7 +281,7 @@ fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Hea
Ok(( Ok((
remaining, remaining,
Heading { Heading {
source, source: source.into(),
stars: star_count, stars: star_count,
title, title,
children, children,
@ -260,18 +292,17 @@ fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Hea
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn headline<'r, 's>( fn headline<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, (usize, &'s str, Vec<Object<'s>>)> { ) -> Res<OrgSource<'s>, (usize, OrgSource<'s>, Vec<Object<'s>>)> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Document, class: ExitClass::Document,
exit_matcher: &headline_end, exit_matcher: &headline_end,
})); }));
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
let start_of_line_matcher = parser_with_context!(start_of_line)(&parser_context);
let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple(( let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple((
start_of_line_matcher, start_of_line,
many1_count(tag("*")), many1_count(tag("*")),
space1, space1,
many1(standard_set_object_matcher), many1(standard_set_object_matcher),
@ -281,7 +312,10 @@ fn headline<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn headline_end<'r, 's>(_context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn headline_end<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
line_ending(input) line_ending(input)
} }

View File

@ -10,6 +10,7 @@ use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -31,13 +32,16 @@ use crate::parser::Element;
use crate::parser::Paragraph; use crate::parser::Paragraph;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Drawer<'s>> { pub fn drawer<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Drawer<'s>> {
if immediate_in_section(context, "drawer") { if immediate_in_section(context, "drawer") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
tag(":"), tag(":"),
@ -63,9 +67,9 @@ pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
))(remaining) ))(remaining)
{ {
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => { Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line)); let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain); let source = get_consumed(remaining, remain);
element.set_source(source); element.set_source(source.into());
(remain, vec![element]) (remain, vec![element])
} }
Err(_) => { Err(_) => {
@ -81,21 +85,24 @@ pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
Ok(( Ok((
remaining, remaining,
Drawer { Drawer {
source, source: source.into(),
name: drawer_name, name: drawer_name.into(),
children, children,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input) take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn drawer_end<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
recognize(tuple(( recognize(tuple((
space0, space0,
tag_no_case(":end:"), tag_no_case(":end:"),

View File

@ -11,6 +11,7 @@ use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -33,15 +34,15 @@ use crate::parser::Element;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn dynamic_block<'r, 's>( pub fn dynamic_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, DynamicBlock<'s>> { ) -> Res<OrgSource<'s>, DynamicBlock<'s>> {
// TODO: Do I need to differentiate between different dynamic block types. // TODO: Do I need to differentiate between different dynamic block types.
if immediate_in_section(context, "dynamic block") { if immediate_in_section(context, "dynamic block") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name, parameters, _ws)) = tuple(( let (remaining, (_begin, name, parameters, _ws)) = tuple((
recognize(tuple((tag_no_case("#+begin:"), space1))), recognize(tuple((tag_no_case("#+begin:"), space1))),
@ -69,9 +70,9 @@ pub fn dynamic_block<'r, 's>(
))(remaining) ))(remaining)
{ {
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => { Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line)); let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain); let source = get_consumed(remaining, remain);
element.set_source(source); element.set_source(source.into());
(remain, vec![element]) (remain, vec![element])
} }
Err(_) => { Err(_) => {
@ -86,27 +87,30 @@ pub fn dynamic_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
DynamicBlock { DynamicBlock {
source, source: source.into(),
name, name: name.into(),
parameters, parameters: parameters.map(|val| val.into()),
children, children,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not(" \t\r\n")(input) is_not(" \t\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parameters<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not("\r\n")(input) is_not("\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn dynamic_block_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn dynamic_block_end<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
let (remaining, source) = recognize(tuple(( let (remaining, source) = recognize(tuple((
space0, space0,
tag_no_case("#+end:"), tag_no_case("#+end:"),

View File

@ -19,6 +19,7 @@ use super::lesser_block::example_block;
use super::lesser_block::export_block; use super::lesser_block::export_block;
use super::lesser_block::src_block; use super::lesser_block::src_block;
use super::lesser_block::verse_block; use super::lesser_block::verse_block;
use super::org_source::OrgSource;
use super::paragraph::paragraph; use super::paragraph::paragraph;
use super::plain_list::plain_list; use super::plain_list::plain_list;
use super::source::SetSource; use super::source::SetSource;
@ -31,16 +32,16 @@ use crate::parser::table::org_mode_table;
pub fn element( pub fn element(
can_be_paragraph: bool, can_be_paragraph: bool,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, Element<'s>> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, Element<'s>> {
move |context: Context, input: &str| _element(context, input, can_be_paragraph) move |context: Context, input: OrgSource<'_>| _element(context, input, can_be_paragraph)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _element<'r, 's>( fn _element<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
can_be_paragraph: bool, can_be_paragraph: bool,
) -> Res<&'s str, Element<'s>> { ) -> Res<OrgSource<'s>, Element<'s>> {
let plain_list_matcher = parser_with_context!(plain_list)(context); let plain_list_matcher = parser_with_context!(plain_list)(context);
let greater_block_matcher = parser_with_context!(greater_block)(context); let greater_block_matcher = parser_with_context!(greater_block)(context);
let dynamic_block_matcher = parser_with_context!(dynamic_block)(context); let dynamic_block_matcher = parser_with_context!(dynamic_block)(context);
@ -103,7 +104,7 @@ fn _element<'r, 's>(
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
element.set_source(source); element.set_source(source.into());
Ok((remaining, element)) Ok((remaining, element))
} }

View File

@ -7,6 +7,7 @@ use nom::combinator::eof;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::object::Entity; use crate::parser::object::Entity;
@ -14,7 +15,10 @@ use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn entity<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Entity<'s>> { pub fn entity<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Entity<'s>> {
let (remaining, _) = tag("\\")(input)?; let (remaining, _) = tag("\\")(input)?;
let (remaining, entity_name) = name(context, remaining)?; let (remaining, entity_name) = name(context, remaining)?;
let (remaining, _) = alt(( let (remaining, _) = alt((
@ -27,14 +31,17 @@ pub fn entity<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
Ok(( Ok((
remaining, remaining,
Entity { Entity {
source, source: source.into(),
entity_name, entity_name: entity_name.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn name<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// TODO: This should be defined by org-entities and optionally org-entities-user // TODO: This should be defined by org-entities and optionally org-entities-user
// TODO: Add the rest of the entities, this is a very incomplete list // TODO: Add the rest of the entities, this is a very incomplete list
@ -43,7 +50,7 @@ fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn entity_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { fn entity_end<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?; let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?;
Ok((remaining, ())) Ok((remaining, ()))

View File

@ -7,6 +7,7 @@ use nom::multi::many1;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@ -20,8 +21,8 @@ use crate::parser::ExportSnippet;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn export_snippet<'r, 's>( pub fn export_snippet<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, ExportSnippet<'s>> { ) -> Res<OrgSource<'s>, ExportSnippet<'s>> {
let (remaining, _) = tag("@@")(input)?; let (remaining, _) = tag("@@")(input)?;
let (remaining, backend_name) = backend(context, remaining)?; let (remaining, backend_name) = backend(context, remaining)?;
let parser_context = let parser_context =
@ -38,15 +39,18 @@ pub fn export_snippet<'r, 's>(
Ok(( Ok((
remaining, remaining,
ExportSnippet { ExportSnippet {
source, source: source.into(),
backend: backend_name, backend: backend_name.into(),
contents: backend_contents.map(|(_colon, backend_contents)| backend_contents), contents: backend_contents.map(|(_colon, backend_contents)| backend_contents.into()),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn backend<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn backend<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, backend_name) = let (remaining, backend_name) =
recognize(many1(verify(anychar, |c| c.is_alphanumeric() || *c == '-')))(input)?; recognize(many1(verify(anychar, |c| c.is_alphanumeric() || *c == '-')))(input)?;
@ -54,7 +58,10 @@ fn backend<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn contents<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn contents<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, source) = recognize(verify( let (remaining, source) = recognize(verify(
many_till(anychar, parser_with_context!(exit_matcher_parser)(context)), many_till(anychar, parser_with_context!(exit_matcher_parser)(context)),
|(children, _exit_contents)| !children.is_empty(), |(children, _exit_contents)| !children.is_empty(),
@ -63,6 +70,9 @@ fn contents<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn export_snippet_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn export_snippet_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag("@@")(input) tag("@@")(input)
} }

View File

@ -11,6 +11,7 @@ use nom::multi::many0;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
@ -22,8 +23,8 @@ use crate::parser::FixedWidthArea;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn fixed_width_area<'r, 's>( pub fn fixed_width_area<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, FixedWidthArea<'s>> { ) -> Res<OrgSource<'s>, FixedWidthArea<'s>> {
let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context); let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(context); let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
let (remaining, _first_line) = fixed_width_area_line_matcher(input)?; let (remaining, _first_line) = fixed_width_area_line_matcher(input)?;
@ -31,15 +32,20 @@ pub fn fixed_width_area<'r, 's>(
many0(preceded(not(exit_matcher), fixed_width_area_line_matcher))(remaining)?; many0(preceded(not(exit_matcher), fixed_width_area_line_matcher))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, FixedWidthArea { source })) Ok((
remaining,
FixedWidthArea {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn fixed_width_area_line<'r, 's>( fn fixed_width_area_line<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _indent) = space0(input)?; let (remaining, _indent) = space0(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple(( let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple((
tag(":"), tag(":"),

View File

@ -10,6 +10,7 @@ use nom::multi::many1;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::WORD_CONSTITUENT_CHARACTERS; use super::util::WORD_CONSTITUENT_CHARACTERS;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
@ -31,14 +32,14 @@ use crate::parser::util::start_of_line;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn footnote_definition<'r, 's>( pub fn footnote_definition<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, FootnoteDefinition<'s>> { ) -> Res<OrgSource<'s>, FootnoteDefinition<'s>> {
if immediate_in_section(context, "footnote definition") { if immediate_in_section(context, "footnote definition") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
start_of_line(context, input)?; start_of_line(input)?;
// Cannot be indented. // Cannot be indented.
let (remaining, (_lead_in, lbl, _lead_out, _ws)) = let (remaining, (_lead_in, lbl, _lead_out, _ws)) =
tuple((tag_no_case("[fn:"), label, tag("]"), space0))(input)?; tuple((tag_no_case("[fn:"), label, tag("]"), space0))(input)?;
@ -59,15 +60,15 @@ pub fn footnote_definition<'r, 's>(
Ok(( Ok((
remaining, remaining,
FootnoteDefinition { FootnoteDefinition {
source, source: source.into(),
label: lbl, label: lbl.into(),
children, children,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn label<'s>(input: &'s str) -> Res<&'s str, &'s str> { pub fn label<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt(( alt((
digit1, digit1,
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c)), take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c)),
@ -77,9 +78,8 @@ pub fn label<'s>(input: &'s str) -> Res<&'s str, &'s str> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn footnote_definition_end<'r, 's>( fn footnote_definition_end<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let start_of_line_matcher = parser_with_context!(start_of_line)(context);
let allow_nesting_context = let allow_nesting_context =
context.with_additional_node(ContextElement::Context("allow nesting footnotes")); context.with_additional_node(ContextElement::Context("allow nesting footnotes"));
let footnote_definition_matcher = parser_with_context!(footnote_definition)( let footnote_definition_matcher = parser_with_context!(footnote_definition)(
@ -97,8 +97,10 @@ fn footnote_definition_end<'r, 's>(
footnote_definition_matcher, footnote_definition_matcher,
))), ))),
recognize(tuple(( recognize(tuple((
start_of_line_matcher, start_of_line,
verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2), verify(many1(blank_line), |lines: &Vec<OrgSource<'_>>| {
lines.len() >= 2
}),
))), ))),
))(input)?; ))(input)?;
@ -108,27 +110,26 @@ fn footnote_definition_end<'r, 's>(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::Source; use crate::parser::Source;
#[test] #[test]
fn two_paragraphs() { fn two_paragraphs() {
let input = "[fn:1] A footnote. let input = OrgSource::new(
"[fn:1] A footnote.
[fn:2] A multi- [fn:2] A multi-
line footnote."; line footnote.",
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let footnote_definition_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let footnote_definition_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_footnote_definition) = let (remaining, first_footnote_definition) =
footnote_definition_matcher(input).expect("Parse first footnote_definition"); footnote_definition_matcher(input).expect("Parse first footnote_definition");
let (remaining, second_footnote_definition) = let (remaining, second_footnote_definition) =
footnote_definition_matcher(remaining).expect("Parse second footnote_definition."); footnote_definition_matcher(remaining).expect("Parse second footnote_definition.");
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!( assert_eq!(
first_footnote_definition.get_source(), first_footnote_definition.get_source(),
"[fn:1] A footnote. "[fn:1] A footnote.
@ -145,19 +146,19 @@ line footnote."
#[test] #[test]
fn multiline_break() { fn multiline_break() {
let input = "[fn:2] A multi- let input = OrgSource::new(
"[fn:2] A multi-
line footnote. line footnote.
not in the footnote."; not in the footnote.",
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let footnote_definition_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let footnote_definition_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_footnote_definition) = let (remaining, first_footnote_definition) =
footnote_definition_matcher(input).expect("Parse first footnote_definition"); footnote_definition_matcher(input).expect("Parse first footnote_definition");
assert_eq!(remaining, "not in the footnote."); assert_eq!(Into::<&str>::into(remaining), "not in the footnote.");
assert_eq!( assert_eq!(
first_footnote_definition.get_source(), first_footnote_definition.get_source(),
"[fn:2] A multi- "[fn:2] A multi-

View File

@ -5,6 +5,7 @@ use nom::character::complete::space0;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::parser_context::ContextElement; use super::parser_context::ContextElement;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
@ -19,13 +20,12 @@ use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::FootnoteReference; use crate::parser::FootnoteReference;
use crate::parser::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn footnote_reference<'r, 's>( pub fn footnote_reference<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, FootnoteReference<'s>> { ) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
alt(( alt((
parser_with_context!(anonymous_footnote)(context), parser_with_context!(anonymous_footnote)(context),
parser_with_context!(footnote_reference_only)(context), parser_with_context!(footnote_reference_only)(context),
@ -36,8 +36,8 @@ pub fn footnote_reference<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn anonymous_footnote<'r, 's>( fn anonymous_footnote<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, FootnoteReference<'s>> { ) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
let (remaining, _) = tag_no_case("[fn::")(input)?; let (remaining, _) = tag_no_case("[fn::")(input)?;
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::FootnoteReferenceDefinition( .with_additional_node(ContextElement::FootnoteReferenceDefinition(
@ -65,7 +65,7 @@ fn anonymous_footnote<'r, 's>(
Ok(( Ok((
remaining, remaining,
FootnoteReference { FootnoteReference {
source, source: source.into(),
label: None, label: None,
definition: children, definition: children,
}, },
@ -75,8 +75,8 @@ fn anonymous_footnote<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inline_footnote<'r, 's>( fn inline_footnote<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, FootnoteReference<'s>> { ) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
let (remaining, _) = tag_no_case("[fn:")(input)?; let (remaining, _) = tag_no_case("[fn:")(input)?;
let (remaining, label_contents) = label(remaining)?; let (remaining, label_contents) = label(remaining)?;
let (remaining, _) = tag(":")(remaining)?; let (remaining, _) = tag(":")(remaining)?;
@ -106,8 +106,8 @@ fn inline_footnote<'r, 's>(
Ok(( Ok((
remaining, remaining,
FootnoteReference { FootnoteReference {
source, source: source.into(),
label: Some(label_contents), label: Some(label_contents.into()),
definition: children, definition: children,
}, },
)) ))
@ -116,8 +116,8 @@ fn inline_footnote<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn footnote_reference_only<'r, 's>( fn footnote_reference_only<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, FootnoteReference<'s>> { ) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
let (remaining, _) = tag_no_case("[fn:")(input)?; let (remaining, _) = tag_no_case("[fn:")(input)?;
let (remaining, label_contents) = label(remaining)?; let (remaining, label_contents) = label(remaining)?;
let (remaining, _) = tag("]")(remaining)?; let (remaining, _) = tag("]")(remaining)?;
@ -126,28 +126,23 @@ fn footnote_reference_only<'r, 's>(
Ok(( Ok((
remaining, remaining,
FootnoteReference { FootnoteReference {
source, source: source.into(),
label: Some(label_contents), label: Some(label_contents.into()),
definition: Vec::with_capacity(0), definition: Vec::with_capacity(0),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn definition<'s>(input: &'s str) -> Res<&'s str, Vec<Object<'s>>> {
Ok((input, vec![]))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn footnote_definition_end<'r, 's>( fn footnote_definition_end<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a footnote definition."); .expect("This function should only be called from inside a footnote definition.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@ -164,7 +159,7 @@ fn footnote_definition_end<'r, 's>(
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep // Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"NoFootnoteReferenceDefinitionEnd", "NoFootnoteReferenceDefinitionEnd".into(),
)))); ))));
} }
tag("]")(input) tag("]")(input)

View File

@ -11,6 +11,7 @@ use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -33,26 +34,28 @@ use crate::parser::Paragraph;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn greater_block<'r, 's>( pub fn greater_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, GreaterBlock<'s>> { ) -> Res<OrgSource<'s>, GreaterBlock<'s>> {
// TODO: Do I need to differentiate between different greater block types. // TODO: Do I need to differentiate between different greater block types.
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name)) = tuple(( let (remaining, (_begin, name)) = tuple((
tag_no_case("#+begin_"), tag_no_case("#+begin_"),
verify(name, |name: &str| match name.to_lowercase().as_str() { verify(name, |name: &OrgSource<'_>| {
match Into::<&str>::into(name).to_lowercase().as_str() {
"comment" | "example" | "export" | "src" | "verse" => false, "comment" | "example" | "export" | "src" | "verse" => false,
_ => true, _ => true,
}
}), }),
))(remaining)?; ))(remaining)?;
let context_name = match name.to_lowercase().as_str() { let context_name = match Into::<&str>::into(name).to_lowercase().as_str() {
"center" => "center block", "center" => "center block",
"quote" => "quote block", "quote" => "quote block",
_ => "greater block", _ => "greater block",
}; };
if immediate_in_section(context, context_name) { if immediate_in_section(context, context_name) {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?; let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
@ -60,7 +63,7 @@ pub fn greater_block<'r, 's>(
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
.with_additional_node(ContextElement::Context(context_name)) .with_additional_node(ContextElement::Context(context_name))
.with_additional_node(ContextElement::GreaterBlock(name)) .with_additional_node(ContextElement::GreaterBlock(name.into()))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Alpha, class: ExitClass::Alpha,
exit_matcher: &greater_block_end, exit_matcher: &greater_block_end,
@ -80,9 +83,9 @@ pub fn greater_block<'r, 's>(
))(remaining) ))(remaining)
{ {
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => { Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line)); let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain); let source = get_consumed(remaining, remain);
element.set_source(source); element.set_source(source.into());
(remain, vec![element]) (remain, vec![element])
} }
Err(_) => { Err(_) => {
@ -99,29 +102,32 @@ pub fn greater_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
GreaterBlock { GreaterBlock {
source, source: source.into(),
name, name: name.into(),
parameters, parameters: parameters.map(|val| Into::<&str>::into(val)),
children, children,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not(" \t\r\n")(input) is_not(" \t\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parameters<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not("\r\n")(input) is_not("\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn greater_block_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn greater_block_end<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
let current_name: &str = get_context_greater_block_name(context).ok_or(nom::Err::Error( let current_name: &str = get_context_greater_block_name(context).ok_or(nom::Err::Error(
CustomError::MyError(MyError("Not inside a greater block")), CustomError::MyError(MyError("Not inside a greater block".into())),
))?; ))?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, _name, _ws)) = tuple(( let (remaining, (_begin, _name, _ws)) = tuple((

View File

@ -8,6 +8,7 @@ use nom::combinator::verify;
use nom::multi::many1_count; use nom::multi::many1_count;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
@ -16,14 +17,19 @@ use crate::parser::HorizontalRule;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn horizontal_rule<'r, 's>( pub fn horizontal_rule<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, HorizontalRule<'s>> { ) -> Res<OrgSource<'s>, HorizontalRule<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, rule) = recognize(tuple(( let (remaining, rule) = recognize(tuple((
space0, space0,
verify(many1_count(tag("-")), |dashes| *dashes >= 5), verify(many1_count(tag("-")), |dashes| *dashes >= 5),
space0, space0,
alt((line_ending, eof)), alt((line_ending, eof)),
)))(input)?; )))(input)?;
Ok((remaining, HorizontalRule { source: rule })) Ok((
remaining,
HorizontalRule {
source: rule.into(),
},
))
} }

View File

@ -10,6 +10,7 @@ use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@ -24,8 +25,8 @@ use crate::parser::InlineBabelCall;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn inline_babel_call<'r, 's>( pub fn inline_babel_call<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, InlineBabelCall<'s>> { ) -> Res<OrgSource<'s>, InlineBabelCall<'s>> {
let (remaining, _) = tag_no_case("call_")(input)?; let (remaining, _) = tag_no_case("call_")(input)?;
let (remaining, _name) = name(context, remaining)?; let (remaining, _name) = name(context, remaining)?;
let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?; let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?;
@ -33,11 +34,19 @@ pub fn inline_babel_call<'r, 's>(
let (remaining, _header2) = opt(parser_with_context!(header)(context))(remaining)?; let (remaining, _header2) = opt(parser_with_context!(header)(context))(remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, InlineBabelCall { source })) Ok((
remaining,
InlineBabelCall {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn name<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -51,12 +60,18 @@ fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn name_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(one_of("[("))(input) recognize(one_of("[("))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn header<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn header<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let parser_context = context let parser_context = context
@ -78,12 +93,15 @@ fn header<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn header_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn header_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside an inline babel call header."); .expect("This function should only be called from inside an inline babel call header.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'(' => { '(' => {
current_depth += 1; current_depth += 1;
@ -101,7 +119,10 @@ fn header_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn argument<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn argument<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("(")(input)?; let (remaining, _) = tag("(")(input)?;
let parser_context = context let parser_context = context
@ -123,12 +144,15 @@ fn argument<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn argument_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn argument_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside an inline babel call argument."); .expect("This function should only be called from inside an inline babel call argument.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;

View File

@ -11,6 +11,7 @@ use nom::multi::many_till;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
use tracing::span; use tracing::span;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
@ -26,19 +27,27 @@ use crate::parser::InlineSourceBlock;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn inline_source_block<'r, 's>( pub fn inline_source_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, InlineSourceBlock<'s>> { ) -> Res<OrgSource<'s>, InlineSourceBlock<'s>> {
let (remaining, _) = tag_no_case("src_")(input)?; let (remaining, _) = tag_no_case("src_")(input)?;
let (remaining, _) = lang(context, remaining)?; let (remaining, _) = lang(context, remaining)?;
let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?; let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?;
let (remaining, _body) = body(context, remaining)?; let (remaining, _body) = body(context, remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, InlineSourceBlock { source })) Ok((
remaining,
InlineSourceBlock {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn lang<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn lang<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -52,12 +61,18 @@ fn lang<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn lang_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn lang_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(one_of("[{"))(input) recognize(one_of("[{"))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn header<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn header<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let parser_context = context let parser_context = context
@ -81,12 +96,15 @@ fn header<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn header_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn header_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside an inline source block header."); .expect("This function should only be called from inside an inline source block header.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@ -101,7 +119,7 @@ fn header_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@ -111,7 +129,10 @@ fn header_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn body<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn body<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("{")(input)?; let (remaining, _) = tag("{")(input)?;
let parser_context = context let parser_context = context
@ -135,7 +156,7 @@ fn body<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
let span = span!( let span = span!(
tracing::Level::DEBUG, tracing::Level::DEBUG,
"outside end body", "outside end body",
remaining = remaining remaining = Into::<&str>::into(remaining)
); );
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _enter = span.enter(); let _enter = span.enter();
@ -145,12 +166,15 @@ fn body<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn body_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn body_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside an inline source block body."); .expect("This function should only be called from inside an inline source block body.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'{' => { '{' => {
current_depth += 1; current_depth += 1;
@ -169,14 +193,14 @@ fn body_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'
let span = span!( let span = span!(
tracing::Level::DEBUG, tracing::Level::DEBUG,
"inside end body", "inside end body",
remaining = input, remaining = Into::<&str>::into(input),
current_depth = current_depth current_depth = current_depth
); );
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _enter = span.enter(); let _enter = span.enter();
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("}")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("}")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }

View File

@ -11,14 +11,18 @@ use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
use crate::parser::Keyword; use crate::parser::Keyword;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn keyword<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Keyword<'s>> { pub fn keyword<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Keyword<'s>> {
start_of_line(input)?;
// TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references. // TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references.
let (remaining, rule) = recognize(tuple(( let (remaining, rule) = recognize(tuple((
space0, space0,
@ -30,5 +34,10 @@ pub fn keyword<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
alt((recognize(tuple((space1, is_not("\r\n")))), space0)), alt((recognize(tuple((space1, is_not("\r\n")))), space0)),
alt((line_ending, eof)), alt((line_ending, eof)),
)))(input)?; )))(input)?;
Ok((remaining, Keyword { source: rule })) Ok((
remaining,
Keyword {
source: rule.into(),
},
))
} }

View File

@ -11,6 +11,7 @@ use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::get_consumed; use super::util::get_consumed;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
@ -25,9 +26,9 @@ use crate::parser::LatexEnvironment;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn latex_environment<'r, 's>( pub fn latex_environment<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, LatexEnvironment<'s>> { ) -> Res<OrgSource<'s>, LatexEnvironment<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple(( let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple((
tag_no_case(r#"\begin{"#), tag_no_case(r#"\begin{"#),
@ -37,7 +38,7 @@ pub fn latex_environment<'r, 's>(
line_ending, line_ending,
))(remaining)?; ))(remaining)?;
let latex_environment_end_specialized = latex_environment_end(name); let latex_environment_end_specialized = latex_environment_end(name.into());
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -48,11 +49,16 @@ pub fn latex_environment<'r, 's>(
let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?; let (remaining, _end) = latex_environment_end_specialized(&parser_context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, LatexEnvironment { source })) Ok((
remaining,
LatexEnvironment {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
take_while1(|c: char| c.is_alphanumeric() || c == '*')(input) take_while1(|c: char| c.is_alphanumeric() || c == '*')(input)
} }
@ -60,11 +66,15 @@ fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> {
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(end_matcher)) tracing::instrument(ret, level = "debug", skip(end_matcher))
)] )]
pub fn contents<'r, 's, F: Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str>>( pub fn contents<
'r,
's,
F: Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>,
>(
end_matcher: F, end_matcher: F,
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, source) = recognize(many_till( let (remaining, source) = recognize(many_till(
anychar, anychar,
peek(alt(( peek(alt((
@ -78,9 +88,9 @@ pub fn contents<'r, 's, F: Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str>
fn latex_environment_end( fn latex_environment_end(
current_name: &str, current_name: &str,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_name_lower = current_name.to_lowercase(); let current_name_lower = current_name.to_lowercase();
move |context: Context, input: &str| { move |context: Context, input: OrgSource<'_>| {
_latex_environment_end(context, input, current_name_lower.as_str()) _latex_environment_end(context, input, current_name_lower.as_str())
} }
} }
@ -88,10 +98,10 @@ fn latex_environment_end(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _latex_environment_end<'r, 's, 'x>( fn _latex_environment_end<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
current_name_lower: &'x str, current_name_lower: &'x str,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, _name, _close_brace, _ws, _line_ending)) = tuple(( let (remaining, (_begin, _name, _close_brace, _ws, _line_ending)) = tuple((
tag_no_case(r#"\end{"#), tag_no_case(r#"\end{"#),

View File

@ -13,6 +13,7 @@ use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -20,14 +21,13 @@ use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::get_one_before;
use crate::parser::LatexFragment; use crate::parser::LatexFragment;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn latex_fragment<'r, 's>( pub fn latex_fragment<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, LatexFragment<'s>> { ) -> Res<OrgSource<'s>, LatexFragment<'s>> {
let (remaining, _) = alt(( let (remaining, _) = alt((
parser_with_context!(raw_latex_fragment)(context), parser_with_context!(raw_latex_fragment)(context),
parser_with_context!(escaped_parenthesis_fragment)(context), parser_with_context!(escaped_parenthesis_fragment)(context),
@ -38,11 +38,19 @@ pub fn latex_fragment<'r, 's>(
))(input)?; ))(input)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, LatexFragment { source })) Ok((
remaining,
LatexFragment {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn raw_latex_fragment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn raw_latex_fragment<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("\\")(input)?; let (remaining, _) = tag("\\")(input)?;
let (remaining, _) = name(context, remaining)?; let (remaining, _) = name(context, remaining)?;
let (remaining, _) = many0(parser_with_context!(brackets)(context))(remaining)?; let (remaining, _) = many0(parser_with_context!(brackets)(context))(remaining)?;
@ -52,12 +60,18 @@ fn raw_latex_fragment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn name<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
alpha1(input) alpha1(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn brackets<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn brackets<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, body) = alt(( let (remaining, body) = alt((
recognize(tuple(( recognize(tuple((
tag("["), tag("["),
@ -88,8 +102,8 @@ fn brackets<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn escaped_parenthesis_fragment<'r, 's>( fn escaped_parenthesis_fragment<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("\\(")(input)?; let (remaining, _) = tag("\\(")(input)?;
let (remaining, _) = recognize(many_till( let (remaining, _) = recognize(many_till(
anychar, anychar,
@ -107,8 +121,8 @@ fn escaped_parenthesis_fragment<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn escaped_bracket_fragment<'r, 's>( fn escaped_bracket_fragment<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("\\[")(input)?; let (remaining, _) = tag("\\[")(input)?;
let (remaining, _) = recognize(many_till( let (remaining, _) = recognize(many_till(
anychar, anychar,
@ -126,8 +140,8 @@ fn escaped_bracket_fragment<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn double_dollar_fragment<'r, 's>( fn double_dollar_fragment<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
// TODO: The documentation on the dollar sign versions is incomplete. Test to figure out what the real requirements are. For example, can this span more than 3 lines and can this contain a single $ since its terminated by $$? // TODO: The documentation on the dollar sign versions is incomplete. Test to figure out what the real requirements are. For example, can this span more than 3 lines and can this contain a single $ since its terminated by $$?
let (remaining, _) = tag("$$")(input)?; let (remaining, _) = tag("$$")(input)?;
let (remaining, _) = recognize(many_till( let (remaining, _) = recognize(many_till(
@ -144,7 +158,10 @@ fn double_dollar_fragment<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn dollar_char_fragment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn dollar_char_fragment<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (_, _) = pre(context, input)?; let (_, _) = pre(context, input)?;
let (remaining, _) = tag("$")(input)?; let (remaining, _) = tag("$")(input)?;
let (remaining, _) = verify(none_of(".,?;\""), |c| !c.is_whitespace())(remaining)?; let (remaining, _) = verify(none_of(".,?;\""), |c| !c.is_whitespace())(remaining)?;
@ -156,21 +173,18 @@ fn dollar_char_fragment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { pub fn pre<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let document_root = context.get_document_root().unwrap(); let preceding_character = input.get_preceding_character();
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
if let Some('$') = preceding_character { if let Some('$') = preceding_character {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid pre character for dollar char fragment.", "Not a valid pre character for dollar char fragment.".into(),
)))); ))));
} }
Ok((input, ())) Ok((input, ()))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { pub fn post<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
// TODO: What about eof? Test to find out. // TODO: What about eof? Test to find out.
// TODO: Figure out which punctuation characters should be included. // TODO: Figure out which punctuation characters should be included.
@ -181,8 +195,8 @@ pub fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn bordered_dollar_fragment<'r, 's>( fn bordered_dollar_fragment<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (_, _) = pre(context, input)?; let (_, _) = pre(context, input)?;
let (remaining, _) = tag("$")(input)?; let (remaining, _) = tag("$")(input)?;
// TODO: I'm assuming I should be peeking at the borders but the documentation is not clear. Test to figure out. // TODO: I'm assuming I should be peeking at the borders but the documentation is not clear. Test to figure out.
@ -197,7 +211,7 @@ fn bordered_dollar_fragment<'r, 's>(
tag("$"), tag("$"),
))), ))),
)), )),
|body: &str| body.lines().take(4).count() <= 3, |body: &OrgSource<'_>| Into::<&str>::into(body).lines().take(4).count() <= 3,
)(remaining)?; )(remaining)?;
let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?; let (_, _) = peek(parser_with_context!(close_border)(context))(remaining)?;
@ -209,21 +223,24 @@ fn bordered_dollar_fragment<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn open_border<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { pub fn open_border<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(verify(none_of(".,;$"), |c| !c.is_whitespace()))(input) recognize(verify(none_of(".,;$"), |c| !c.is_whitespace()))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn close_border<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { pub fn close_border<'r, 's>(
let document_root = context.get_document_root().unwrap(); context: Context<'r, 's>,
let preceding_character = get_one_before(document_root, input) input: OrgSource<'s>,
.map(|slice| slice.chars().next()) ) -> Res<OrgSource<'s>, ()> {
.flatten(); let preceding_character = input.get_preceding_character();
match preceding_character { match preceding_character {
Some(c) if !c.is_whitespace() && !".,;$".contains(c) => Ok((input, ())), Some(c) if !c.is_whitespace() && !".,;$".contains(c) => Ok((input, ())),
_ => { _ => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid pre character for dollar char fragment.", "Not a valid pre character for dollar char fragment.".into(),
)))); ))));
} }
} }

View File

@ -11,6 +11,7 @@ use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@ -34,8 +35,8 @@ use crate::parser::util::text_until_exit;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn verse_block<'r, 's>( pub fn verse_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, VerseBlock<'s>> { ) -> Res<OrgSource<'s>, VerseBlock<'s>> {
let (remaining, name) = lesser_block_begin("verse")(context, input)?; let (remaining, name) = lesser_block_begin("verse")(context, input)?;
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
let (remaining, _nl) = line_ending(remaining)?; let (remaining, _nl) = line_ending(remaining)?;
@ -58,7 +59,9 @@ pub fn verse_block<'r, 's>(
let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) { let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) {
Ok((remaining, (whitespace, (_children, _exit_contents)))) => ( Ok((remaining, (whitespace, (_children, _exit_contents)))) => (
remaining, remaining,
vec![Object::PlainText(PlainText { source: whitespace })], vec![Object::PlainText(PlainText {
source: whitespace.into(),
})],
), ),
Err(_) => { Err(_) => {
let (remaining, (children, _exit_contents)) = let (remaining, (children, _exit_contents)) =
@ -72,9 +75,9 @@ pub fn verse_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
VerseBlock { VerseBlock {
source, source: source.into(),
name, name: name.into(),
data: parameters, data: parameters.map(|parameters| Into::<&str>::into(parameters)),
children, children,
}, },
)) ))
@ -83,8 +86,8 @@ pub fn verse_block<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn comment_block<'r, 's>( pub fn comment_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, CommentBlock<'s>> { ) -> Res<OrgSource<'s>, CommentBlock<'s>> {
let (remaining, name) = lesser_block_begin("comment")(context, input)?; let (remaining, name) = lesser_block_begin("comment")(context, input)?;
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
let (remaining, _nl) = line_ending(remaining)?; let (remaining, _nl) = line_ending(remaining)?;
@ -108,10 +111,10 @@ pub fn comment_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
CommentBlock { CommentBlock {
source, source: source.into(),
name, name: name.into(),
data: parameters, data: parameters.map(|parameters| Into::<&str>::into(parameters)),
contents, contents: contents.into(),
}, },
)) ))
} }
@ -119,9 +122,9 @@ pub fn comment_block<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn example_block<'r, 's>( pub fn example_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, ExampleBlock<'s>> { ) -> Res<OrgSource<'s>, ExampleBlock<'s>> {
let (remaining, name) = lesser_block_begin("example")(context, input)?; let (remaining, _name) = lesser_block_begin("example")(context, input)?;
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
let (remaining, _nl) = line_ending(remaining)?; let (remaining, _nl) = line_ending(remaining)?;
let lesser_block_end_specialized = lesser_block_end("example"); let lesser_block_end_specialized = lesser_block_end("example");
@ -144,10 +147,10 @@ pub fn example_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
ExampleBlock { ExampleBlock {
source, source: source.into(),
name, name: source.into(),
data: parameters, data: parameters.map(|parameters| Into::<&str>::into(parameters)),
contents, contents: contents.into(),
}, },
)) ))
} }
@ -155,8 +158,8 @@ pub fn example_block<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn export_block<'r, 's>( pub fn export_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, ExportBlock<'s>> { ) -> Res<OrgSource<'s>, ExportBlock<'s>> {
let (remaining, name) = lesser_block_begin("export")(context, input)?; let (remaining, name) = lesser_block_begin("export")(context, input)?;
// https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block. // https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block.
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
@ -181,16 +184,19 @@ pub fn export_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
ExportBlock { ExportBlock {
source, source: source.into(),
name, name: name.into(),
data: parameters, data: parameters.map(|parameters| Into::<&str>::into(parameters)),
contents, contents: contents.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn src_block<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, SrcBlock<'s>> { pub fn src_block<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, SrcBlock<'s>> {
let (remaining, name) = lesser_block_begin("src")(context, input)?; let (remaining, name) = lesser_block_begin("src")(context, input)?;
// https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block. // https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block.
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?; let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
@ -215,29 +221,29 @@ pub fn src_block<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
Ok(( Ok((
remaining, remaining,
SrcBlock { SrcBlock {
source, source: source.into(),
name, name: name.into(),
data: parameters, data: parameters.map(|parameters| Into::<&str>::into(parameters)),
contents, contents: contents.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not(" \t\r\n")(input) is_not(" \t\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn data<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn data<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not("\r\n")(input) is_not("\r\n")(input)
} }
fn lesser_block_end( fn lesser_block_end(
current_name: &str, current_name: &str,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_name_lower = current_name.to_lowercase(); let current_name_lower = current_name.to_lowercase();
move |context: Context, input: &str| { move |context: Context, input: OrgSource<'_>| {
_lesser_block_end(context, input, current_name_lower.as_str()) _lesser_block_end(context, input, current_name_lower.as_str())
} }
} }
@ -245,10 +251,10 @@ fn lesser_block_end(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _lesser_block_end<'r, 's, 'x>( fn _lesser_block_end<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
current_name_lower: &'x str, current_name_lower: &'x str,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, _name, _ws)) = tuple(( let (remaining, (_begin, _name, _ws)) = tuple((
tag_no_case("#+end_"), tag_no_case("#+end_"),
@ -261,9 +267,9 @@ fn _lesser_block_end<'r, 's, 'x>(
fn lesser_block_begin( fn lesser_block_begin(
current_name: &str, current_name: &str,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_name_lower = current_name.to_lowercase(); let current_name_lower = current_name.to_lowercase();
move |context: Context, input: &str| { move |context: Context, input: OrgSource<'_>| {
_lesser_block_begin(context, input, current_name_lower.as_str()) _lesser_block_begin(context, input, current_name_lower.as_str())
} }
} }
@ -271,15 +277,15 @@ fn lesser_block_begin(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _lesser_block_begin<'r, 's, 'x>( fn _lesser_block_begin<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
current_name_lower: &'x str, current_name_lower: &'x str,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name)) = tuple(( let (remaining, (_begin, name)) = tuple((
tag_no_case("#+begin_"), tag_no_case("#+begin_"),
verify(name, |name: &str| { verify(name, |name: &OrgSource<'_>| {
name.to_lowercase().as_str() == current_name_lower Into::<&str>::into(name).to_lowercase().as_str() == current_name_lower
}), }),
))(remaining)?; ))(remaining)?;
Ok((remaining, name)) Ok((remaining, name))

View File

@ -4,56 +4,52 @@ use nom::character::complete::one_of;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::multi::many0; use nom::multi::many0;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::get_current_line_before_position;
use crate::parser::util::get_one_before;
use crate::parser::LineBreak; use crate::parser::LineBreak;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn line_break<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, LineBreak<'s>> { pub fn line_break<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, LineBreak<'s>> {
let (remaining, _) = pre(context, input)?; let (remaining, _) = pre(context, input)?;
let (remaining, _) = tag(r#"\\"#)(remaining)?; let (remaining, _) = tag(r#"\\"#)(remaining)?;
let (remaining, _) = recognize(many0(one_of(" \t")))(remaining)?; let (remaining, _) = recognize(many0(one_of(" \t")))(remaining)?;
let (remaining, _) = line_ending(remaining)?; let (remaining, _) = line_ending(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, LineBreak { source })) Ok((
remaining,
LineBreak {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { fn pre<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let document_root = context.get_document_root().unwrap(); let preceding_character = input.get_preceding_character();
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
match preceding_character { match preceding_character {
// If None, we are at the start of the file // If None, we are at the start of the file
None | Some('\\') => { None | Some('\\') => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid pre character for line break.", "Not a valid pre character for line break.".into(),
)))); ))));
} }
_ => {} _ => {}
}; };
let current_line = get_current_line_before_position(document_root, input);
match current_line { let current_line = input.text_since_line_break();
Some(line) => { let is_non_empty_line = current_line.chars().any(|c| !c.is_whitespace());
let is_non_empty_line = line.chars().any(|c| !c.is_whitespace());
if !is_non_empty_line { if !is_non_empty_line {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid pre line for line break.", "Not a valid pre line for line break.".into(),
)))); ))));
} }
}
None => {
return Err(nom::Err::Error(CustomError::MyError(MyError(
"No preceding line for line break.",
))));
}
}
Ok((input, ())) Ok((input, ()))
} }

View File

@ -30,6 +30,7 @@ mod list;
mod object; mod object;
mod object_parser; mod object_parser;
mod org_macro; mod org_macro;
mod org_source;
mod paragraph; mod paragraph;
mod parser_context; mod parser_context;
mod parser_with_context; mod parser_with_context;

View File

@ -2,6 +2,7 @@ use nom::branch::alt;
use nom::combinator::map; use nom::combinator::map;
use nom::combinator::not; use nom::combinator::not;
use super::org_source::OrgSource;
use super::parser_with_context::parser_with_context; use super::parser_with_context::parser_with_context;
use super::plain_text::plain_text; use super::plain_text::plain_text;
use super::regular_link::regular_link; use super::regular_link::regular_link;
@ -31,8 +32,8 @@ use crate::parser::timestamp::timestamp;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn standard_set_object<'r, 's>( pub fn standard_set_object<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
not(|i| context.check_exit_matcher(i))(input)?; not(|i| context.check_exit_matcher(i))(input)?;
alt(( alt((
@ -90,8 +91,8 @@ pub fn standard_set_object<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn minimal_set_object<'r, 's>( pub fn minimal_set_object<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
not(|i| context.check_exit_matcher(i))(input)?; not(|i| context.check_exit_matcher(i))(input)?;
alt(( alt((
@ -113,8 +114,8 @@ pub fn minimal_set_object<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn any_object_except_plain_text<'r, 's>( pub fn any_object_except_plain_text<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
// Used for exit matchers so this does not check exit matcher condition. // Used for exit matchers so this does not check exit matcher condition.
alt(( alt((
map(parser_with_context!(timestamp)(context), Object::Timestamp), map(parser_with_context!(timestamp)(context), Object::Timestamp),
@ -170,8 +171,8 @@ pub fn any_object_except_plain_text<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn regular_link_description_object_set<'r, 's>( pub fn regular_link_description_object_set<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
// TODO: It can also contain another link, but only when it is a plain or angle link. It can contain square brackets, but not ]] // TODO: It can also contain another link, but only when it is a plain or angle link. It can contain square brackets, but not ]]
alt(( alt((
map( map(

View File

@ -7,6 +7,7 @@ use nom::combinator::verify;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::separated_list0; use nom::multi::separated_list0;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::object::OrgMacro; use crate::parser::object::OrgMacro;
@ -15,7 +16,10 @@ use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn org_macro<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, OrgMacro<'s>> { pub fn org_macro<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgMacro<'s>> {
let (remaining, _) = tag("{{{")(input)?; let (remaining, _) = tag("{{{")(input)?;
let (remaining, macro_name) = org_macro_name(context, remaining)?; let (remaining, macro_name) = org_macro_name(context, remaining)?;
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?; let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
@ -25,15 +29,22 @@ pub fn org_macro<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
Ok(( Ok((
remaining, remaining,
OrgMacro { OrgMacro {
source, source: source.into(),
macro_name, macro_name: macro_name.into(),
macro_args: macro_args.unwrap_or_else(|| Vec::with_capacity(0)), macro_args: macro_args
.unwrap_or_else(|| Vec::with_capacity(0))
.into_iter()
.map(|arg| arg.into())
.collect(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn org_macro_name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn org_macro_name<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = verify(anychar, |c| c.is_alphabetic())(input)?; let (remaining, _) = verify(anychar, |c| c.is_alphabetic())(input)?;
let (remaining, _) = many0(verify(anychar, |c| { let (remaining, _) = many0(verify(anychar, |c| {
c.is_alphanumeric() || *c == '-' || *c == '_' c.is_alphanumeric() || *c == '-' || *c == '_'
@ -43,7 +54,10 @@ fn org_macro_name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn org_macro_args<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec<&'s str>> { fn org_macro_args<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<OrgSource<'s>>> {
let (remaining, _) = tag("(")(input)?; let (remaining, _) = tag("(")(input)?;
let (remaining, args) = let (remaining, args) =
separated_list0(tag(","), parser_with_context!(org_macro_arg)(context))(remaining)?; separated_list0(tag(","), parser_with_context!(org_macro_arg)(context))(remaining)?;
@ -53,7 +67,10 @@ fn org_macro_args<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn org_macro_arg<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn org_macro_arg<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let mut remaining = input; let mut remaining = input;
let mut escaping: bool = false; let mut escaping: bool = false;
loop { loop {

372
src/parser/org_source.rs Normal file
View File

@ -0,0 +1,372 @@
use std::ops::RangeBounds;
use nom::Compare;
use nom::InputIter;
use nom::InputLength;
use nom::InputTake;
use nom::InputTakeAtPosition;
use nom::Offset;
use nom::Slice;
use crate::error::CustomError;
use crate::error::MyError;
#[derive(Debug, Copy, Clone)]
pub struct OrgSource<'s> {
full_source: &'s str,
start: usize,
end: usize, // exclusive
start_of_line: usize,
preceding_character: Option<char>,
}
impl<'s> OrgSource<'s> {
/// Returns a wrapped string that keeps track of values we need for parsing org-mode.
///
/// Only call this on the full original string. Calling this on a substring can result in invalid values.
pub fn new(input: &'s str) -> Self {
OrgSource {
full_source: input,
start: 0,
end: input.len(),
start_of_line: 0,
preceding_character: None,
}
}
/// Get the text since the line break preceding the start of this WrappedInput.
pub fn text_since_line_break(&self) -> &'s str {
&self.full_source[self.start_of_line..self.start]
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn get_preceding_character(&self) -> Option<char> {
self.preceding_character
}
pub fn is_at_start_of_line(&self) -> bool {
self.start == self.start_of_line
}
pub fn get_until(&self, other: OrgSource<'s>) -> OrgSource<'s> {
assert!(other.start >= self.start);
assert!(other.end <= self.end);
self.slice(..(other.start - self.start))
}
}
impl<'s> InputTake for OrgSource<'s> {
fn take(&self, count: usize) -> Self {
self.slice(..count)
}
fn take_split(&self, count: usize) -> (Self, Self) {
(self.slice(count..), self.slice(..count))
}
}
impl<'s, 'o, O: Into<&'o str>> Compare<O> for OrgSource<'s> {
fn compare(&self, t: O) -> nom::CompareResult {
(&self.full_source[self.start..self.end]).compare(t.into())
}
fn compare_no_case(&self, t: O) -> nom::CompareResult {
(&self.full_source[self.start..self.end]).compare_no_case(t.into())
}
}
impl<'s> From<&'s str> for OrgSource<'s> {
fn from(value: &'s str) -> Self {
OrgSource::new(value)
}
}
impl<'s> From<&OrgSource<'s>> for &'s str {
fn from(value: &OrgSource<'s>) -> Self {
&value.full_source[value.start..value.end]
}
}
impl<'s> From<OrgSource<'s>> for &'s str {
fn from(value: OrgSource<'s>) -> Self {
&value.full_source[value.start..value.end]
}
}
impl<'s, R> Slice<R> for OrgSource<'s>
where
R: RangeBounds<usize>,
{
fn slice(&self, range: R) -> Self {
let new_start = match range.start_bound() {
std::ops::Bound::Included(idx) => self.start + idx,
std::ops::Bound::Excluded(idx) => self.start + idx - 1,
std::ops::Bound::Unbounded => self.start,
};
let new_end = match range.end_bound() {
std::ops::Bound::Included(idx) => self.start + idx + 1,
std::ops::Bound::Excluded(idx) => self.start + idx,
std::ops::Bound::Unbounded => self.end,
};
if new_start < self.start {
panic!("Attempted to extend before the start of the WrappedInput.")
}
if new_end > self.end {
panic!("Attempted to extend past the end of the WrappedInput.")
}
let skipped_text = &self.full_source[self.start..new_start];
let start_of_line = skipped_text
.rfind('\n')
.map(|idx| self.start + idx + 1)
.unwrap_or(self.start_of_line);
OrgSource {
full_source: self.full_source,
start: new_start,
end: new_end,
start_of_line,
preceding_character: skipped_text.chars().last(),
}
}
}
impl<'s> std::fmt::Display for OrgSource<'s> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Into::<&str>::into(self).fmt(f)
}
}
impl<'s> InputLength for OrgSource<'s> {
fn input_len(&self) -> usize {
self.end - self.start
}
}
impl<'s> InputIter for OrgSource<'s> {
type Item = <&'s str as InputIter>::Item;
type Iter = <&'s str as InputIter>::Iter;
type IterElem = <&'s str as InputIter>::IterElem;
fn iter_indices(&self) -> Self::Iter {
Into::<&str>::into(self).char_indices()
}
fn iter_elements(&self) -> Self::IterElem {
Into::<&str>::into(self).iter_elements()
}
fn position<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Item) -> bool,
{
Into::<&str>::into(self).position(predicate)
}
fn slice_index(&self, count: usize) -> Result<usize, nom::Needed> {
Into::<&str>::into(self).slice_index(count)
}
}
impl<'s> Offset for OrgSource<'s> {
fn offset(&self, second: &Self) -> usize {
second.start - self.start
}
}
impl<'s> InputTakeAtPosition for OrgSource<'s> {
type Item = <&'s str as InputTakeAtPosition>::Item;
fn split_at_position<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match Into::<&str>::into(self).position(predicate) {
Some(idx) => Ok(self.take_split(idx)),
None => Err(nom::Err::Incomplete(nom::Needed::new(1))),
}
}
fn split_at_position1<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
e: nom::error::ErrorKind,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match Into::<&str>::into(self).position(predicate) {
Some(0) => Err(nom::Err::Error(E::from_error_kind(self.clone(), e))),
Some(idx) => Ok(self.take_split(idx)),
None => Err(nom::Err::Incomplete(nom::Needed::new(1))),
}
}
fn split_at_position_complete<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match self.split_at_position(predicate) {
Err(nom::Err::Incomplete(_)) => Ok(self.take_split(self.input_len())),
res => res,
}
}
fn split_at_position1_complete<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
e: nom::error::ErrorKind,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
let window = Into::<&str>::into(self);
match window.position(predicate) {
Some(0) => Err(nom::Err::Error(E::from_error_kind(self.clone(), e))),
Some(n) => Ok(self.take_split(n)),
None => {
if window.input_len() == 0 {
Err(nom::Err::Error(E::from_error_kind(self.clone(), e)))
} else {
Ok(self.take_split(self.input_len()))
}
}
}
}
}
pub fn convert_error(err: nom::Err<CustomError<OrgSource<'_>>>) -> nom::Err<CustomError<&str>> {
match err {
nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed),
nom::Err::Error(err) => nom::Err::Error(err.into()),
nom::Err::Failure(err) => nom::Err::Failure(err.into()),
}
}
impl<'s> From<CustomError<OrgSource<'s>>> for CustomError<&'s str> {
fn from(value: CustomError<OrgSource<'s>>) -> Self {
match value {
CustomError::MyError(err) => CustomError::MyError(err.into()),
CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind),
}
}
}
impl<'s> From<MyError<OrgSource<'s>>> for MyError<&'s str> {
fn from(value: MyError<OrgSource<'s>>) -> Self {
MyError(value.0.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn range() {
let input = OrgSource::new("foo bar baz");
let output = input.slice(4..7);
assert_eq!(output.to_string(), "bar");
}
#[test]
fn range_to() {
let input = OrgSource::new("foo bar baz");
let output = input.slice(..7);
assert_eq!(output.to_string(), "foo bar");
}
#[test]
fn range_from() {
let input = OrgSource::new("foo bar baz");
let output = input.slice(4..);
assert_eq!(output.to_string(), "bar baz");
}
#[test]
fn full_range() {
let input = OrgSource::new("foo bar baz");
let output = input.slice(..);
assert_eq!(output.to_string(), "foo bar baz");
}
#[test]
fn nested_range() {
let input = OrgSource::new("lorem foo bar baz ipsum");
let first_cut = input.slice(6..17);
let output = first_cut.slice(4..7);
assert_eq!(first_cut.to_string(), "foo bar baz");
assert_eq!(output.to_string(), "bar");
}
#[test]
#[should_panic]
fn out_of_bounds() {
let input = OrgSource::new("lorem foo bar baz ipsum");
input.slice(6..30);
}
#[test]
#[should_panic]
fn out_of_nested_bounds() {
let input = OrgSource::new("lorem foo bar baz ipsum");
let first_cut = input.slice(6..17);
first_cut.slice(4..14);
}
#[test]
fn line_break() {
let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum");
assert_eq!(input.slice(5..).start_of_line, 0);
assert_eq!(input.slice(6..).start_of_line, 6);
assert_eq!(input.slice(6..).slice(10..).start_of_line, 14);
}
#[test]
fn text_since_line_break() {
let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum");
assert_eq!(input.text_since_line_break(), "");
assert_eq!(input.slice(5..).text_since_line_break(), "lorem");
assert_eq!(input.slice(6..).text_since_line_break(), "");
assert_eq!(input.slice(6..).slice(10..).text_since_line_break(), "ba");
}
#[test]
fn preceding_character() {
let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum");
assert_eq!(input.get_preceding_character(), None);
assert_eq!(input.slice(5..).get_preceding_character(), Some('m'));
assert_eq!(input.slice(6..).get_preceding_character(), Some('\n'));
assert_eq!(
input.slice(6..).slice(10..).get_preceding_character(),
Some('a')
);
}
#[test]
fn is_at_start_of_line() {
let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum");
assert_eq!(input.is_at_start_of_line(), true);
assert_eq!(input.slice(5..).is_at_start_of_line(), false);
assert_eq!(input.slice(6..).is_at_start_of_line(), true);
assert_eq!(input.slice(6..).slice(10..).is_at_start_of_line(), false);
}
#[test]
fn preceding_character_unicode() {
let input = OrgSource::new("🧡💛💚💙💜");
assert_eq!(input.get_preceding_character(), None);
assert_eq!(input.slice(8..).get_preceding_character(), Some('💛'));
}
}

View File

@ -7,6 +7,7 @@ use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::lesser_element::Paragraph; use super::lesser_element::Paragraph;
use super::org_source::OrgSource;
use super::util::blank_line; use super::util::blank_line;
use super::util::get_consumed; use super::util::get_consumed;
use super::Context; use super::Context;
@ -21,7 +22,10 @@ use crate::parser::util::exit_matcher_parser;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn paragraph<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Paragraph<'s>> { pub fn paragraph<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Paragraph<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -39,15 +43,23 @@ pub fn paragraph<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Paragraph { source, children })) Ok((
remaining,
Paragraph {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn paragraph_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn paragraph_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let non_paragraph_element_matcher = parser_with_context!(element(false))(context); let non_paragraph_element_matcher = parser_with_context!(element(false))(context);
let start_of_line_matcher = parser_with_context!(start_of_line)(&context);
alt(( alt((
recognize(tuple((start_of_line_matcher, many1(blank_line)))), recognize(tuple((start_of_line, many1(blank_line)))),
recognize(non_paragraph_element_matcher), recognize(non_paragraph_element_matcher),
eof, eof,
))(input) ))(input)
@ -56,22 +68,20 @@ fn paragraph_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::parser_context::ContextElement; use crate::parser::org_source::OrgSource;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::source::Source; use crate::parser::source::Source;
#[test] #[test]
fn two_paragraphs() { fn two_paragraphs() {
let input = "foo bar baz\n\nlorem ipsum"; let input = OrgSource::new("foo bar baz\n\nlorem ipsum");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let paragraph_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let paragraph_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph"); let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph");
let (remaining, second_paragraph) = let (remaining, second_paragraph) =
paragraph_matcher(remaining).expect("Parse second paragraph."); paragraph_matcher(remaining).expect("Parse second paragraph.");
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "foo bar baz\n\n"); assert_eq!(first_paragraph.get_source(), "foo bar baz\n\n");
assert_eq!(second_paragraph.get_source(), "lorem ipsum"); assert_eq!(second_paragraph.get_source(), "lorem ipsum");
} }

View File

@ -5,6 +5,7 @@ use nom::IResult;
use super::list::List; use super::list::List;
use super::list::Node; use super::list::Node;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use super::Object; use super::Object;
use crate::error::CustomError; use crate::error::CustomError;
@ -12,7 +13,8 @@ use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
type Matcher = dyn for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str>; type Matcher =
dyn for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ContextTree<'r, 's> { pub struct ContextTree<'r, 's> {
@ -47,8 +49,8 @@ impl<'r, 's> ContextTree<'r, 's> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn check_exit_matcher( pub fn check_exit_matcher(
&'r self, &'r self,
i: &'s str, i: OrgSource<'s>,
) -> IResult<&'s str, &'s str, CustomError<&'s str>> { ) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError<OrgSource<'s>>> {
// Special check for EOF. We don't just make this a document-level exit matcher since the IgnoreParent ChainBehavior could cause early exit matchers to not run. // Special check for EOF. We don't just make this a document-level exit matcher since the IgnoreParent ChainBehavior could cause early exit matchers to not run.
let at_end_of_file = eof(i); let at_end_of_file = eof(i);
if at_end_of_file.is_ok() { if at_end_of_file.is_ok() {
@ -78,20 +80,9 @@ impl<'r, 's> ContextTree<'r, 's> {
}; };
} }
// TODO: Make this a specific error instead of just a generic MyError // TODO: Make this a specific error instead of just a generic MyError
return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit")))); return Err(nom::Err::Error(CustomError::MyError(MyError(
} "NoExit".into(),
))));
pub fn get_document_root(&self) -> Option<&'s str> {
for current_node in self.iter() {
let context_element = current_node.get_data();
match context_element {
ContextElement::DocumentRoot(body) => {
return Some(body);
}
_ => {}
}
}
None
} }
/// Indicates if elements should consume the whitespace after them. /// Indicates if elements should consume the whitespace after them.
@ -117,11 +108,6 @@ impl<'r, 's> ContextTree<'r, 's> {
#[derive(Debug)] #[derive(Debug)]
pub enum ContextElement<'r, 's> { pub enum ContextElement<'r, 's> {
/// Stores a reference to the entire org-mode document being parsed.
///
/// This is used for look-behind.
DocumentRoot(&'s str),
/// Stores a parser that indicates that children should exit upon matching an exit matcher. /// Stores a parser that indicates that children should exit upon matching an exit matcher.
ExitMatcherNode(ExitMatcherNode<'r>), ExitMatcherNode(ExitMatcherNode<'r>),
Context(&'r str), Context(&'r str),
@ -215,31 +201,31 @@ pub struct ExitMatcherNode<'r> {
#[derive(Debug)] #[derive(Debug)]
pub struct FootnoteReferenceDefinition<'s> { pub struct FootnoteReferenceDefinition<'s> {
pub position: &'s str, pub position: OrgSource<'s>,
pub depth: usize, pub depth: usize,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct CitationBracket<'s> { pub struct CitationBracket<'s> {
pub position: &'s str, pub position: OrgSource<'s>,
pub depth: usize, pub depth: usize,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct BabelHeaderBracket<'s> { pub struct BabelHeaderBracket<'s> {
pub position: &'s str, pub position: OrgSource<'s>,
pub depth: usize, pub depth: usize,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct InlineSourceBlockBracket<'s> { pub struct InlineSourceBlockBracket<'s> {
pub position: &'s str, pub position: OrgSource<'s>,
pub depth: usize, pub depth: usize,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SubscriptSuperscriptBrace<'s> { pub struct SubscriptSuperscriptBrace<'s> {
pub position: &'s str, pub position: OrgSource<'s>,
pub depth: usize, pub depth: usize,
} }

View File

@ -9,6 +9,7 @@ use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -20,11 +21,13 @@ use crate::parser::parser_context::ExitMatcherNode;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::get_one_before;
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS; use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn plain_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainLink<'s>> { pub fn plain_link<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PlainLink<'s>> {
let (remaining, _) = pre(context, input)?; let (remaining, _) = pre(context, input)?;
let (remaining, proto) = protocol(context, remaining)?; let (remaining, proto) = protocol(context, remaining)?;
let (remaining, _separator) = tag(":")(remaining)?; let (remaining, _separator) = tag(":")(remaining)?;
@ -34,19 +37,16 @@ pub fn plain_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
Ok(( Ok((
remaining, remaining,
PlainLink { PlainLink {
source, source: source.into(),
link_type: proto, link_type: proto.into(),
path, path: path.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { fn pre<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let document_root = context.get_document_root().unwrap(); let preceding_character = input.get_preceding_character();
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
match preceding_character { match preceding_character {
// If None, we are at the start of the file which is fine // If None, we are at the start of the file which is fine
None => {} None => {}
@ -54,7 +54,7 @@ fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> {
Some(_) => { Some(_) => {
// Not at start of line, cannot be a heading // Not at start of line, cannot be a heading
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid pre character for plain link.", "Not a valid pre character for plain link.".into(),
)))); ))));
} }
}; };
@ -62,13 +62,16 @@ fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { fn post<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = alt((eof, recognize(none_of(WORD_CONSTITUENT_CHARACTERS))))(input)?; let (remaining, _) = alt((eof, recognize(none_of(WORD_CONSTITUENT_CHARACTERS))))(input)?;
Ok((remaining, ())) Ok((remaining, ()))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn protocol<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { pub fn protocol<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// TODO: This should be defined by org-link-parameters // TODO: This should be defined by org-link-parameters
let (remaining, proto) = alt(( let (remaining, proto) = alt((
alt(( alt((
@ -103,7 +106,10 @@ pub fn protocol<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_plain<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn path_plain<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// TODO: "optionally containing parenthesis-wrapped non-whitespace non-bracket substrings up to a depth of two. The string must end with either a non-punctation non-whitespace character, a forwards slash, or a parenthesis-wrapped substring" // TODO: "optionally containing parenthesis-wrapped non-whitespace non-bracket substrings up to a depth of two. The string must end with either a non-punctation non-whitespace character, a forwards slash, or a parenthesis-wrapped substring"
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
@ -118,6 +124,9 @@ fn path_plain<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_plain_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn path_plain_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(one_of(" \t\r\n()[]<>"))(input) recognize(one_of(" \t\r\n()[]<>"))(input)
} }

View File

@ -15,6 +15,7 @@ use nom::sequence::tuple;
use super::greater_element::PlainList; use super::greater_element::PlainList;
use super::greater_element::PlainListItem; use super::greater_element::PlainListItem;
use super::org_source::OrgSource;
use super::parser_with_context::parser_with_context; use super::parser_with_context::parser_with_context;
use super::util::non_whitespace_character; use super::util::non_whitespace_character;
use super::Context; use super::Context;
@ -32,7 +33,10 @@ use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn plain_list<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainList<'s>> { pub fn plain_list<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PlainList<'s>> {
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::Context("plain list")) .with_additional_node(ContextElement::Context("plain list"))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
@ -76,7 +80,7 @@ pub fn plain_list<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
Some(final_child) => final_child, Some(final_child) => final_child,
None => { None => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Plain lists require at least one element.", "Plain lists require at least one element.".into(),
)))); ))));
} }
}; };
@ -93,7 +97,7 @@ pub fn plain_list<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
Ok(( Ok((
remaining, remaining,
PlainList { PlainList {
source, source: source.into(),
children: children.into_iter().map(|(_start, item)| item).collect(), children: children.into_iter().map(|(_start, item)| item).collect(),
}, },
)) ))
@ -102,25 +106,27 @@ pub fn plain_list<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn plain_list_item<'r, 's>( pub fn plain_list_item<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, PlainListItem<'s>> { ) -> Res<OrgSource<'s>, PlainListItem<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, leading_whitespace) = space0(input)?; let (remaining, leading_whitespace) = space0(input)?;
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09) // It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
let indent_level = leading_whitespace.len(); let indent_level = leading_whitespace.len();
let (remaining, bull) = let (remaining, bull) = verify(bullet, |bull: &OrgSource<'_>| {
verify(bullet, |bull: &str| bull != "*" || indent_level > 0)(remaining)?; Into::<&str>::into(bull) != "*" || indent_level > 0
})(remaining)?;
let maybe_contentless_item: Res<&str, &str> = alt((eof, line_ending))(remaining); let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> =
alt((eof, line_ending))(remaining);
match maybe_contentless_item { match maybe_contentless_item {
Ok((rem, _ws)) => { Ok((rem, _ws)) => {
let source = get_consumed(input, rem); let source = get_consumed(input, rem);
return Ok(( return Ok((
rem, rem,
PlainListItem { PlainListItem {
source, source: source.into(),
indentation: indent_level, indentation: indent_level,
bullet: bull, bullet: bull.into(),
children: Vec::new(), children: Vec::new(),
}, },
)); ));
@ -152,16 +158,16 @@ pub fn plain_list_item<'r, 's>(
return Ok(( return Ok((
remaining, remaining,
PlainListItem { PlainListItem {
source, source: source.into(),
indentation: indent_level, indentation: indent_level,
bullet: bull, bullet: bull.into(),
children, children,
}, },
)); ));
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn bullet<'s>(i: &'s str) -> Res<&'s str, &'s str> { fn bullet<'s>(i: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt(( alt((
tag("*"), tag("*"),
tag("-"), tag("-"),
@ -171,22 +177,29 @@ fn bullet<'s>(i: &'s str) -> Res<&'s str, &'s str> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn counter<'s>(i: &'s str) -> Res<&'s str, &'s str> { fn counter<'s>(i: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((recognize(one_of("abcdefghijklmnopqrstuvwxyz")), digit1))(i) alt((recognize(one_of("abcdefghijklmnopqrstuvwxyz")), digit1))(i)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn plain_list_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn plain_list_end<'r, 's>(
let start_of_line_matcher = parser_with_context!(start_of_line)(context); context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple(( recognize(tuple((
start_of_line_matcher, start_of_line,
verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2), verify(many1(blank_line), |lines: &Vec<OrgSource<'_>>| {
lines.len() >= 2
}),
)))(input) )))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn plain_list_item_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn plain_list_item_end<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
recognize(tuple(( recognize(tuple((
opt(blank_line), opt(blank_line),
parser_with_context!(line_indented_lte)(context), parser_with_context!(line_indented_lte)(context),
@ -194,14 +207,17 @@ fn plain_list_item_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn line_indented_lte<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn line_indented_lte<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_item_indent_level: &usize = let current_item_indent_level: &usize =
get_context_item_indent(context).ok_or(nom::Err::Error(CustomError::MyError(MyError( get_context_item_indent(context).ok_or(nom::Err::Error(CustomError::MyError(MyError(
"Not inside a plain list item", "Not inside a plain list item".into(),
))))?; ))))?;
let matched = recognize(verify( let matched = recognize(verify(
tuple((space0::<&str, _>, non_whitespace_character)), tuple((space0::<OrgSource<'_>, _>, non_whitespace_character)),
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09) // It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
|(_space0, _anychar)| _space0.len() <= *current_item_indent_level, |(_space0, _anychar)| _space0.len() <= *current_item_indent_level,
))(input)?; ))(input)?;
@ -222,67 +238,56 @@ fn get_context_item_indent<'r, 's>(context: Context<'r, 's>) -> Option<&'r usize
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::Source; use crate::parser::Source;
#[test] #[test]
fn plain_list_item_empty() { fn plain_list_item_empty() {
let input = "1."; let input = OrgSource::new("1.");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&document_context);
let (remaining, result) = plain_list_item_matcher(input).unwrap(); let (remaining, result) = plain_list_item_matcher(input).unwrap();
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1."); assert_eq!(result.source, "1.");
} }
#[test] #[test]
fn plain_list_item_simple() { fn plain_list_item_simple() {
let input = "1. foo"; let input = OrgSource::new("1. foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&document_context);
let (remaining, result) = plain_list_item_matcher(input).unwrap(); let (remaining, result) = plain_list_item_matcher(input).unwrap();
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1. foo"); assert_eq!(result.source, "1. foo");
} }
#[test] #[test]
fn plain_list_empty() { fn plain_list_empty() {
let input = "1."; let input = OrgSource::new("1.");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(plain_list)(&document_context);
let (remaining, result) = plain_list_matcher(input).unwrap(); let (remaining, result) = plain_list_matcher(input).unwrap();
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1."); assert_eq!(result.source, "1.");
} }
#[test] #[test]
fn plain_list_simple() { fn plain_list_simple() {
let input = "1. foo"; let input = OrgSource::new("1. foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(plain_list)(&document_context);
let (remaining, result) = plain_list_matcher(input).unwrap(); let (remaining, result) = plain_list_matcher(input).unwrap();
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1. foo"); assert_eq!(result.source, "1. foo");
} }
#[test] #[test]
fn plain_list_cant_start_line_with_asterisk() { fn plain_list_cant_start_line_with_asterisk() {
// Plain lists with an asterisk bullet must be indented or else they would be a headline // Plain lists with an asterisk bullet must be indented or else they would be a headline
let input = "* foo"; let input = OrgSource::new("* foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(plain_list)(&document_context);
let result = plain_list_matcher(input); let result = plain_list_matcher(input);
assert!(result.is_err()); assert!(result.is_err());
} }
@ -290,32 +295,30 @@ mod tests {
#[test] #[test]
fn indented_can_start_line_with_asterisk() { fn indented_can_start_line_with_asterisk() {
// Plain lists with an asterisk bullet must be indented or else they would be a headline // Plain lists with an asterisk bullet must be indented or else they would be a headline
let input = " * foo"; let input = OrgSource::new(" * foo");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(plain_list)(&document_context);
let result = plain_list_matcher(input); let result = plain_list_matcher(input);
assert!(result.is_ok()); assert!(result.is_ok());
} }
#[test] #[test]
fn two_blank_lines_ends_list() { fn two_blank_lines_ends_list() {
let input = r#"1. foo let input = OrgSource::new(
r#"1. foo
2. bar 2. bar
baz baz
3. lorem 3. lorem
ipsum ipsum
"#; "#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, result) = let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully."); plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(remaining, " ipsum\n"); assert_eq!(Into::<&str>::into(remaining), " ipsum\n");
assert_eq!( assert_eq!(
result.get_source(), result.get_source(),
r#"1. foo r#"1. foo
@ -330,18 +333,18 @@ mod tests {
#[test] #[test]
fn two_blank_lines_ends_nested_list() { fn two_blank_lines_ends_nested_list() {
let input = r#"1. foo let input = OrgSource::new(
r#"1. foo
1. bar 1. bar
baz"#; baz"#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, result) = let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully."); plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(remaining, "baz"); assert_eq!(Into::<&str>::into(remaining), "baz");
assert_eq!( assert_eq!(
result.get_source(), result.get_source(),
r#"1. foo r#"1. foo
@ -354,7 +357,8 @@ baz"#;
#[test] #[test]
fn interior_trailing_whitespace() { fn interior_trailing_whitespace() {
let input = r#"1. foo let input = OrgSource::new(
r#"1. foo
bar bar
@ -365,14 +369,13 @@ baz"#;
ipsum ipsum
dolar"#; dolar"#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_list_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_list_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, result) = let (remaining, result) =
plain_list_matcher(input).expect("Should parse the plain list successfully."); plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(remaining, "dolar"); assert_eq!(Into::<&str>::into(remaining), "dolar");
assert_eq!( assert_eq!(
result.get_source(), result.get_source(),
r#"1. foo r#"1. foo

View File

@ -8,6 +8,7 @@ use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::object::PlainText; use super::object::PlainText;
use super::org_source::OrgSource;
use super::radio_link::RematchObject; use super::radio_link::RematchObject;
use super::Context; use super::Context;
use super::Object; use super::Object;
@ -17,7 +18,10 @@ use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn plain_text<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, PlainText<'s>> { pub fn plain_text<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PlainText<'s>> {
let (remaining, source) = recognize(verify( let (remaining, source) = recognize(verify(
many_till( many_till(
anychar, anychar,
@ -29,11 +33,19 @@ pub fn plain_text<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
|(children, _exit_contents)| !children.is_empty(), |(children, _exit_contents)| !children.is_empty(),
))(input)?; ))(input)?;
Ok((remaining, PlainText { source })) Ok((
remaining,
PlainText {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn plain_text_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn plain_text_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(parser_with_context!(any_object_except_plain_text)(context))(input) recognize(parser_with_context!(any_object_except_plain_text)(context))(input)
} }
@ -42,10 +54,12 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
fn rematch_object<'r, 's>( fn rematch_object<'r, 's>(
&'x self, &'x self,
_context: Context<'r, 's>, _context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
map(tag(self.source), |s| { map(tag(self.source), |s| {
Object::PlainText(PlainText { source: s }) Object::PlainText(PlainText {
source: Into::<&str>::into(s),
})
})(input) })(input)
} }
} }
@ -55,20 +69,17 @@ mod tests {
use nom::combinator::map; use nom::combinator::map;
use super::*; use super::*;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::source::Source; use crate::parser::source::Source;
#[test] #[test]
fn plain_text_simple() { fn plain_text_simple() {
let input = "foobarbaz"; let input = OrgSource::new("foobarbaz");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let plain_text_matcher = parser_with_context!(plain_text)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let plain_text_matcher = parser_with_context!(plain_text)(&document_context);
let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap(); let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap();
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_source(), input); assert_eq!(result.get_source(), Into::<&str>::into(input));
} }
} }

View File

@ -9,6 +9,7 @@ use nom::combinator::eof;
use nom::multi::separated_list1; use nom::multi::separated_list1;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::lesser_element::Planning; use crate::parser::lesser_element::Planning;
@ -16,19 +17,27 @@ use crate::parser::util::get_consumed;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn planning<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Planning<'s>> { pub fn planning<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Planning<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _planning_parameters) = separated_list1(space1, planning_parameter)(remaining)?; let (remaining, _planning_parameters) = separated_list1(space1, planning_parameter)(remaining)?;
let (remaining, _trailing_ws) = tuple((space0, alt((line_ending, eof))))(remaining)?; let (remaining, _trailing_ws) = tuple((space0, alt((line_ending, eof))))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Planning { source })) Ok((
remaining,
Planning {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn planning_parameter<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn planning_parameter<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _planning_type) = alt(( let (remaining, _planning_type) = alt((
tag_no_case("DEADLINE"), tag_no_case("DEADLINE"),
tag_no_case("SCHEDULED"), tag_no_case("SCHEDULED"),

View File

@ -12,6 +12,7 @@ use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -32,18 +33,18 @@ use crate::parser::util::start_of_line;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn property_drawer<'r, 's>( pub fn property_drawer<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, PropertyDrawer<'s>> { ) -> Res<OrgSource<'s>, PropertyDrawer<'s>> {
if immediate_in_section(context, "property-drawer") { if immediate_in_section(context, "property-drawer") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
let ( let (
remaining, remaining,
(_start_of_line, _leading_whitespace, _open_tag, _trailing_whitespace, _line_ending), (_start_of_line, _leading_whitespace, _open_tag, _trailing_whitespace, _line_ending),
) = tuple(( ) = tuple((
parser_with_context!(start_of_line)(context), start_of_line,
space0, space0,
tag_no_case(":PROPERTIES:"), tag_no_case(":PROPERTIES:"),
space0, space0,
@ -68,13 +69,22 @@ pub fn property_drawer<'r, 's>(
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, PropertyDrawer { source, children })) Ok((
remaining,
PropertyDrawer {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn property_drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn property_drawer_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple(( recognize(tuple((
parser_with_context!(start_of_line)(context), start_of_line,
space0, space0,
tag_no_case(":end:"), tag_no_case(":end:"),
space0, space0,
@ -85,23 +95,27 @@ fn property_drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn node_property<'r, 's>( fn node_property<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, NodeProperty<'s>> { ) -> Res<OrgSource<'s>, NodeProperty<'s>> {
let (remaining, (_start_of_line, _leading_whitespace, _open_colon, _name, _close_colon)) = let (remaining, (_start_of_line, _leading_whitespace, _open_colon, _name, _close_colon)) =
tuple(( tuple((
parser_with_context!(start_of_line)(context), start_of_line,
space0, space0,
tag(":"), tag(":"),
parser_with_context!(node_property_name)(context), parser_with_context!(node_property_name)(context),
tag(":"), tag(":"),
))(input)?; ))(input)?;
match tuple((space0::<&str, nom::error::Error<&str>>, line_ending))(remaining) { match tuple((
space0::<OrgSource<'_>, nom::error::Error<OrgSource<'_>>>,
line_ending,
))(remaining)
{
Ok((remaining, _ws)) => { Ok((remaining, _ws)) => {
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
NodeProperty { NodeProperty {
source, source: source.into(),
value: None, value: None,
}, },
)) ))
@ -113,8 +127,8 @@ fn node_property<'r, 's>(
Ok(( Ok((
remaining, remaining,
NodeProperty { NodeProperty {
source, source: source.into(),
value: Some(value), value: Some(value.into()),
}, },
)) ))
} }
@ -122,7 +136,10 @@ fn node_property<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn node_property_name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn node_property_name<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -141,7 +158,7 @@ fn node_property_name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn node_property_name_end<'r, 's>( fn node_property_name_end<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((tag("+:"), tag(":")))(input) alt((tag("+:"), tag(":")))(input)
} }

View File

@ -5,6 +5,7 @@ use nom::character::complete::space0;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use super::Object; use super::Object;
use crate::error::CustomError; use crate::error::CustomError;
@ -21,7 +22,10 @@ use crate::parser::RadioLink;
use crate::parser::RadioTarget; use crate::parser::RadioTarget;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn radio_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, RadioLink<'s>> { pub fn radio_link<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, RadioLink<'s>> {
let radio_targets = context let radio_targets = context
.iter() .iter()
.filter_map(|context_element| match context_element.get_data() { .filter_map(|context_element| match context_element.get_data() {
@ -37,14 +41,14 @@ pub fn radio_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
return Ok(( return Ok((
remaining, remaining,
RadioLink { RadioLink {
source, source: source.into(),
children: rematched_target, children: rematched_target,
}, },
)); ));
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::MyError(MyError(
"NoRadioLink", "NoRadioLink".into(),
)))) ))))
} }
@ -52,8 +56,8 @@ pub fn radio_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
pub fn rematch_target<'x, 'r, 's>( pub fn rematch_target<'x, 'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
target: &'x Vec<Object<'x>>, target: &'x Vec<Object<'x>>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let mut remaining = input; let mut remaining = input;
let mut new_matches = Vec::with_capacity(target.len()); let mut new_matches = Vec::with_capacity(target.len());
for original_object in target { for original_object in target {
@ -71,7 +75,7 @@ pub fn rematch_target<'x, 'r, 's>(
} }
_ => { _ => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"OnlyMinimalSetObjectsAllowed", "OnlyMinimalSetObjectsAllowed".into(),
)))); ))));
} }
}; };
@ -82,8 +86,8 @@ pub fn rematch_target<'x, 'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn radio_target<'r, 's>( pub fn radio_target<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, RadioTarget<'s>> { ) -> Res<OrgSource<'s>, RadioTarget<'s>> {
let (remaining, _opening) = tag("<<<")(input)?; let (remaining, _opening) = tag("<<<")(input)?;
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
@ -102,11 +106,20 @@ pub fn radio_target<'r, 's>(
let (remaining, _closing) = tag(">>>")(remaining)?; let (remaining, _closing) = tag(">>>")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?; let (remaining, _trailing_whitespace) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, RadioTarget { source, children })) Ok((
remaining,
RadioTarget {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn radio_target_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn radio_target_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((tag("<"), tag(">"), line_ending))(input) alt((tag("<"), tag(">"), line_ending))(input)
} }
@ -114,8 +127,8 @@ pub trait RematchObject<'x> {
fn rematch_object<'r, 's>( fn rematch_object<'r, 's>(
&'x self, &'x self,
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>>; ) -> Res<OrgSource<'s>, Object<'s>>;
} }
#[cfg(test)] #[cfg(test)]
@ -131,11 +144,10 @@ mod tests {
#[test] #[test]
fn plain_text_radio_target() { fn plain_text_radio_target() {
let input = "foo bar baz"; let input = OrgSource::new("foo bar baz");
let radio_target_match = vec![Object::PlainText(PlainText { source: "bar" })]; let radio_target_match = vec![Object::PlainText(PlainText { source: "bar" })];
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = initial_context let document_context = initial_context
.with_additional_node(ContextElement::DocumentRoot(input))
.with_additional_node(ContextElement::RadioTarget(vec![&radio_target_match])); .with_additional_node(ContextElement::RadioTarget(vec![&radio_target_match]));
let paragraph_matcher = parser_with_context!(element(true))(&document_context); let paragraph_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph"); let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph");
@ -143,7 +155,7 @@ mod tests {
crate::parser::Element::Paragraph(paragraph) => paragraph, crate::parser::Element::Paragraph(paragraph) => paragraph,
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "foo bar baz"); assert_eq!(first_paragraph.get_source(), "foo bar baz");
assert_eq!(first_paragraph.children.len(), 3); assert_eq!(first_paragraph.children.len(), 3);
assert_eq!( assert_eq!(
@ -160,22 +172,22 @@ mod tests {
#[test] #[test]
fn bold_radio_target() { fn bold_radio_target() {
let input = "foo *bar* baz"; let input = OrgSource::new("foo *bar* baz");
let radio_target_match = vec![Object::Bold(Bold { let radio_target_match = vec![Object::Bold(Bold {
source: "*bar*", source: "*bar*",
children: vec![Object::PlainText(PlainText { source: "bar" })], children: vec![Object::PlainText(PlainText { source: "bar" })],
})]; })];
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = initial_context let document_context = initial_context
.with_additional_node(ContextElement::DocumentRoot(input))
.with_additional_node(ContextElement::RadioTarget(vec![&radio_target_match])); .with_additional_node(ContextElement::RadioTarget(vec![&radio_target_match]));
let paragraph_matcher = parser_with_context!(element(true))(&document_context); let paragraph_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph"); let (remaining, first_paragraph) =
paragraph_matcher(input.into()).expect("Parse first paragraph");
let first_paragraph = match first_paragraph { let first_paragraph = match first_paragraph {
crate::parser::Element::Paragraph(paragraph) => paragraph, crate::parser::Element::Paragraph(paragraph) => paragraph,
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "foo *bar* baz"); assert_eq!(first_paragraph.get_source(), "foo *bar* baz");
assert_eq!(first_paragraph.children.len(), 3); assert_eq!(first_paragraph.children.len(), 3);
assert_eq!( assert_eq!(

View File

@ -7,6 +7,7 @@ use nom::character::complete::space0;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::parser_with_context::parser_with_context; use super::parser_with_context::parser_with_context;
use super::util::get_consumed; use super::util::get_consumed;
use super::Context; use super::Context;
@ -22,8 +23,8 @@ use crate::parser::util::exit_matcher_parser;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn regular_link<'r, 's>( pub fn regular_link<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, RegularLink<'s>> { ) -> Res<OrgSource<'s>, RegularLink<'s>> {
alt(( alt((
parser_with_context!(regular_link_without_description)(context), parser_with_context!(regular_link_without_description)(context),
parser_with_context!(regular_link_with_description)(context), parser_with_context!(regular_link_with_description)(context),
@ -33,21 +34,26 @@ pub fn regular_link<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn regular_link_without_description<'r, 's>( pub fn regular_link_without_description<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, RegularLink<'s>> { ) -> Res<OrgSource<'s>, RegularLink<'s>> {
let (remaining, _opening_bracket) = tag("[[")(input)?; let (remaining, _opening_bracket) = tag("[[")(input)?;
let (remaining, _path) = pathreg(context, remaining)?; let (remaining, _path) = pathreg(context, remaining)?;
let (remaining, _closing_bracket) = tag("]]")(remaining)?; let (remaining, _closing_bracket) = tag("]]")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?; let (remaining, _trailing_whitespace) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, RegularLink { source })) Ok((
remaining,
RegularLink {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn regular_link_with_description<'r, 's>( pub fn regular_link_with_description<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, RegularLink<'s>> { ) -> Res<OrgSource<'s>, RegularLink<'s>> {
let (remaining, _opening_bracket) = tag("[[")(input)?; let (remaining, _opening_bracket) = tag("[[")(input)?;
let (remaining, _path) = pathreg(context, remaining)?; let (remaining, _path) = pathreg(context, remaining)?;
let (remaining, _closing_bracket) = tag("][")(remaining)?; let (remaining, _closing_bracket) = tag("][")(remaining)?;
@ -55,11 +61,19 @@ pub fn regular_link_with_description<'r, 's>(
let (remaining, _closing_bracket) = tag("]]")(remaining)?; let (remaining, _closing_bracket) = tag("]]")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?; let (remaining, _trailing_whitespace) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, RegularLink { source })) Ok((
remaining,
RegularLink {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn pathreg<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { pub fn pathreg<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, path) = escaped( let (remaining, path) = escaped(
take_till1(|c| match c { take_till1(|c| match c {
'\\' | ']' => true, '\\' | ']' => true,
@ -74,8 +88,8 @@ pub fn pathreg<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn description<'r, 's>( pub fn description<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -93,6 +107,9 @@ pub fn description<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn description_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn description_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag("]]")(input) tag("]]")(input)
} }

View File

@ -16,6 +16,9 @@ use nom::sequence::delimited;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::convert_error;
use super::org_source::OrgSource;
use super::util::get_consumed;
use crate::error::Res; use crate::error::Res;
#[derive(Debug)] #[derive(Debug)]
@ -28,9 +31,7 @@ pub enum Token<'s> {
#[derive(Debug)] #[derive(Debug)]
pub struct TextWithProperties<'s> { pub struct TextWithProperties<'s> {
#[allow(dead_code)]
pub text: &'s str, pub text: &'s str,
#[allow(dead_code)]
pub properties: Vec<Token<'s>>, pub properties: Vec<Token<'s>>,
} }
@ -135,24 +136,25 @@ impl<'s> Token<'s> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { pub fn sexp_with_padding<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = multispace0(input)?; let (remaining, _) = multispace0(input)?;
let (remaining, tkn) = token(remaining)?; let remaining = OrgSource::new(remaining);
let (remaining, tkn) = token(remaining).map(|(rem, out)| (Into::<&str>::into(rem), out)).map_err(convert_error)?;
let (remaining, _) = multispace0(remaining)?; let (remaining, _) = multispace0(remaining)?;
Ok((remaining, tkn)) Ok((remaining, tkn))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn sexp<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { pub fn sexp<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, tkn) = token(input)?; let (remaining, tkn) = token(input)?;
Ok((remaining, tkn)) Ok((remaining, tkn))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn token<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn token<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
alt((list, vector, atom))(input) alt((list, vector, atom))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn list<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, _) = tag("(")(input)?; let (remaining, _) = tag("(")(input)?;
let (remaining, children) = delimited( let (remaining, children) = delimited(
multispace0, multispace0,
@ -164,7 +166,7 @@ fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn vector<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn vector<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let (remaining, children) = delimited( let (remaining, children) = delimited(
multispace0, multispace0,
@ -176,7 +178,7 @@ fn vector<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
not(peek(one_of(")]")))(input)?; not(peek(one_of(")]")))(input)?;
alt(( alt((
text_with_properties, text_with_properties,
@ -187,16 +189,16 @@ fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn unquoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn unquoted_atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, body) = take_till1(|c| match c { let (remaining, body) = take_till1(|c| match c {
' ' | '\t' | '\r' | '\n' | ')' | ']' => true, ' ' | '\t' | '\r' | '\n' | ')' | ']' => true,
_ => false, _ => false,
})(input)?; })(input)?;
Ok((remaining, Token::Atom(body))) Ok((remaining, Token::Atom(body.into())))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn quoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn quoted_atom<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, _) = tag(r#"""#)(input)?; let (remaining, _) = tag(r#"""#)(input)?;
let (remaining, _) = escaped( let (remaining, _) = escaped(
take_till1(|c| match c { take_till1(|c| match c {
@ -208,11 +210,11 @@ fn quoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
)(remaining)?; )(remaining)?;
let (remaining, _) = tag(r#"""#)(remaining)?; let (remaining, _) = tag(r#"""#)(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Token::Atom(source))) Ok((remaining, Token::Atom(source.into())))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn hash_notation<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn hash_notation<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, _) = tag("#<")(input)?; let (remaining, _) = tag("#<")(input)?;
let (remaining, _body) = take_till1(|c| match c { let (remaining, _body) = take_till1(|c| match c {
'>' => true, '>' => true,
@ -220,10 +222,10 @@ fn hash_notation<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
})(remaining)?; })(remaining)?;
let (remaining, _) = tag(">")(remaining)?; let (remaining, _) = tag(">")(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Token::Atom(source))) Ok((remaining, Token::Atom(source.into())))
} }
fn text_with_properties<'s>(input: &'s str) -> Res<&'s str, Token<'s>> { fn text_with_properties<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Token<'s>> {
let (remaining, _) = tag("#(")(input)?; let (remaining, _) = tag("#(")(input)?;
let (remaining, (text, props)) = delimited( let (remaining, (text, props)) = delimited(
multispace0, multispace0,
@ -246,25 +248,6 @@ fn text_with_properties<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
)) ))
} }
/// Get a slice of the string that was consumed in a parser using the original input to the parser and the remaining input after the parser.
fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
assert!(is_slice_of(input, remaining));
let source = {
let offset = remaining.as_ptr() as usize - input.as_ptr() as usize;
&input[..offset]
};
source
}
/// Check if the child string slice is a slice of the parent string slice.
fn is_slice_of(parent: &str, child: &str) -> bool {
let parent_start = parent.as_ptr() as usize;
let parent_end = parent_start + parent.len();
let child_start = child.as_ptr() as usize;
let child_end = child_start + child.len();
child_start >= parent_start && child_end <= parent_end
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -4,6 +4,7 @@ use nom::character::complete::space0;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
@ -12,8 +13,8 @@ use crate::parser::StatisticsCookie;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn statistics_cookie<'r, 's>( pub fn statistics_cookie<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, StatisticsCookie<'s>> { ) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
alt(( alt((
parser_with_context!(percent_statistics_cookie)(context), parser_with_context!(percent_statistics_cookie)(context),
parser_with_context!(fraction_statistics_cookie)(context), parser_with_context!(fraction_statistics_cookie)(context),
@ -23,19 +24,24 @@ pub fn statistics_cookie<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn percent_statistics_cookie<'r, 's>( pub fn percent_statistics_cookie<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, StatisticsCookie<'s>> { ) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
let (remaining, source) = let (remaining, source) =
recognize(tuple((tag("["), nom::character::complete::u64, tag("%]"))))(input)?; recognize(tuple((tag("["), nom::character::complete::u64, tag("%]"))))(input)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
Ok((remaining, StatisticsCookie { source })) Ok((
remaining,
StatisticsCookie {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn fraction_statistics_cookie<'r, 's>( pub fn fraction_statistics_cookie<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, StatisticsCookie<'s>> { ) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
let (remaining, source) = recognize(tuple(( let (remaining, source) = recognize(tuple((
tag("["), tag("["),
nom::character::complete::u64, nom::character::complete::u64,
@ -44,5 +50,10 @@ pub fn fraction_statistics_cookie<'r, 's>(
tag("]"), tag("]"),
)))(input)?; )))(input)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
Ok((remaining, StatisticsCookie { source })) Ok((
remaining,
StatisticsCookie {
source: source.into(),
},
))
} }

View File

@ -11,6 +11,7 @@ use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use super::Object; use super::Object;
use crate::error::CustomError; use crate::error::CustomError;
@ -24,46 +25,55 @@ use crate::parser::parser_context::SubscriptSuperscriptBrace;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::get_one_before;
use crate::parser::Subscript; use crate::parser::Subscript;
use crate::parser::Superscript; use crate::parser::Superscript;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn subscript<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Subscript<'s>> { pub fn subscript<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Subscript<'s>> {
// We check for the underscore first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily. // We check for the underscore first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily.
let (remaining, _) = tag("_")(input)?; let (remaining, _) = tag("_")(input)?;
pre(context, input)?; pre(context, input)?;
let (remaining, _body) = script_body(context, remaining)?; let (remaining, _body) = script_body(context, remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Subscript { source })) Ok((
remaining,
Subscript {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn superscript<'r, 's>( pub fn superscript<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Superscript<'s>> { ) -> Res<OrgSource<'s>, Superscript<'s>> {
// We check for the circumflex first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily. // We check for the circumflex first before checking the pre-character as a minor optimization to avoid walking up the context tree to find the document root unnecessarily.
let (remaining, _) = tag("^")(input)?; let (remaining, _) = tag("^")(input)?;
pre(context, input)?; pre(context, input)?;
let (remaining, _body) = script_body(context, remaining)?; let (remaining, _body) = script_body(context, remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Superscript { source })) Ok((
remaining,
Superscript {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { fn pre<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let document_root = context.get_document_root().unwrap(); let preceding_character = input.get_preceding_character();
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
match preceding_character { match preceding_character {
Some(c) if !c.is_whitespace() => {} Some(c) if !c.is_whitespace() => {}
_ => { _ => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Must be preceded by a non-whitespace character.", "Must be preceded by a non-whitespace character.".into(),
)))); ))));
} }
}; };
@ -77,27 +87,36 @@ enum ScriptBody<'s> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_body<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ScriptBody<'s>> { fn script_body<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, ScriptBody<'s>> {
alt(( alt((
map(parser_with_context!(script_asterisk)(context), |body| { map(parser_with_context!(script_asterisk)(context), |body| {
ScriptBody::Braceless(body) ScriptBody::Braceless(body.into())
}), }),
map(parser_with_context!(script_alphanum)(context), |body| { map(parser_with_context!(script_alphanum)(context), |body| {
ScriptBody::Braceless(body) ScriptBody::Braceless(body.into())
}), }),
map(parser_with_context!(script_with_braces)(context), |body| { map(parser_with_context!(script_with_braces)(context), |body| {
ScriptBody::WithBraces(body) ScriptBody::WithBraces(body.into())
}), }),
))(input) ))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_asterisk<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn script_asterisk<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag("*")(input) tag("*")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_alphanum<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn script_alphanum<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _sign) = opt(recognize(one_of("+-")))(input)?; let (remaining, _sign) = opt(recognize(one_of("+-")))(input)?;
let (remaining, _script) = many_till( let (remaining, _script) = many_till(
parser_with_context!(script_alphanum_character)(context), parser_with_context!(script_alphanum_character)(context),
@ -110,8 +129,8 @@ fn script_alphanum<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_alphanum_character<'r, 's>( fn script_alphanum_character<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(verify(anychar, |c| { recognize(verify(anychar, |c| {
c.is_alphanumeric() || r#",.\"#.contains(*c) c.is_alphanumeric() || r#",.\"#.contains(*c)
}))(input) }))(input)
@ -120,8 +139,8 @@ fn script_alphanum_character<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn end_script_alphanum_character<'r, 's>( fn end_script_alphanum_character<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, final_char) = recognize(verify(anychar, |c| c.is_alphanumeric()))(input)?; let (remaining, final_char) = recognize(verify(anychar, |c| c.is_alphanumeric()))(input)?;
peek(not(parser_with_context!(script_alphanum_character)( peek(not(parser_with_context!(script_alphanum_character)(
context, context,
@ -132,13 +151,13 @@ fn end_script_alphanum_character<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_with_braces<'r, 's>( fn script_with_braces<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let (remaining, _) = tag("{")(input)?; let (remaining, _) = tag("{")(input)?;
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::SubscriptSuperscriptBrace( .with_additional_node(ContextElement::SubscriptSuperscriptBrace(
SubscriptSuperscriptBrace { SubscriptSuperscriptBrace {
position: remaining, position: remaining.into(),
depth: 0, depth: 0,
}, },
)) ))
@ -159,13 +178,13 @@ fn script_with_braces<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn script_with_braces_end<'r, 's>( fn script_with_braces_end<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a subscript or superscript."); .expect("This function should only be called from inside a subscript or superscript.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'{' => { '{' => {
current_depth += 1; current_depth += 1;
@ -180,13 +199,13 @@ fn script_with_braces_end<'r, 's>(
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("}")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("}")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
} }
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid end for subscript or superscript.", "Not a valid end for subscript or superscript.".into(),
)))); ))));
} }

View File

@ -12,6 +12,7 @@ use nom::multi::many1;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@ -31,8 +32,11 @@ use crate::parser::Table;
/// ///
/// This is not the table.el style. /// This is not the table.el style.
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn org_mode_table<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Table<'s>> { pub fn org_mode_table<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Table<'s>> {
start_of_line(input)?;
peek(tuple((space0, tag("|"))))(input)?; peek(tuple((space0, tag("|"))))(input)?;
let parser_context = context let parser_context = context
@ -52,20 +56,29 @@ pub fn org_mode_table<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&
// TODO: Consume trailing formulas // TODO: Consume trailing formulas
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Table { source, children })) Ok((
remaining,
Table {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn table_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn table_end<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
recognize(tuple((space0, not(tag("|")))))(input) recognize(tuple((space0, not(tag("|")))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn org_mode_table_row<'r, 's>( pub fn org_mode_table_row<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, TableRow<'s>> { ) -> Res<OrgSource<'s>, TableRow<'s>> {
alt(( alt((
parser_with_context!(org_mode_table_row_rule)(context), parser_with_context!(org_mode_table_row_rule)(context),
parser_with_context!(org_mode_table_row_regular)(context), parser_with_context!(org_mode_table_row_regular)(context),
@ -75,15 +88,15 @@ pub fn org_mode_table_row<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn org_mode_table_row_rule<'r, 's>( pub fn org_mode_table_row_rule<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, TableRow<'s>> { ) -> Res<OrgSource<'s>, TableRow<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _) = tuple((space0, tag("|-"), is_not("\r\n"), line_ending))(input)?; let (remaining, _) = tuple((space0, tag("|-"), is_not("\r\n"), line_ending))(input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
TableRow { TableRow {
source, source: source.into(),
children: Vec::new(), children: Vec::new(),
}, },
)) ))
@ -92,22 +105,28 @@ pub fn org_mode_table_row_rule<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn org_mode_table_row_regular<'r, 's>( pub fn org_mode_table_row_regular<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, TableRow<'s>> { ) -> Res<OrgSource<'s>, TableRow<'s>> {
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _) = tuple((space0, tag("|")))(input)?; let (remaining, _) = tuple((space0, tag("|")))(input)?;
let (remaining, children) = let (remaining, children) =
many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?; many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?;
let (remaining, _tail) = recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?; let (remaining, _tail) = recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, TableRow { source, children })) Ok((
remaining,
TableRow {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn org_mode_table_cell<'r, 's>( pub fn org_mode_table_cell<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, TableCell<'s>> { ) -> Res<OrgSource<'s>, TableCell<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -118,29 +137,37 @@ pub fn org_mode_table_cell<'r, 's>(
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, (children, _exit_contents)) = verify( let (remaining, (children, _exit_contents)) = verify(
many_till(table_cell_set_object_matcher, exit_matcher), many_till(table_cell_set_object_matcher, exit_matcher),
|(children, exit_contents)| !children.is_empty() || exit_contents.ends_with("|"), |(children, exit_contents)| {
!children.is_empty() || Into::<&str>::into(exit_contents).ends_with("|")
},
)(input)?; )(input)?;
let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?; let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, TableCell { source, children })) Ok((
remaining,
TableCell {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn org_mode_table_cell_end<'r, 's>( fn org_mode_table_cell_end<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input) recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn table_cell_set_object<'r, 's>( pub fn table_cell_set_object<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
not(|i| context.check_exit_matcher(i))(input)?; not(|i| context.check_exit_matcher(i))(input)?;
parser_with_context!(minimal_set_object)(context)(input) parser_with_context!(minimal_set_object)(context)(input)

View File

@ -7,6 +7,7 @@ use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -17,11 +18,13 @@ use crate::parser::parser_context::ExitMatcherNode;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::get_one_before;
use crate::parser::Target; use crate::parser::Target;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn target<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Target<'s>> { pub fn target<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Target<'s>> {
let (remaining, _) = tag("<<")(input)?; let (remaining, _) = tag("<<")(input)?;
let (remaining, _) = peek(verify(anychar, |c| { let (remaining, _) = peek(verify(anychar, |c| {
!c.is_whitespace() && !"<>\n".contains(*c) !c.is_whitespace() && !"<>\n".contains(*c)
@ -37,24 +40,30 @@ pub fn target<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
parser_with_context!(exit_matcher_parser)(&parser_context), parser_with_context!(exit_matcher_parser)(&parser_context),
))(remaining)?; ))(remaining)?;
let document_root = context.get_document_root().unwrap(); let preceding_character = remaining
let preceding_character = get_one_before(document_root, remaining) .get_preceding_character()
.map(|slice| slice.chars().next())
.flatten()
.expect("We cannot be at the start of the file because we are inside a target."); .expect("We cannot be at the start of the file because we are inside a target.");
if preceding_character.is_whitespace() { if preceding_character.is_whitespace() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Targets cannot end with whitespace.", "Targets cannot end with whitespace.".into(),
)))); ))));
} }
let (remaining, _) = tag(">>")(remaining)?; let (remaining, _) = tag(">>")(remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Target { source })) Ok((
remaining,
Target {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn target_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn target_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(one_of("<>\n"))(input) recognize(one_of("<>\n"))(input)
} }

View File

@ -15,6 +15,7 @@ use nom::sequence::terminated;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
use tracing::span; use tracing::span;
use super::org_source::OrgSource;
use super::radio_link::RematchObject; use super::radio_link::RematchObject;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
@ -28,7 +29,6 @@ use crate::parser::parser_with_context::parser_with_context;
use crate::parser::radio_link::rematch_target; use crate::parser::radio_link::rematch_target;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::get_one_before;
use crate::parser::util::preceded_by_whitespace; use crate::parser::util::preceded_by_whitespace;
use crate::parser::Bold; use crate::parser::Bold;
use crate::parser::Code; use crate::parser::Code;
@ -39,7 +39,10 @@ use crate::parser::Underline;
use crate::parser::Verbatim; use crate::parser::Verbatim;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn text_markup<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Object<'s>> { pub fn text_markup<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Object<'s>> {
alt(( alt((
map(parser_with_context!(bold)(context), Object::Bold), map(parser_with_context!(bold)(context), Object::Bold),
map(parser_with_context!(italic)(context), Object::Italic), map(parser_with_context!(italic)(context), Object::Italic),
@ -54,73 +57,126 @@ pub fn text_markup<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn bold<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Bold<'s>> { pub fn bold<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Bold<'s>> {
let text_markup_object_specialized = text_markup_object("*"); let text_markup_object_specialized = text_markup_object("*");
let (remaining, children) = text_markup_object_specialized(context, input)?; let (remaining, children) = text_markup_object_specialized(context, input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Bold { source, children })) Ok((
remaining,
Bold {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn italic<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Italic<'s>> { pub fn italic<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Italic<'s>> {
let text_markup_object_specialized = text_markup_object("/"); let text_markup_object_specialized = text_markup_object("/");
let (remaining, children) = text_markup_object_specialized(context, input)?; let (remaining, children) = text_markup_object_specialized(context, input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Italic { source, children })) Ok((
remaining,
Italic {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn underline<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Underline<'s>> { pub fn underline<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Underline<'s>> {
let text_markup_object_specialized = text_markup_object("_"); let text_markup_object_specialized = text_markup_object("_");
let (remaining, children) = text_markup_object_specialized(context, input)?; let (remaining, children) = text_markup_object_specialized(context, input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Underline { source, children })) Ok((
remaining,
Underline {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn strike_through<'r, 's>( pub fn strike_through<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, StrikeThrough<'s>> { ) -> Res<OrgSource<'s>, StrikeThrough<'s>> {
let text_markup_object_specialized = text_markup_object("+"); let text_markup_object_specialized = text_markup_object("+");
let (remaining, children) = text_markup_object_specialized(context, input)?; let (remaining, children) = text_markup_object_specialized(context, input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, StrikeThrough { source, children })) Ok((
remaining,
StrikeThrough {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn verbatim<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Verbatim<'s>> { pub fn verbatim<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Verbatim<'s>> {
let text_markup_string_specialized = text_markup_string("="); let text_markup_string_specialized = text_markup_string("=");
let (remaining, contents) = text_markup_string_specialized(context, input)?; let (remaining, contents) = text_markup_string_specialized(context, input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Verbatim { source, contents })) Ok((
remaining,
Verbatim {
source: source.into(),
contents: contents.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn code<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Code<'s>> { pub fn code<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Code<'s>> {
let text_markup_string_specialized = text_markup_string("~"); let text_markup_string_specialized = text_markup_string("~");
let (remaining, contents) = text_markup_string_specialized(context, input)?; let (remaining, contents) = text_markup_string_specialized(context, input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Code { source, contents })) Ok((
remaining,
Code {
source: source.into(),
contents: contents.into(),
},
))
} }
fn text_markup_object( fn text_markup_object(
marker_symbol: &str, marker_symbol: &str,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, Vec<Object<'s>>> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let marker_symbol = marker_symbol.to_owned(); let marker_symbol = marker_symbol.to_owned();
move |context: Context, input: &str| _text_markup_object(context, input, marker_symbol.as_str()) move |context: Context, input: OrgSource<'_>| {
_text_markup_object(context, input, marker_symbol.as_str())
}
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _text_markup_object<'r, 's, 'x>( fn _text_markup_object<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
marker_symbol: &'x str, marker_symbol: &'x str,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let (remaining, _) = pre(context, input)?; let (remaining, _) = pre(context, input)?;
let (remaining, open) = tag(marker_symbol)(remaining)?; let (remaining, open) = tag(marker_symbol)(remaining)?;
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?; let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
let text_markup_end_specialized = text_markup_end(open); let text_markup_end_specialized = text_markup_end(open.into());
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -142,7 +198,7 @@ fn _text_markup_object<'r, 's, 'x>(
let _enter = span.enter(); let _enter = span.enter();
if exit_matcher_parser(context, remaining).is_ok() { if exit_matcher_parser(context, remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Parent exit matcher is triggering.", "Parent exit matcher is triggering.".into(),
)))); ))));
} }
} }
@ -154,21 +210,23 @@ fn _text_markup_object<'r, 's, 'x>(
fn text_markup_string( fn text_markup_string(
marker_symbol: &str, marker_symbol: &str,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let marker_symbol = marker_symbol.to_owned(); let marker_symbol = marker_symbol.to_owned();
move |context: Context, input: &str| _text_markup_string(context, input, marker_symbol.as_str()) move |context: Context, input: OrgSource<'_>| {
_text_markup_string(context, input, marker_symbol.as_str())
}
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _text_markup_string<'r, 's, 'x>( fn _text_markup_string<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
marker_symbol: &'x str, marker_symbol: &'x str,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = pre(context, input)?; let (remaining, _) = pre(context, input)?;
let (remaining, open) = tag(marker_symbol)(remaining)?; let (remaining, open) = tag(marker_symbol)(remaining)?;
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?; let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
let text_markup_end_specialized = text_markup_end(open); let text_markup_end_specialized = text_markup_end(open.into());
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -190,7 +248,7 @@ fn _text_markup_string<'r, 's, 'x>(
let _enter = span.enter(); let _enter = span.enter();
if exit_matcher_parser(context, remaining).is_ok() { if exit_matcher_parser(context, remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Parent exit matcher is triggering.", "Parent exit matcher is triggering.".into(),
)))); ))));
} }
} }
@ -201,11 +259,8 @@ fn _text_markup_string<'r, 's, 'x>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { pub fn pre<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let document_root = context.get_document_root().unwrap(); let preceding_character = input.get_preceding_character();
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
match preceding_character { match preceding_character {
// If None, we are at the start of the file which is technically the beginning of a line. // If None, we are at the start of the file which is technically the beginning of a line.
None | Some('\r') | Some('\n') | Some(' ') | Some('\t') | Some('-') | Some('(') None | Some('\r') | Some('\n') | Some(' ') | Some('\t') | Some('-') | Some('(')
@ -213,7 +268,7 @@ pub fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()>
Some(_) => { Some(_) => {
// Not at start of line, cannot be a heading // Not at start of line, cannot be a heading
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not a valid pre character for text markup.", "Not a valid pre character for text markup.".into(),
)))); ))));
} }
}; };
@ -221,25 +276,27 @@ pub fn pre<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()>
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn post<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { pub fn post<'r, 's>(context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\">")), line_ending))(input)?; let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\">")), line_ending))(input)?;
Ok((remaining, ())) Ok((remaining, ()))
} }
fn text_markup_end( fn text_markup_end(
marker_symbol: &str, marker_symbol: &str,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, &'s str> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let marker_symbol = marker_symbol.to_owned(); let marker_symbol = marker_symbol.to_owned();
move |context: Context, input: &str| _text_markup_end(context, input, marker_symbol.as_str()) move |context: Context, input: OrgSource<'_>| {
_text_markup_end(context, input, marker_symbol.as_str())
}
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _text_markup_end<'r, 's, 'x>( fn _text_markup_end<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
marker_symbol: &'x str, marker_symbol: &'x str,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
not(parser_with_context!(preceded_by_whitespace)(context))(input)?; not(preceded_by_whitespace)(input)?;
let (remaining, _marker) = terminated( let (remaining, _marker) = terminated(
tag(marker_symbol), tag(marker_symbol),
peek(parser_with_context!(post)(context)), peek(parser_with_context!(post)(context)),
@ -253,26 +310,32 @@ impl<'x> RematchObject<'x> for Bold<'x> {
fn rematch_object<'r, 's>( fn rematch_object<'r, 's>(
&'x self, &'x self,
_context: Context<'r, 's>, _context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Object<'s>> { ) -> Res<OrgSource<'s>, Object<'s>> {
let (remaining, children) = let (remaining, children) =
_rematch_text_markup_object(_context, input, "*", &self.children)?; _rematch_text_markup_object(_context, input, "*", &self.children)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Object::Bold(Bold { source, children }))) Ok((
remaining,
Object::Bold(Bold {
source: source.into(),
children,
}),
))
} }
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _rematch_text_markup_object<'r, 's, 'x>( fn _rematch_text_markup_object<'r, 's, 'x>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
marker_symbol: &'static str, marker_symbol: &'static str,
original_match_children: &'x Vec<Object<'x>>, original_match_children: &'x Vec<Object<'x>>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
let (remaining, _) = pre(context, input)?; let (remaining, _) = pre(context, input)?;
let (remaining, open) = tag(marker_symbol)(remaining)?; let (remaining, open) = tag(marker_symbol)(remaining)?;
let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?; let (remaining, _peek_not_whitespace) = peek(not(multispace1))(remaining)?;
let text_markup_end_specialized = text_markup_end(open); let text_markup_end_specialized = text_markup_end(open.into());
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -290,7 +353,7 @@ fn _rematch_text_markup_object<'r, 's, 'x>(
let _enter = span.enter(); let _enter = span.enter();
if exit_matcher_parser(context, remaining).is_ok() { if exit_matcher_parser(context, remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Parent exit matcher is triggering.", "Parent exit matcher is triggering.".into(),
)))); ))));
} }
} }

View File

@ -11,6 +11,7 @@ use nom::combinator::verify;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@ -23,7 +24,10 @@ use crate::parser::util::get_consumed;
use crate::parser::Timestamp; use crate::parser::Timestamp;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn timestamp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Timestamp<'s>> { pub fn timestamp<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> {
// TODO: This would be more efficient if we didn't throw away the parse result of the first half of an active/inactive date range timestamp if the parse fails (as in, the first thing active_date_range_timestamp parses is a active_timestamp but then we throw that away if it doesn't turn out to be a full active_date_range_timestamp despite the active_timestamp parse being completely valid). I am going with the simplest/cleanest approach for the first implementation. // TODO: This would be more efficient if we didn't throw away the parse result of the first half of an active/inactive date range timestamp if the parse fails (as in, the first thing active_date_range_timestamp parses is a active_timestamp but then we throw that away if it doesn't turn out to be a full active_date_range_timestamp despite the active_timestamp parse being completely valid). I am going with the simplest/cleanest approach for the first implementation.
alt(( alt((
// Order matters here. If its a date range, we need to parse the entire date range instead of just the first timestamp. If its a time range, we need to make sure thats parsed as a time range instead of as the "rest" portion of a single timestamp. // Order matters here. If its a date range, we need to parse the entire date range instead of just the first timestamp. If its a time range, we need to make sure thats parsed as a time range instead of as the "rest" portion of a single timestamp.
@ -40,19 +44,27 @@ pub fn timestamp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn diary_timestamp<'r, 's>( fn diary_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("<%%(")(input)?; let (remaining, _) = tag("<%%(")(input)?;
let (remaining, _body) = sexp(context, remaining)?; let (remaining, _body) = sexp(context, remaining)?;
let (remaining, _) = tag(")>")(remaining)?; let (remaining, _) = tag(")>")(remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn sexp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn sexp<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -71,15 +83,18 @@ fn sexp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn sexp_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn sexp_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((tag(")>"), recognize(one_of(">\n"))))(input) alt((tag(")>"), recognize(one_of(">\n"))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn active_timestamp<'r, 's>( fn active_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("<")(input)?; let (remaining, _) = tag("<")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, _date) = date(context, remaining)?;
let time_context = let time_context =
@ -100,14 +115,19 @@ fn active_timestamp<'r, 's>(
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_timestamp<'r, 's>( fn inactive_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, _date) = date(context, remaining)?;
let time_context = let time_context =
@ -128,14 +148,19 @@ fn inactive_timestamp<'r, 's>(
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn active_date_range_timestamp<'r, 's>( fn active_date_range_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _first_timestamp) = active_timestamp(context, input)?; let (remaining, _first_timestamp) = active_timestamp(context, input)?;
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace // TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
let (remaining, _separator) = tag("--")(remaining)?; let (remaining, _separator) = tag("--")(remaining)?;
@ -144,14 +169,19 @@ fn active_date_range_timestamp<'r, 's>(
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn active_time_range_timestamp<'r, 's>( fn active_time_range_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("<")(input)?; let (remaining, _) = tag("<")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, _date) = date(context, remaining)?;
let time_context = let time_context =
@ -179,14 +209,19 @@ fn active_time_range_timestamp<'r, 's>(
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_date_range_timestamp<'r, 's>( fn inactive_date_range_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _first_timestamp) = inactive_timestamp(context, input)?; let (remaining, _first_timestamp) = inactive_timestamp(context, input)?;
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace // TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
let (remaining, _separator) = tag("--")(remaining)?; let (remaining, _separator) = tag("--")(remaining)?;
@ -195,14 +230,19 @@ fn inactive_date_range_timestamp<'r, 's>(
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_time_range_timestamp<'r, 's>( fn inactive_time_range_timestamp<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, _date) = date(context, remaining)?;
let time_context = let time_context =
@ -230,17 +270,26 @@ fn inactive_time_range_timestamp<'r, 's>(
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Timestamp { source })) Ok((
remaining,
Timestamp {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn date<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn date<'r, 's>(
let (remaining, _year) = verify(digit1, |year: &str| year.len() == 4)(input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _year) = verify(digit1, |year: &OrgSource<'_>| year.len() == 4)(input)?;
let (remaining, _) = tag("-")(remaining)?; let (remaining, _) = tag("-")(remaining)?;
let (remaining, _month) = verify(digit1, |month: &str| month.len() == 2)(remaining)?; let (remaining, _month) = verify(digit1, |month: &OrgSource<'_>| month.len() == 2)(remaining)?;
let (remaining, _) = tag("-")(remaining)?; let (remaining, _) = tag("-")(remaining)?;
let (remaining, _day_of_month) = let (remaining, _day_of_month) = verify(digit1, |day_of_month: &OrgSource<'_>| {
verify(digit1, |day_of_month: &str| day_of_month.len() == 2)(remaining)?; day_of_month.len() == 2
})(remaining)?;
let (remaining, _dayname) = let (remaining, _dayname) =
opt(tuple((space1, parser_with_context!(dayname)(context))))(remaining)?; opt(tuple((space1, parser_with_context!(dayname)(context))))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
@ -248,7 +297,10 @@ fn date<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn dayname<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn dayname<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Beta,
@ -267,25 +319,36 @@ fn dayname<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn dayname_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn dayname_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(verify(anychar, |c| { recognize(verify(anychar, |c| {
c.is_whitespace() || "+-]>0123456789\n".contains(*c) c.is_whitespace() || "+-]>0123456789\n".contains(*c)
}))(input) }))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn time<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn time<'r, 's>(
let (remaining, _hour) = context: Context<'r, 's>,
verify(digit1, |hour: &str| hour.len() >= 1 && hour.len() <= 2)(input)?; input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _hour) = verify(digit1, |hour: &OrgSource<'_>| {
hour.len() >= 1 && hour.len() <= 2
})(input)?;
let (remaining, _) = tag(":")(remaining)?; let (remaining, _) = tag(":")(remaining)?;
let (remaining, _minute) = verify(digit1, |minute: &str| minute.len() == 2)(remaining)?; let (remaining, _minute) =
verify(digit1, |minute: &OrgSource<'_>| minute.len() == 2)(remaining)?;
let (remaining, _time_rest) = opt(parser_with_context!(time_rest)(context))(remaining)?; let (remaining, _time_rest) = opt(parser_with_context!(time_rest)(context))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, source)) Ok((remaining, source))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn time_rest<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn time_rest<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, body) = recognize(verify( let (remaining, body) = recognize(verify(
many_till(anychar, parser_with_context!(exit_matcher_parser)(context)), many_till(anychar, parser_with_context!(exit_matcher_parser)(context)),
|(body, _end_contents)| !body.is_empty(), |(body, _end_contents)| !body.is_empty(),
@ -295,7 +358,10 @@ fn time_rest<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn active_time_rest_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn active_time_rest_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt(( alt((
recognize(verify(anychar, |c| ">\n".contains(*c))), recognize(verify(anychar, |c| ">\n".contains(*c))),
recognize(tuple((space1, parser_with_context!(repeater)(context)))), recognize(tuple((space1, parser_with_context!(repeater)(context)))),
@ -309,8 +375,8 @@ fn active_time_rest_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_time_rest_end<'r, 's>( fn inactive_time_rest_end<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt(( alt((
recognize(verify(anychar, |c| "]\n".contains(*c))), recognize(verify(anychar, |c| "]\n".contains(*c))),
recognize(tuple((space1, parser_with_context!(repeater)(context)))), recognize(tuple((space1, parser_with_context!(repeater)(context)))),
@ -322,7 +388,10 @@ fn inactive_time_rest_end<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn time_range_rest_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn time_range_rest_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// We pop off the most recent context element to get a context tree with just the active/inactive_time_rest_end exit matcher (removing this function from the exit matcher chain) because the 2nd time in the range does not end when a "-TIME" pattern is found. // We pop off the most recent context element to get a context tree with just the active/inactive_time_rest_end exit matcher (removing this function from the exit matcher chain) because the 2nd time in the range does not end when a "-TIME" pattern is found.
let parent_node = context.iter().next().expect("Two context elements are added to the tree when adding this exit matcher, so it should be impossible for this to return None."); let parent_node = context.iter().next().expect("Two context elements are added to the tree when adding this exit matcher, so it should be impossible for this to return None.");
let parent_tree = ContextTree::branch_from(parent_node); let parent_tree = ContextTree::branch_from(parent_node);
@ -332,7 +401,10 @@ fn time_range_rest_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn repeater<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn repeater<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// + for cumulative type // + for cumulative type
// ++ for catch-up type // ++ for catch-up type
// .+ for restart type // .+ for restart type
@ -345,7 +417,10 @@ fn repeater<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn warning_delay<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn warning_delay<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// - for all type // - for all type
// -- for first type // -- for first type
let (remaining, _mark) = alt((tag("--"), tag("-")))(input)?; let (remaining, _mark) = alt((tag("--"), tag("-")))(input)?;

View File

@ -1,7 +1,6 @@
use nom::branch::alt; use nom::branch::alt;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::multispace0;
use nom::character::complete::none_of; use nom::character::complete::none_of;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::combinator::eof; use nom::combinator::eof;
@ -14,6 +13,7 @@ use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::parser_context::ContextElement; use super::parser_context::ContextElement;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
@ -48,90 +48,34 @@ pub fn immediate_in_section<'r, 's, 'x>(context: Context<'r, 's>, section_name:
false false
} }
/// Get one character from before the current position.
pub fn get_one_before<'s>(document: &'s str, current_position: &'s str) -> Option<&'s str> {
assert!(is_slice_of(document, current_position));
if document.as_ptr() as usize == current_position.as_ptr() as usize {
return None;
}
let offset = current_position.as_ptr() as usize - document.as_ptr() as usize;
let previous_character_offset = document.floor_char_boundary(offset - 1);
Some(&document[previous_character_offset..offset])
}
/// Get the line current_position is on up until current_position
pub fn get_current_line_before_position<'s>(
document: &'s str,
current_position: &'s str,
) -> Option<&'s str> {
assert!(is_slice_of(document, current_position));
if document.as_ptr() as usize == current_position.as_ptr() as usize {
return None;
}
let offset = current_position.as_ptr() as usize - document.as_ptr() as usize;
let mut previous_character_offset = offset;
loop {
let new_offset = document.floor_char_boundary(previous_character_offset - 1);
let new_line = &document[new_offset..offset];
let leading_char = new_line
.chars()
.next()
.expect("Impossible to not have at least 1 character to read.");
if "\r\n".contains(leading_char) || new_offset == 0 {
break;
}
previous_character_offset = new_offset;
}
Some(&document[previous_character_offset..offset])
}
/// Check if the child string slice is a slice of the parent string slice.
fn is_slice_of(parent: &str, child: &str) -> bool {
let parent_start = parent.as_ptr() as usize;
let parent_end = parent_start + parent.len();
let child_start = child.as_ptr() as usize;
let child_end = child_start + child.len();
child_start >= parent_start && child_end <= parent_end
}
/// Get a slice of the string that was consumed in a parser using the original input to the parser and the remaining input after the parser. /// Get a slice of the string that was consumed in a parser using the original input to the parser and the remaining input after the parser.
pub fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str { pub fn get_consumed<'s>(input: OrgSource<'s>, remaining: OrgSource<'s>) -> OrgSource<'s> {
assert!(is_slice_of(input, remaining)); input.get_until(remaining)
let source = {
let offset = remaining.as_ptr() as usize - input.as_ptr() as usize;
&input[..offset]
};
source
} }
/// A line containing only whitespace and then a line break /// A line containing only whitespace and then a line break
/// ///
/// It is up to the caller to ensure this is called at the start of a line. /// It is up to the caller to ensure this is called at the start of a line.
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn blank_line(input: &str) -> Res<&str, &str> { pub fn blank_line(input: OrgSource<'_>) -> Res<OrgSource<'_>, OrgSource<'_>> {
not(eof)(input)?; not(eof)(input)?;
recognize(tuple((space0, alt((line_ending, eof)))))(input) recognize(tuple((space0, alt((line_ending, eof)))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn element_trailing_whitespace<'r, 's>( pub fn element_trailing_whitespace<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
context: Context<'r, 's>, start_of_line(input)?;
input: &'s str,
) -> Res<&'s str, &'s str> {
start_of_line(context, input)?;
alt((eof, recognize(many0(blank_line))))(input) alt((eof, recognize(many0(blank_line))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn maybe_consume_trailing_whitespace_if_not_exiting<'r, 's>( pub fn maybe_consume_trailing_whitespace_if_not_exiting<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Option<&'s str>> { ) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
if context.should_consume_trailing_whitespace() && exit_matcher_parser(context, input).is_err() if context.should_consume_trailing_whitespace() && exit_matcher_parser(context, input).is_err()
{ {
Ok(opt(parser_with_context!(element_trailing_whitespace)( Ok(opt(element_trailing_whitespace)(input)?)
context,
))(input)?)
} else { } else {
Ok((input, None)) Ok((input, None))
} }
@ -140,59 +84,37 @@ pub fn maybe_consume_trailing_whitespace_if_not_exiting<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn maybe_consume_trailing_whitespace<'r, 's>( pub fn maybe_consume_trailing_whitespace<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Option<&'s str>> { ) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
if context.should_consume_trailing_whitespace() { if context.should_consume_trailing_whitespace() {
Ok(opt(parser_with_context!(element_trailing_whitespace)( Ok(opt(element_trailing_whitespace)(input)?)
context,
))(input)?)
} else { } else {
Ok((input, None)) Ok((input, None))
} }
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn trailing_whitespace(input: &str) -> Res<&str, &str> {
alt((eof, recognize(tuple((line_ending, many0(blank_line))))))(input)
}
/// Check that we are at the start of a line /// Check that we are at the start of a line
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn start_of_line<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { pub fn start_of_line<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let document_root = context.get_document_root().unwrap(); if input.is_at_start_of_line() {
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
match preceding_character {
Some('\n') => {}
Some(_) => {
// Not at start of line, cannot be a heading
return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not at start of line",
))));
}
// If None, we are at the start of the file which allows for headings
None => {}
};
Ok((input, ())) Ok((input, ()))
} else {
Err(nom::Err::Error(CustomError::MyError(MyError(
"Not at start of line".into(),
))))
}
} }
/// Check that we are at the start of a line /// Check that we are at the start of a line
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn preceded_by_whitespace<'r, 's>( pub fn preceded_by_whitespace<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
context: Context<'r, 's>, let preceding_character = input.get_preceding_character();
input: &'s str,
) -> Res<&'s str, ()> {
let document_root = context.get_document_root().unwrap();
let preceding_character = get_one_before(document_root, input)
.map(|slice| slice.chars().next())
.flatten();
match preceding_character { match preceding_character {
Some('\n') | Some('\r') | Some(' ') | Some('\t') => {} Some('\n') | Some('\r') | Some(' ') | Some('\t') => {}
// If None, we are at the start of the file which is not allowed // If None, we are at the start of the file which is not allowed
None | Some(_) => { None | Some(_) => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not preceded by whitespace.", "Not preceded by whitespace.".into(),
)))); ))));
} }
}; };
@ -203,7 +125,7 @@ pub fn preceded_by_whitespace<'r, 's>(
/// ///
/// This function only operates on spaces, tabs, carriage returns, and line feeds. It does not handle fancy unicode whitespace. /// This function only operates on spaces, tabs, carriage returns, and line feeds. It does not handle fancy unicode whitespace.
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn non_whitespace_character(input: &str) -> Res<&str, char> { pub fn non_whitespace_character(input: OrgSource<'_>) -> Res<OrgSource<'_>, char> {
none_of(" \t\r\n")(input) none_of(" \t\r\n")(input)
} }
@ -211,25 +133,16 @@ pub fn non_whitespace_character(input: &str) -> Res<&str, char> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn exit_matcher_parser<'r, 's>( pub fn exit_matcher_parser<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
peek(|i| context.check_exit_matcher(i))(input) peek(|i| context.check_exit_matcher(i))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn always_fail<'r, 's>(_context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { pub fn text_until_exit<'r, 's>(
Err(nom::Err::Error(CustomError::MyError(MyError( context: Context<'r, 's>,
"Always fail", input: OrgSource<'s>,
)))) ) -> Res<OrgSource<'s>, OrgSource<'s>> {
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn whitespace_eof(input: &str) -> Res<&str, &str> {
recognize(tuple((multispace0, eof)))(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn text_until_exit<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
recognize(verify( recognize(verify(
many_till(anychar, parser_with_context!(exit_matcher_parser)(context)), many_till(anychar, parser_with_context!(exit_matcher_parser)(context)),
|(children, _exit_contents)| !children.is_empty(), |(children, _exit_contents)| !children.is_empty(),
@ -237,23 +150,8 @@ pub fn text_until_exit<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn not_yet_implemented() -> Res<&'static str, ()> { pub fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Not implemented yet.", "Not implemented yet.".into(),
)))); ))));
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn get_one_before_unicode() {
let input = "🧡💛💚💙💜";
let (green_heart_index, _) = input.char_indices().skip(2).next().unwrap();
let starting_with_green_heart = &input[green_heart_index..];
let yellow_heart = get_one_before(input, starting_with_green_heart).unwrap();
assert!(is_slice_of(input, yellow_heart));
assert_eq!(yellow_heart, "💛");
}
}