Handle text with properties.

This commit is contained in:
Tom Alexander 2023-04-11 16:02:57 -04:00
parent 3bdb1e3841
commit 8df02fa8b9
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE

View File

@ -5,10 +5,15 @@ use nom::bytes::complete::take_till1;
use nom::character::complete::multispace0;
use nom::character::complete::multispace1;
use nom::character::complete::one_of;
use nom::combinator::map;
use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::verify;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::tuple;
use super::error::Res;
@ -16,6 +21,13 @@ use super::error::Res;
pub enum Token<'s> {
Atom(&'s str),
List(Vec<Token<'s>>),
TextWithProperties(TextWithProperties<'s>),
}
#[derive(Debug)]
pub struct TextWithProperties<'s> {
text: &'s str,
properties: Vec<Token<'s>>,
}
#[tracing::instrument(ret, level = "debug")]
@ -33,20 +45,20 @@ fn token<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
#[tracing::instrument(ret, level = "debug")]
fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, opening_paren) = tag("(")(input)?;
let (remaining, _) = tag("(")(input)?;
let (remaining, children) = delimited(
multispace0,
separated_list1(multispace1, token),
multispace0,
)(remaining)?;
let (remaining, closing_paren) = tag(")")(remaining)?;
let (remaining, _) = tag(")")(remaining)?;
Ok((remaining, Token::List(children)))
}
#[tracing::instrument(ret, level = "debug")]
fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
not(peek(tag(")")))(input)?;
alt((quoted_atom, unquoted_atom))(input)
alt((text_with_properties, quoted_atom, unquoted_atom))(input)
}
#[tracing::instrument(ret, level = "debug")]
@ -74,6 +86,29 @@ fn quoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
Ok((remaining, Token::Atom(source)))
}
fn text_with_properties<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
let (remaining, _) = tag("#(")(input)?;
let (remaining, (text, props)) = delimited(
multispace0,
tuple((
map(quoted_atom, |atom| match atom {
Token::Atom(body) => body,
_ => unreachable!(),
}),
preceded(multispace1, opt(separated_list1(multispace1, token))),
)),
multispace0,
)(remaining)?;
let (remaining, _) = tag(")")(remaining)?;
Ok((
remaining,
Token::TextWithProperties(TextWithProperties {
text,
properties: props.unwrap_or(Vec::new()),
}),
))
}
/// 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));
@ -105,6 +140,7 @@ mod tests {
assert!(match parsed {
Token::Atom(_) => false,
Token::List(_) => true,
Token::TextWithProperties(_) => false,
});
}
@ -116,6 +152,18 @@ mod tests {
assert!(match parsed {
Token::Atom(_) => false,
Token::List(_) => true,
Token::TextWithProperties(_) => false,
});
let children = match parsed {
Token::List(children) => children,
_ => panic!("Should be a list."),
};
assert_eq!(
match children.first() {
Some(Token::Atom(body)) => *body,
_ => panic!("First child should be an atom."),
},
r#""foo""#
)
}
}