use std::iter::Peekable; use crate::{ ast::{Atom, Error, Expr, Program}, lexer::Token, span::{Pos, Span, Spanned}, }; fn parse_number(number: &str) -> Result { let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')); let atom = if is_float { Atom::Float(number.parse()?) } else { Atom::Integer(number.parse()?) }; Ok(atom) } fn parse_string(string: &str) -> Result { let mut result = String::new(); let mut is_escape = false; for ch in string.chars() { if !is_escape { match ch { '\\' => is_escape = true, _ => result.push(ch), } } else { match ch { '"' => result.push('"'), 'n' => result.push('\n'), '\\' => result.push('\\'), '\n' => {} _ => return Err(Error::UnexpectedEscapeChar(ch)), } is_escape = false; } } if is_escape { return Err(Error::UnclosedString(string.into())); } Ok(Atom::String(result.into())) } fn parse_symbol(symbol: &str) -> Atom { match symbol { "true" => Atom::Bool(true), "false" => Atom::Bool(false), "nil" => Atom::Nil, _ => Atom::Symbol(symbol.into()), } } pub struct Parser<'a, I> where I: Iterator>>, { tokens: Peekable, last_token_span: Span, } impl<'a, I> Parser<'a, I> where I: Iterator>>, { pub fn new(tokens: I) -> Self { Self { tokens: tokens.peekable(), last_token_span: Span::new(Pos::new(1, 0, 0), Pos::new(1, 0, 0)), } } fn peek(&mut self) -> Option>> { self.tokens.peek().copied() } fn consume(&mut self) -> Option>> { self.tokens .next() .inspect(|s| self.last_token_span = s.span) } fn parse_expr(&mut self) -> Result, Spanned> { let Spanned { inner: token, span } = match self.peek() { Some(spanned) => spanned, None => return Err(Spanned::new(Error::UnexpectedEof, self.last_token_span)), }; let expr = match token { Token::LeftPar => { self.consume(); let list = self.parse_list(span)?; let expr = if !list.is_empty() { Expr::List(list) } else { Expr::Atom(Atom::Nil) }; Spanned::new(expr, Span::new(span.start, self.last_token_span.end)) } Token::RightPar => { self.consume(); return Err(Spanned::new(Error::UnexpectedRightPar, span)); } Token::Quote => { self.consume(); let quote = Spanned::new(Expr::Atom(Atom::Symbol("quote".into())), span); let expr = self.parse_expr()?; Spanned::new( Expr::List(vec![quote, expr]), Span::new(span.start, self.last_token_span.end), ) } Token::Number(number) => { self.consume(); let atom = parse_number(number).map_err(|e| Spanned::new(e, span))?; Spanned::new(Expr::Atom(atom), span) } Token::String(string) => { self.consume(); let atom = parse_string(string).map_err(|e| Spanned::new(e, span))?; Spanned::new(Expr::Atom(atom), span) } Token::UnclosedString(string) => { self.consume(); return Err(Spanned::new(Error::UnclosedString(string.into()), span)); } Token::Symbol(symbol) => { self.consume(); let atom = parse_symbol(symbol); Spanned::new(Expr::Atom(atom), span) } }; Ok(expr) } fn parse_list(&mut self, left_par_span: Span) -> Result>, Spanned> { let mut list = Vec::new(); while let Some(Spanned { inner: token, .. }) = self.peek() { match token { Token::RightPar => { self.consume(); return Ok(list); } _ => list.push(self.parse_expr()?), } } Err(Spanned::new(Error::UnclosedLeftPar, left_par_span)) } pub fn parse(mut self) -> Result> { let mut program = Vec::new(); while self.peek().is_some() { program.push(self.parse_expr()?) } Ok(Program(program)) } }