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

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)
}