70 lines
1.9 KiB
Rust
70 lines
1.9 KiB
Rust
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::bytes::complete::take_till1;
|
|
use nom::character::complete::multispace0;
|
|
use nom::character::complete::multispace1;
|
|
use nom::combinator::not;
|
|
use nom::combinator::peek;
|
|
use nom::multi::separated_list1;
|
|
use nom::sequence::delimited;
|
|
|
|
use super::error::Res;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Token<'s> {
|
|
Atom(&'s str),
|
|
List(Vec<Token<'s>>),
|
|
}
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
pub fn sexp<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
|
let (remaining, _) = multispace0(input)?;
|
|
let (remaining, tkn) = token(remaining)?;
|
|
let (remaining, _) = multispace0(remaining)?;
|
|
Ok((remaining, tkn))
|
|
}
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
fn token<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
|
alt((list, atom))(input)
|
|
}
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
fn list<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
|
let (remaining, opening_paren) = tag("(")(input)?;
|
|
let (remaining, children) = delimited(multispace0, separated_list1(multispace1, token), multispace0)(remaining)?;
|
|
let (remaining, closing_paren) = 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)?;
|
|
unquoted_atom(input)
|
|
}
|
|
|
|
#[tracing::instrument(ret, level = "debug")]
|
|
fn unquoted_atom<'s>(input: &'s str) -> Res<&'s str, Token<'s>> {
|
|
let (remaining, body) = take_till1(|c| match c {
|
|
' ' | '\t' | '\r' | '\n' => true,
|
|
_ => false,
|
|
})(input)?;
|
|
Ok((remaining, Token::Atom(body)))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn simple() {
|
|
let input = " (foo bar baz ) ";
|
|
let (remaining, parsed) = sexp(input).expect("Parse the input");
|
|
assert_eq!(remaining, "");
|
|
assert!(match parsed {
|
|
Token::Atom(_) => false,
|
|
Token::List(_) => true,
|
|
});
|
|
}
|
|
}
|