Skip to content

Commit

Permalink
Implement parser for function calls
Browse files Browse the repository at this point in the history
  • Loading branch information
froth committed Mar 10, 2024
1 parent 3e2270c commit 84f942b
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 15 deletions.
38 changes: 29 additions & 9 deletions src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,37 @@ impl Expr {
src,
}
}

pub fn call(callee: Expr, arguments: Vec<Expr>, 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, "))")
}
}
}
}
Expand All @@ -111,6 +126,7 @@ pub enum ExprType {
Literal(Literal),
Unary(Token, Box<Expr>),
Variable(Name),
Call(Box<Expr>, Vec<Expr>),
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -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<Expr>) -> ExprType {
Self::Call(callee.into(), arguments)
}
}

#[derive(Debug, Clone)]
Expand Down
1 change: 1 addition & 0 deletions src/interpreter/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(),
}
}

Expand Down
79 changes: 77 additions & 2 deletions src/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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<Expr> {
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<Expr> {
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<Expr> {
use TokenType::*;
let token = self.advance().clone();
Expand Down Expand Up @@ -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)=>())")
}
}
7 changes: 7 additions & 0 deletions src/parser/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
8 changes: 8 additions & 0 deletions src/parser/parser_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<NamedSource<String>>,
#[label("this one is one too many")]
location: SourceSpan,
},
}

#[derive(thiserror::Error, Debug, Diagnostic)]
Expand Down
8 changes: 4 additions & 4 deletions src/parser/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -223,7 +223,7 @@ impl Parser {
fn block(&mut self) -> Result<InternalBlock> {
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()?);
}

Expand Down

0 comments on commit 84f942b

Please sign in to comment.