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::multispace0;
|
||||||
use nom::character::complete::multispace1;
|
use nom::character::complete::multispace1;
|
||||||
use nom::character::complete::one_of;
|
use nom::character::complete::one_of;
|
||||||
|
use nom::combinator::map;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
|
use nom::combinator::verify;
|
||||||
use nom::multi::separated_list1;
|
use nom::multi::separated_list1;
|
||||||
use nom::sequence::delimited;
|
use nom::sequence::delimited;
|
||||||
|
use nom::sequence::preceded;
|
||||||
|
use nom::sequence::tuple;
|
||||||
|
|
||||||
use super::error::Res;
|
use super::error::Res;
|
||||||
|
|
||||||
@ -16,6 +21,13 @@ use super::error::Res;
|
|||||||
pub enum Token<'s> {
|
pub enum Token<'s> {
|
||||||
Atom(&'s str),
|
Atom(&'s str),
|
||||||
List(Vec<Token<'s>>),
|
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")]
|
#[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")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
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(
|
let (remaining, children) = delimited(
|
||||||
multispace0,
|
multispace0,
|
||||||
separated_list1(multispace1, token),
|
separated_list1(multispace1, token),
|
||||||
multispace0,
|
multispace0,
|
||||||
)(remaining)?;
|
)(remaining)?;
|
||||||
let (remaining, closing_paren) = tag(")")(remaining)?;
|
let (remaining, _) = tag(")")(remaining)?;
|
||||||
Ok((remaining, Token::List(children)))
|
Ok((remaining, Token::List(children)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[tracing::instrument(ret, level = "debug")]
|
||||||
fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
fn atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
||||||
not(peek(tag(")")))(input)?;
|
not(peek(tag(")")))(input)?;
|
||||||
alt((quoted_atom, unquoted_atom))(input)
|
alt((text_with_properties, quoted_atom, unquoted_atom))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(ret, level = "debug")]
|
#[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)))
|
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.
|
/// 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 {
|
fn get_consumed<'s>(input: &'s str, remaining: &'s str) -> &'s str {
|
||||||
assert!(is_slice_of(input, remaining));
|
assert!(is_slice_of(input, remaining));
|
||||||
@ -105,6 +140,7 @@ mod tests {
|
|||||||
assert!(match parsed {
|
assert!(match parsed {
|
||||||
Token::Atom(_) => false,
|
Token::Atom(_) => false,
|
||||||
Token::List(_) => true,
|
Token::List(_) => true,
|
||||||
|
Token::TextWithProperties(_) => false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +152,18 @@ mod tests {
|
|||||||
assert!(match parsed {
|
assert!(match parsed {
|
||||||
Token::Atom(_) => false,
|
Token::Atom(_) => false,
|
||||||
Token::List(_) => true,
|
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