Allow matched parenthesis inside plain links.

This commit is contained in:
Tom Alexander 2023-09-21 18:34:32 -04:00
parent 5716cbccea
commit 9b2348c0ef
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
2 changed files with 143 additions and 11 deletions

View File

@ -0,0 +1,9 @@
foo_(bar)
foo_(b(ar)
foo_(b{ar)
foo_{b(ar}
foo_(b(a)r)

View File

@ -5,10 +5,14 @@ use nom::character::complete::anychar;
use nom::character::complete::none_of; use nom::character::complete::none_of;
use nom::character::complete::one_of; use nom::character::complete::one_of;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0;
use nom::multi::many1;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple;
use super::org_source::BracketDepth; use super::org_source::BracketDepth;
use super::org_source::OrgSource; use super::org_source::OrgSource;
@ -18,6 +22,7 @@ use crate::context::ContextElement;
use crate::context::ContextMatcher; use crate::context::ContextMatcher;
use crate::context::ExitClass; use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::Matcher;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@ -132,17 +137,77 @@ fn path_plain<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, 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" let path_plain_end = path_plain_end(input.get_parenthesis_depth());
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode { let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma, class: ExitClass::Gamma,
exit_matcher: &path_plain_end, exit_matcher: &path_plain_end,
}); });
let parser_context = context.with_additional_node(&parser_context); let parser_context = context.with_additional_node(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let (remaining, _components) = many1(alt((
parser_with_context!(path_plain_no_parenthesis)(&parser_context),
parser_with_context!(path_plain_parenthesis)(&parser_context),
)))(input)?;
let source = get_consumed(input, remaining);
Ok((remaining, source))
}
fn path_plain_end(starting_parenthesis_depth: BracketDepth) -> impl ContextMatcher {
move |context, input: OrgSource<'_>| _path_plain_end(context, input, starting_parenthesis_depth)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _path_plain_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
starting_parenthesis_depth: BracketDepth,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _leading_punctuation) = many0(verify(anychar, |c| {
!" \t\r\n[]<>()/".contains(*c) && c.is_ascii_punctuation()
}))(input)?;
let disallowed_character = recognize(one_of(" \t\r\n[]<>"))(remaining);
if disallowed_character.is_ok() {
return disallowed_character;
}
let current_depth = remaining.get_parenthesis_depth() - starting_parenthesis_depth;
if current_depth == 0 {
let close_parenthesis =
tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(remaining);
if close_parenthesis.is_ok() {
return close_parenthesis;
}
let open_parenthesis_without_match = recognize(tuple((
peek(tag("(")),
not(parser_with_context!(path_plain_parenthesis)(context)),
)))(remaining);
if open_parenthesis_without_match.is_ok() {
return open_parenthesis_without_match;
}
}
// many0 punctuation
Err(nom::Err::Error(CustomError::MyError(MyError(
"No path plain end".into(),
))))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_plain_no_parenthesis<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, path) = recognize(verify( let (remaining, path) = recognize(verify(
many_till(anychar, exit_matcher), many_till(
anychar,
alt((
peek(path_plain_no_parenthesis_disallowed_character),
parser_with_context!(exit_matcher_parser)(context),
)),
),
|(children, _exit_contents)| !children.is_empty(), |(children, _exit_contents)| !children.is_empty(),
))(input)?; ))(input)?;
@ -150,14 +215,72 @@ fn path_plain<'b, 'g, 'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_plain_end<'b, 'g, 'r, 's>( fn path_plain_no_parenthesis_disallowed_character<'s>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(many_till( recognize(verify(anychar, |c| {
verify(anychar, |c| { c.is_whitespace() || "()[]<>".contains(*c)
*c != '/' && (c.is_ascii_punctuation() || c.is_whitespace()) }))(input)
}), }
one_of(" \t\r\n()[]<>"),
))(input) #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_plain_parenthesis<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _opening) = tag("(")(input)?;
let starting_depth = remaining.get_parenthesis_depth();
let (remaining, _path) = recognize(verify(
many_till(
anychar,
alt((
peek(path_plain_parenthesis_end(starting_depth)),
parser_with_context!(exit_matcher_parser)(context),
)),
),
|(children, _exit_contents)| !children.is_empty(),
))(remaining)?;
let (remaining, _opening) = tag(")")(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, source))
}
fn path_plain_parenthesis_end(starting_parenthesis_depth: BracketDepth) -> impl Matcher {
move |input: OrgSource<'_>| _path_plain_parenthesis_end(input, starting_parenthesis_depth)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _path_plain_parenthesis_end<'s>(
input: OrgSource<'s>,
starting_parenthesis_depth: BracketDepth,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
eprintln!(
"current_depth: {}, starting: {}, now: {}, remaining input: {}",
current_depth,
starting_parenthesis_depth,
input.get_parenthesis_depth(),
input
);
if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing parenthesis should end the link.
unreachable!("Exceeded plain link parenthesis depth.")
}
if current_depth == 0 {
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input);
if close_parenthesis.is_ok() {
return close_parenthesis;
}
}
if current_depth == 1 {
let open_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("(")(input);
if open_parenthesis.is_ok() {
return open_parenthesis;
}
}
Err(nom::Err::Error(CustomError::MyError(MyError(
"No closing parenthesis".into(),
))))
} }