Implement a very basic first stab at lisp parser.

This commit is contained in:
Tom Alexander
2023-04-11 14:50:37 -04:00
parent 96d2cc7c6a
commit 751a3beffd
4 changed files with 105 additions and 0 deletions

65
src/compare/sexp.rs Normal file
View File

@@ -0,0 +1,65 @@
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::multi::separated_list1;
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) = separated_list1(multispace1, token)(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>> {
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,
});
}
}