use std::{iter::Peekable, result}; use crate::{ ast::{Atom, Error, Expr, Program}, lexer::Token, span::{Pos, Span, Spanned}, }; type Result = result::Result>; fn parse_number(number: &str) -> Atom { let is_float = number.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')); if is_float { match number.parse() { Ok(ok) => Atom::Float(ok), Err(err) => todo!("invalid float literal {number}: {err}"), } } else { match number.parse() { Ok(ok) => Atom::Integer(ok), Err(err) => todo!("invalid integer literal {number}: {err}"), } } } fn parse_string(string: &str) -> Atom { 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' => {} _ => todo!("unexpected escape char {ch:?}"), } is_escape = false; } } if is_escape { todo!("unclosed string"); } 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_end: Pos, } impl<'a, I> Parser<'a, I> where I: Iterator>>, { pub fn new(tokens: I) -> Self { Self { tokens: tokens.peekable(), last_token_end: 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_end = s.span.end) } fn parse_expr(&mut self) -> Result> { let Spanned { inner: token, span } = match self.peek() { Some(spanned) => spanned, None => todo!("unexpected eof"), }; let expr = match token { Token::LeftPar => { self.consume(); let list = self.parse_list()?; let expr = if !list.is_empty() { Expr::List(list) } else { Expr::Atom(Atom::Nil) }; Spanned::new(expr, Span::new(span.start, self.last_token_end)) } Token::RightPar => todo!("unexpected par"), Token::Quote => { self.consume(); let quote = Spanned::new( Expr::Atom(Atom::Symbol("quote".into())), Span::new(span.start, self.last_token_end), ); let expr = self.parse_expr()?; Spanned::new( Expr::List(vec![quote, expr]), Span::new(span.start, self.last_token_end), ) } Token::Number(number) => { self.consume(); Spanned::new( Expr::Atom(parse_number(number)), Span::new(span.start, self.last_token_end), ) } Token::String(string) => { self.consume(); Spanned::new( Expr::Atom(parse_string(string)), Span::new(span.start, self.last_token_end), ) } Token::UnclosedString(string) => { self.consume(); todo!("unclosed string {string:?}") } Token::Symbol(symbol) => { self.consume(); Spanned::new( Expr::Atom(parse_symbol(symbol)), Span::new(span.start, self.last_token_end), ) } }; Ok(expr) } fn parse_list(&mut self) -> Result>> { 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()?), } } todo!("unclosed par") } pub fn parse(mut self) -> Result { let mut program = Vec::new(); while self.peek().is_some() { program.push(self.parse_expr()?) } Ok(Program(program)) } }