You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
93 lines
2.4 KiB
93 lines
2.4 KiB
use std::iter::Peekable;
|
|
use std::ops::{Deref, RangeInclusive};
|
|
use std::usize;
|
|
use std::vec::IntoIter;
|
|
|
|
use super::ParseError;
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum Token {
|
|
LeftParen,
|
|
RightParen,
|
|
Symbol(String),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum Sexpr {
|
|
Symbol(String),
|
|
List(Vec<Sexpr>),
|
|
}
|
|
|
|
impl Sexpr {
|
|
pub fn symbol(self) -> Option<String> {
|
|
match self {
|
|
Sexpr::Symbol(item) => Some(item),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn list(self) -> Option<Vec<Sexpr>> {
|
|
match self {
|
|
Sexpr::List(item) => Some(item),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn tokenize(input: &str) -> Vec<Token> {
|
|
let mut tokens = Vec::new();
|
|
// let mut chars = input.chars().peekable();
|
|
let mut chars = input.chars().peekable();
|
|
while let Some(c) = chars.next() {
|
|
match c {
|
|
'(' => tokens.push(Token::LeftParen),
|
|
')' => tokens.push(Token::RightParen),
|
|
_ if c.is_whitespace() => (),
|
|
_ => {
|
|
let mut symbol = c.to_string();
|
|
while let Some(c) = chars.peek() {
|
|
if c.is_whitespace() || *c == '(' || *c == ')' {
|
|
break;
|
|
}
|
|
symbol.push(*c);
|
|
chars.next();
|
|
}
|
|
tokens.push(Token::Symbol(symbol));
|
|
}
|
|
}
|
|
}
|
|
tokens
|
|
}
|
|
|
|
fn parse_expr(tokens: &mut Peekable<IntoIter<Token>>) -> Result<Sexpr, ParseError> {
|
|
match tokens.next() {
|
|
Some(Token::LeftParen) => {
|
|
let mut list = Vec::new();
|
|
while !matches!(tokens.peek(), Some(Token::RightParen,)) {
|
|
list.push(parse_expr(tokens)?);
|
|
}
|
|
let Some(Token::RightParen) = tokens.next() else {
|
|
unreachable!()
|
|
};
|
|
Ok(Sexpr::List(list))
|
|
}
|
|
Some(Token::RightParen) => Err(ParseError::UnexpectedParenClose),
|
|
Some(Token::Symbol(s)) => Ok(Sexpr::Symbol(s)),
|
|
None => Err(ParseError::UnexpectedEof),
|
|
}
|
|
}
|
|
|
|
fn parse(tokens: Vec<Token>) -> Result<Sexpr, ParseError> {
|
|
let mut tokens = tokens.into_iter().peekable();
|
|
let ast = parse_expr(&mut tokens)?;
|
|
if tokens.peek().is_some() {
|
|
return Err(ParseError::TrailingTokens);
|
|
};
|
|
Ok(ast)
|
|
}
|
|
|
|
pub fn parse_string(src: &str) -> Result<Sexpr, ParseError> {
|
|
let tokens = tokenize(src);
|
|
parse(tokens)
|
|
}
|