Parse the babel call.

This commit is contained in:
Tom Alexander
2023-10-05 16:32:40 -04:00
parent 343af41f78
commit 68e392811e
8 changed files with 176 additions and 12 deletions

View File

@@ -1,10 +1,14 @@
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::consumed;
use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::combinator::verify;
use nom::multi::many0;
use nom::multi::many_till;
use nom::sequence::tuple;
@@ -12,6 +16,7 @@ use nom::InputTake;
use super::keyword::affiliated_keyword;
use super::util::get_name;
use super::util::start_of_line;
use super::OrgSource;
use crate::context::RefContext;
use crate::error::Res;
@@ -26,8 +31,8 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, BabelCall<'s>> {
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
let (remaining, (consumed_input, (_, _, parsed_key, _))) =
consumed(tuple((space0, tag("#+"), tag_no_case("call"), tag(":"))))(input)?;
start_of_line(input)?;
let (remaining, _) = tuple((space0, tag("#+"), tag_no_case("call"), tag(":")))(input)?;
if let Ok((remaining, (_, line_break))) = tuple((space0, org_line_ending))(remaining) {
let source = get_consumed(input, remaining);
@@ -37,13 +42,17 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
source: Into::<&str>::into(source),
name: get_name(&affiliated_keywords),
value: Into::<&str>::into(line_break.take(0)),
call: None,
inside_header: None,
arguments: None,
end_header: None,
},
));
}
let (remaining, _ws) = space0(remaining)?;
let (remaining, parsed_value) =
recognize(many_till(anychar, peek(tuple((space0, org_line_ending)))))(remaining)?;
let (remaining, (value, (call, inside_header, arguments, end_header))) =
consumed(babel_call_value)(remaining)?;
let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
let source = get_consumed(input, remaining);
@@ -53,7 +62,106 @@ pub(crate) fn babel_call<'b, 'g, 'r, 's>(
BabelCall {
source: Into::<&str>::into(source),
name: get_name(&affiliated_keywords),
value: Into::<&str>::into(parsed_value),
value: Into::<&str>::into(value).trim_end(),
call: call.map(Into::<&str>::into),
inside_header: inside_header.map(Into::<&str>::into),
arguments: arguments.map(Into::<&str>::into),
end_header: end_header.map(Into::<&str>::into),
},
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn babel_call_value<'s>(
input: OrgSource<'s>,
) -> Res<
OrgSource<'s>,
(
Option<OrgSource<'s>>,
Option<OrgSource<'s>>,
Option<OrgSource<'s>>,
Option<OrgSource<'s>>,
),
> {
let (remaining, call) = opt(babel_call_call)(input)?;
let (remaining, inside_header) = opt(inside_header)(remaining)?;
let (remaining, arguments) = opt(arguments)(remaining)?;
let (remaining, end_header) = opt(end_header)(remaining)?;
Ok((
remaining,
(call, inside_header, arguments.flatten(), end_header),
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn babel_call_call<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
verify(
recognize(many_till(
anychar,
alt((
peek(recognize(one_of("[("))),
recognize(tuple((space0, org_line_ending))),
)),
)),
|s| s.len() > 0,
)(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inside_header<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tag("[")(input)?;
let contents_start = remaining;
let (remaining, contents) = opt(recognize(many_till(
anychar,
alt((
peek(recognize(one_of("]"))),
recognize(tuple((space0, org_line_ending))),
)),
)))(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
Ok((remaining, contents.unwrap_or(contents_start.take(0))))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn arguments<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
let (remaining, _) = tag("(")(input)?;
let (remaining, contents) = opt(verify(
recognize(many_till(
anychar,
alt((
peek(recognize(one_of(")"))),
recognize(tuple((space0, org_line_ending))),
)),
)),
|s| s.len() > 0,
))(remaining)?;
let (remaining, _) = tag(")")(remaining)?;
Ok((remaining, contents))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn end_header<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = space0(input)?;
verify(
recognize(many_till(anychar, peek(tuple((space0, org_line_ending))))),
|s| s.len() > 0,
)(remaining)
}
#[cfg(test)]
mod tests {
use nom::combinator::opt;
use super::*;
#[test]
fn simple_call() -> Result<(), Box<dyn std::error::Error>> {
let input = OrgSource::new("()");
let (remaining, call) = opt(babel_call_call)(input)?;
assert_eq!(Into::<&str>::into(remaining), "()");
assert_eq!(call, None);
Ok(())
}
}