use std::iter::Peekable; use std::sync::Arc; use rust_decimal::Decimal; use crate::tokens::{Lexer, Token}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Program { pub commands: Arc<[Command]>, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Command { Statement(Statement), Expression(Expression), PanicMode(Arc<[Token]>), } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Statement { pub function_name: Arc, pub function_token: Token, pub args: Arc<[Expression]>, pub newline: Option, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Expression { pub prefix: Arc<[Token]>, pub suffix: ExpressionSuffix, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ExpressionSuffix { Nothing, Number { value: Decimal, token: Token }, String { value: Arc, token: Token }, Identifier { name: Arc, token: Token }, List(List), } #[derive(Debug, Clone, PartialEq, Eq)] pub struct List { pub left_paren: Token, pub items: Arc<[ListItem]>, pub right_paren: Option, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ListItem { Dot(Token), Expression(Expression), } impl Command { pub fn is_empty(&self) -> bool { if let Self::PanicMode(tokens) = self { tokens.is_empty() } else { false } } pub fn expression(&self) -> Option<&Expression> { if let Self::Expression(expr) = self { Some(expr) } else { None } } pub fn statement(&self) -> Option<&Statement> { if let Self::Statement(statement) = self { Some(statement) } else { None } } } impl Expression { pub fn is_quoted(&self) -> bool { self.prefix.len() == 1 && self.prefix[0].is_apostrophe() } } impl ExpressionSuffix { pub fn is_empty(&self) -> bool { matches!(self, Self::Nothing) } pub fn number(&self) -> Option<&Decimal> { if let Self::Number { value, .. } = self { Some(value) } else { None } } pub fn string(&self) -> Option<&Arc> { if let Self::String { value, .. } = self { Some(value) } else { None } } pub fn identifier(&self) -> Option<&Arc> { if let Self::Identifier { name, .. } = self { Some(name) } else { None } } pub fn list(&self) -> Option<&List> { if let Self::List(list) = self { Some(list) } else { None } } } impl List { pub fn contains_dot(&self) -> bool { self.items.iter().any(|item| item.dot().is_some()) } } impl ListItem { pub fn dot(&self) -> Option<&Token> { if let Self::Dot(token) = self { Some(token) } else { None } } pub fn expression(&self) -> Option<&Expression> { if let Self::Expression(expression) = self { Some(expression) } else { None } } } fn parse_many( lexer: &mut Peekable, predicate: fn(&Token) -> bool, parser: fn(&mut Peekable) -> Result, ) -> Result, String> { let mut items = Vec::new(); while let Some(next) = lexer.peek() { if !predicate(next) { break; } items.push(parser(lexer)?) } Ok(items) } fn parse_if( lexer: &mut Peekable, predicate: fn(&Token) -> bool, parser: fn(&mut Peekable) -> Result, ) -> Option> { let next = lexer.peek()?; predicate(next).then(|| parser(lexer)) } fn skip_whitespace_and_comments(lexer: &mut Peekable) { while lexer .next_if(|token| token.is_comment() || token.is_whitespace()) .is_some() {} } fn skip_spaces_and_comments(lexer: &mut Peekable) { while lexer .next_if(|token: &Token| { (token.is_whitespace() && !token.contains_newline()) || token.is_comment() }) .is_some() {} } fn skip_non_expression_tokens(lexer: &mut Peekable) -> Vec { let mut tokens = Vec::new(); while let Some(token) = lexer.next_if(|token| !begins_expression(token)) { tokens.push(token); } tokens } fn begins_expression(token: &Token) -> bool { match &token.ty { crate::tokens::TokenType::Whitespace(_) => false, crate::tokens::TokenType::LineComment(_) => false, crate::tokens::TokenType::BlockComment { .. } => false, crate::tokens::TokenType::LeftParenthesis => true, crate::tokens::TokenType::RightParenthesis => false, crate::tokens::TokenType::Apostrophe => true, crate::tokens::TokenType::Pound => true, crate::tokens::TokenType::Dot => false, crate::tokens::TokenType::Identifier(_) => true, crate::tokens::TokenType::String { .. } => true, crate::tokens::TokenType::Number(_) => true, } } fn begins_list_item(token: &Token) -> bool { begins_expression(token) || token.is_dot() } pub fn parse_program(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); let mut commands = parse_many(lexer, |_| true, parse_command)?; if commands.last().map(|c| c.is_empty()).unwrap_or(false) { commands.pop(); } let commands = commands.into(); Ok(Program { commands }) } fn parse_command(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); if let Some(result) = parse_if(lexer, |token| token.is_identifier(), parse_statement) { result.map(Command::Statement) } else if let Some(result) = parse_if(lexer, begins_expression, parse_expression) { result.map(Command::Expression) } else { Ok(Command::PanicMode(skip_non_expression_tokens(lexer).into())) } } fn parse_statement(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); let function_token = lexer .next() .ok_or_else(|| String::from("expected a function name"))?; let function_name = function_token .identifier() .ok_or_else(|| String::from("expected the function name to be an identifier"))? .into(); skip_spaces_and_comments(lexer); let args = parse_many(lexer, begins_expression, parse_expression)?.into(); skip_spaces_and_comments(lexer); let newline = lexer.next_if(|token| token.contains_newline()); Ok(Statement { function_name, function_token, args, newline, }) } fn parse_expression(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); let prefix = parse_many( lexer, |token| token.is_pound() || token.is_apostrophe(), |parser| { let result = parser.next().ok_or(String::new()); skip_whitespace_and_comments(parser); result }, )? .into(); skip_whitespace_and_comments(lexer); let suffix = parse_expression_suffix(lexer)?; skip_spaces_and_comments(lexer); Ok(Expression { prefix, suffix }) } fn parse_expression_suffix(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); if let Some(list) = parse_if(lexer, |token| token.is_left_parenthesis(), parse_list) { list.map(ExpressionSuffix::List) } else if let Some(identifier) = lexer.next_if(|token| token.is_identifier()) { Ok(ExpressionSuffix::Identifier { name: identifier .identifier() .ok_or_else(|| String::from("we just checked for an identifier"))? .into(), token: identifier, }) } else if let Some(string) = lexer.next_if(|token| token.is_string()) { Ok(ExpressionSuffix::String { value: string .computed_string() .ok_or_else(|| String::from("we just checked for an string"))? .into(), token: string, }) } else if let Some(number) = lexer.next_if(|token| token.is_number()) { Ok(ExpressionSuffix::Number { value: *number .number() .ok_or_else(|| String::from("we just checked for a number"))?, token: number, }) } else { Ok(ExpressionSuffix::Nothing) } } fn parse_list(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); let left_paren = lexer .next_if(|token| token.is_left_parenthesis()) .ok_or_else(|| String::from("Unexpected token. Expected a left parenthesis"))?; skip_whitespace_and_comments(lexer); let items = parse_many(lexer, begins_list_item, parse_list_item)?.into(); skip_whitespace_and_comments(lexer); let right_paren = lexer.next_if(|token| token.is_right_parenthesis()); Ok(List { left_paren, items, right_paren, }) } fn parse_list_item(lexer: &mut Peekable) -> Result { skip_whitespace_and_comments(lexer); if let Some(dot) = lexer.next_if(|token| token.is_dot()) { skip_whitespace_and_comments(lexer); Ok(ListItem::Dot(dot)) } else { let result = parse_expression(lexer).map(ListItem::Expression); skip_whitespace_and_comments(lexer); result } }