Handle text with properties.
This commit is contained in:
parent
3bdb1e3841
commit
8df02fa8b9
@ -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""#
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user