use std::iter::Peekable; use crate::{ ast::{Ast, Atom, Error, Expr}, lex::Token, span::{Pos, Span, Spanned}, }; pub(super) const MAX_DEPTH: usize = 256; // TODO: make it a compile flag fn parse_number(number: &str) -> Result { let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')) || matches!(number, "inf" | "+inf" | "-inf" | "nan"); if is_float { match number.parse() { Ok(ok) => Ok(Atom::Float(ok)), Err(err) => Err(Error::InvalidFloatLiteral(number.into(), err)), } } else { match number.parse() { Ok(ok) => Ok(Atom::Integer(ok)), Err(err) => Err(Error::InvalidIntegerLiteral(number.into(), err)), } } } 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, "inf" | "+inf" => Atom::Float(f64::INFINITY), "-inf" => Atom::Float(f64::NEG_INFINITY), "nan" => Atom::Float(f64::NAN), _ => Atom::Symbol(symbol.into()), } } pub struct Parser<'a, I> where I: Iterator>>, { tokens: Peekable, last_token_span: Span, depth: usize, } 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)), depth: 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::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(); self.depth += 1; if self.depth >= MAX_DEPTH { return Err(Spanned::new(Error::RecursionLimit, self.last_token_span)); } while let Some(Spanned { inner: token, .. }) = self.peek() { match token { Token::RightPar => { self.consume(); return Ok(list); } _ => list.push(self.parse_expr()?), } } self.depth -= 1; Err(Spanned::new(Error::UnclosedLeftPar, left_par_span)) } pub fn parse(mut self) -> Result> { let mut ast = Vec::new(); while self.peek().is_some() { ast.push(self.parse_expr()?) } Ok(Ast::new(ast)) } }