diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 878496b..ea47282 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -82,22 +82,37 @@ impl Expr { src, } } + + pub fn call(callee: Expr, arguments: Vec, location: SourceSpan) -> Self { + let src = callee.src.clone(); + Self { + expr_type: ExprType::call(callee, arguments), + location, + src, + } + } } impl Display for Expr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use ExprType::*; match &self.expr_type { - ExprType::Binary(left, token, right) => { - write!(f, "({} {} {})", token.token_type, left, right) - } - ExprType::Logical(left, token, right) => { + Binary(left, token, right) => write!(f, "({} {} {})", token.token_type, left, right), + Logical(left, token, right) => { write!(f, "(Logical {} {} {})", token.token_type, left, right) } - ExprType::Grouping(expr) => write!(f, "(group {})", expr), - ExprType::Literal(literal) => write!(f, "({})", literal), - ExprType::Unary(token, expr) => write!(f, "({} {})", token.token_type, expr), - ExprType::Variable(name) => write!(f, "(variable {})", name.0), - ExprType::Assign(name, right) => write!(f, "({}={})", name.name, right), + Grouping(expr) => write!(f, "(group {})", expr), + Literal(literal) => write!(f, "({})", literal), + Unary(token, expr) => write!(f, "({} {})", token.token_type, expr), + Variable(name) => write!(f, "(variable {})", name.0), + Assign(name, right) => write!(f, "({}={})", name.name, right), + Call(callee, arguments) => { + write!(f, "(Call {}=>(", callee)?; + arguments + .iter() + .try_for_each(|arg| write!(f, "{}, ", arg))?; + write!(f, "))") + } } } } @@ -111,6 +126,7 @@ pub enum ExprType { Literal(Literal), Unary(Token, Box), Variable(Name), + Call(Box, Vec), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -162,6 +178,10 @@ impl ExprType { pub fn assign(name: NameExpr, expr: Expr) -> ExprType { Self::Assign(name, Box::new(expr)) } + + pub fn call(callee: Expr, arguments: Vec) -> ExprType { + Self::Call(callee.into(), arguments) + } } #[derive(Debug, Clone)] diff --git a/src/interpreter/expression.rs b/src/interpreter/expression.rs index 82e3006..8d778ad 100644 --- a/src/interpreter/expression.rs +++ b/src/interpreter/expression.rs @@ -21,6 +21,7 @@ impl Interpreter { Unary(token, expr) => self.interpret_unary(token, expr), Variable(name) => self.read_variable(name, expr), Assign(name, expr) => self.assign_variable(name, expr), + Call(callee, arguments) => todo!(), } } diff --git a/src/parser/expression.rs b/src/parser/expression.rs index b733f46..65c6f11 100644 --- a/src/parser/expression.rs +++ b/src/parser/expression.rs @@ -2,7 +2,8 @@ use crate::ast::expr::Literal::{self}; use crate::ast::expr::{Expr, NameExpr}; use crate::ast::token::Token; use crate::ast::{expr::ExprType, token::TokenType}; -use crate::parser::macros::consume; +use crate::parser::macros::{check, consume}; +use crate::parser::parser_error::ParserError; use crate::source_span_extensions::SourceSpanExtensions; use super::parser_error::ParserError::*; @@ -103,10 +104,52 @@ impl Parser { if let Some(token) = match_token!(self, Bang | Minus).cloned() { Ok(Expr::unary(token, self.unary()?)) } else { - self.primary() + self.call() } } + fn call(&mut self) -> Result { + use TokenType::*; + let mut expr = self.primary()?; + while match_token!(self, LeftParen).is_some() { + expr = self.finish_call(expr)?; + } + Ok(expr) + } + + fn finish_call(&mut self, callee: Expr) -> Result { + use TokenType::*; + let callee_location = callee.location; + let mut arguments = vec![]; + if !check!(self, RightParen) { + loop { + if arguments.len() >= 255 { + self.errors.push(ParserError::TooManyArguments { + src: self.peek().src.clone(), + location: self.peek().location, + }) + } + arguments.push(self.expression()?); + if match_token!(self, Comma).is_none() { + break; + } + } + } + + let right_paran = consume!(self, RightParen, |t: &Token| { + ExpectedRightParen { + src: t.src.clone(), + location: self.previous_if_eof(t.location), + } + }); + + Ok(Expr::call( + callee, + arguments, + callee_location.until(right_paran.location), + )) + } + fn primary(&mut self) -> Result { use TokenType::*; let token = self.advance().clone(); @@ -305,4 +348,36 @@ mod tests { } ) } + #[test] + fn parse_multi_argument_lists() { + let name: String = "name".into(); + let tokens = vec![ + token(TokenType::Identifier(name.clone())), + token(TokenType::LeftParen), + token(TokenType::True), + token(TokenType::RightParen), + token(TokenType::LeftParen), + token(TokenType::True), + token(TokenType::RightParen), + token(TokenType::Eof), + ]; + let expr = parse_expr(tokens).unwrap(); + assert_eq!( + expr.to_string().trim_end(), + "(Call (Call (variable name)=>((true), ))=>((true), ))" + ) + } + + #[test] + fn parse_empty_argument_list() { + let name: String = "name".into(); + let tokens = vec![ + token(TokenType::Identifier(name.clone())), + token(TokenType::LeftParen), + token(TokenType::RightParen), + token(TokenType::Eof), + ]; + let expr = parse_expr(tokens).unwrap(); + assert_eq!(expr.to_string().trim_end(), "(Call (variable name)=>())") + } } diff --git a/src/parser/macros.rs b/src/parser/macros.rs index cef851d..d9cb09d 100644 --- a/src/parser/macros.rs +++ b/src/parser/macros.rs @@ -19,5 +19,12 @@ macro_rules! consume { }}; } +macro_rules! check { + ($self:ident, $pattern:pat $(if $guard:expr)?) => { + matches!($self.peek().token_type, $pattern $(if $guard)?) + }; +} + +pub(super) use check; pub(super) use consume; pub(super) use match_token; diff --git a/src/parser/parser_error.rs b/src/parser/parser_error.rs index 4b4f828..c02817b 100644 --- a/src/parser/parser_error.rs +++ b/src/parser/parser_error.rs @@ -69,6 +69,14 @@ pub enum ParserError { #[label("not a valid target")] location: SourceSpan, }, + + #[error("Too many arguments (max 255)")] + TooManyArguments { + #[source_code] + src: Arc>, + #[label("this one is one too many")] + location: SourceSpan, + }, } #[derive(thiserror::Error, Debug, Diagnostic)] diff --git a/src/parser/statement.rs b/src/parser/statement.rs index 4243da6..d695ba5 100644 --- a/src/parser/statement.rs +++ b/src/parser/statement.rs @@ -10,7 +10,7 @@ use crate::source_span_extensions::SourceSpanExtensions; use super::parser_error::ParserError::{self, *}; -use super::macros::{consume, match_token}; +use super::macros::{check, consume, match_token}; use super::{Parser, Result}; struct InternalBlock { @@ -166,7 +166,7 @@ impl Parser { _ => Some(self.expression_statement()?), }; - let condition = if !matches!(self.peek().token_type, Semicolon) { + let condition = if !check!(self, Semicolon) { self.expression()? } else { Expr { @@ -178,7 +178,7 @@ impl Parser { consume!(self, Semicolon, |t| self.expected_semicolon(t)); - let increment = if !matches!(self.peek().token_type, RightParen) { + let increment = if !check!(self, RightParen) { Some(self.expression()?) } else { None @@ -223,7 +223,7 @@ impl Parser { fn block(&mut self) -> Result { let left_brace_location = self.advance().location; let mut stmts = vec![]; - while !matches!(self.peek().token_type, TokenType::RightBrace) && !self.is_at_end() { + while !check!(self, TokenType::RightBrace) && !self.is_at_end() { stmts.push(self.declaration()?); }