From 02f3c9aee622a4e2cccf106aa90a0b048efbbe86 Mon Sep 17 00:00:00 2001 From: Araozu Date: Tue, 2 Jan 2024 12:45:45 -0500 Subject: [PATCH] Use expressions as a function call callable --- src/codegen/expression.rs | 4 +- src/codegen/function_call.rs | 2 +- src/codegen/function_declaration.rs | 2 +- src/codegen/module_ast.rs | 2 +- src/lexic/scanner/new_line.rs | 2 +- src/syntax/ast/functions.rs | 4 +- src/syntax/block.rs | 15 ++++ src/syntax/expression/function_call_expr.rs | 42 +++++++++ src/syntax/expression/mod.rs | 1 + src/syntax/expression/primary.rs | 25 ++---- src/syntax/expression/unary.rs | 8 +- src/syntax/functions/function_call.rs | 95 --------------------- src/syntax/functions/mod.rs | 2 +- src/syntax/grammar.md | 7 +- src/syntax/statement.rs | 16 +++- src/syntax/utils.rs | 28 ++++++ 16 files changed, 127 insertions(+), 128 deletions(-) create mode 100644 src/syntax/expression/function_call_expr.rs delete mode 100644 src/syntax/functions/function_call.rs diff --git a/src/codegen/expression.rs b/src/codegen/expression.rs index e2097a6..3ce806a 100644 --- a/src/codegen/expression.rs +++ b/src/codegen/expression.rs @@ -17,7 +17,7 @@ impl Transpilable for Expression { } Expression::Boolean(value) => String::from(if *value { "true" } else { "false" }), Expression::Identifier(value) => format!("{}", *value), - Expression::FunctionCall(_) => todo!("FunctionCall codegen is not implemented yet"), + Expression::FunctionCall(f) => f.transpile(), Expression::BinaryOperator(_, _, _) => { todo!("BinaryOperator codegen is not implemented yet") } @@ -44,7 +44,7 @@ mod tests { #[test] fn should_transpile_string() { - let str = String::from("Hello world"); + let str = String::from("\"Hello world\""); let exp = Expression::String(Box::new(str)); let result = exp.transpile(); diff --git a/src/codegen/function_call.rs b/src/codegen/function_call.rs index 33b9f12..11b111e 100644 --- a/src/codegen/function_call.rs +++ b/src/codegen/function_call.rs @@ -4,6 +4,6 @@ use super::Transpilable; impl Transpilable for FunctionCall { fn transpile(&self) -> String { - format!("{}();", self.identifier) + format!("{}();", self.function.transpile()) } } diff --git a/src/codegen/function_declaration.rs b/src/codegen/function_declaration.rs index f02d3b7..d0c4359 100644 --- a/src/codegen/function_declaration.rs +++ b/src/codegen/function_declaration.rs @@ -32,7 +32,7 @@ mod tests { TopLevelDeclaration::FunctionDeclaration(fun_decl) => { let transpiled = fun_decl.transpile(); - assert_eq!("function id() {}", transpiled); + assert_eq!("function id() {\n\n}", transpiled); } } } diff --git a/src/codegen/module_ast.rs b/src/codegen/module_ast.rs index b9eab6f..2b70d25 100644 --- a/src/codegen/module_ast.rs +++ b/src/codegen/module_ast.rs @@ -37,6 +37,6 @@ mod tests { let result = module.transpile(); - assert_eq!("$identifier = 322;", result); + assert_eq!(", start_pos: usize) -> LexResult { Some(c) if *c == ' ' => match look_ahead_for_new_line(chars, start_pos + 1) { Some(next_pos) => scan(chars, next_pos), None => { - let token = Token::new(String::from(";"), start_pos, TokenType::NewLine); + let token = Token::new(String::from(""), start_pos, TokenType::NewLine); LexResult::Some(token, start_pos) } }, diff --git a/src/syntax/ast/functions.rs b/src/syntax/ast/functions.rs index 91269e4..6932939 100644 --- a/src/syntax/ast/functions.rs +++ b/src/syntax/ast/functions.rs @@ -1,6 +1,8 @@ +use super::Expression; + #[derive(Debug)] pub struct FunctionCall { - pub identifier: Box, + pub function: Box, } #[derive(Debug)] diff --git a/src/syntax/block.rs b/src/syntax/block.rs index eea3256..31c3e91 100644 --- a/src/syntax/block.rs +++ b/src/syntax/block.rs @@ -104,4 +104,19 @@ mod tests { assert_eq!(block.statements.len(), 2); } + + #[test] + fn test_parse_block_3() { + let tokens = get_tokens(&String::from("{\n f()\n}")).unwrap(); + let block = parse_block(&tokens, 0); + + let block = match block { + ParseResult::Ok(p, _) => p, + _ => { + panic!("Expected a block, got: {:?}\n\n{:?}", block, tokens) + }, + }; + + assert_eq!(block.statements.len(), 1); + } } diff --git a/src/syntax/expression/function_call_expr.rs b/src/syntax/expression/function_call_expr.rs new file mode 100644 index 0000000..4bd2d8d --- /dev/null +++ b/src/syntax/expression/function_call_expr.rs @@ -0,0 +1,42 @@ +use crate::{ + lexic::token::Token, + syntax::{ + ast::{functions::FunctionCall, Expression}, + functions::arguments_list, + ParseResult, + }, +}; + +/// Parses a function call expression. +/// +/// ```ebnf +/// function call expr = primary, "(", (arguments list)?, ")" +/// | primary; +/// ``` +pub fn try_parse(tokens: &Vec, pos: usize) -> ParseResult { + let (primary_expr, next_pos) = match super::primary::try_parse(tokens, pos) { + ParseResult::Ok(expr, next_pos) => (expr, next_pos), + _ => return ParseResult::Unmatched, + }; + + // Parse arguments list + let (_args_list, next_pos) = match arguments_list::try_parse(tokens, next_pos) { + ParseResult::Ok(args, next) => (args, next), + ParseResult::Err(err) => return ParseResult::Err(err), + _ => { + return ParseResult::Ok(primary_expr, next_pos); + } + }; + + let fun_call = FunctionCall { + function: Box::new(primary_expr), + }; + + ParseResult::Ok(Expression::FunctionCall(fun_call), next_pos) +} + + +#[cfg(test)] +mod test { + +} diff --git a/src/syntax/expression/mod.rs b/src/syntax/expression/mod.rs index aab819f..308f8fe 100644 --- a/src/syntax/expression/mod.rs +++ b/src/syntax/expression/mod.rs @@ -4,6 +4,7 @@ use crate::lexic::token::Token; mod comparison; mod equality; mod factor; +pub mod function_call_expr; mod primary; mod term; mod unary; diff --git a/src/syntax/expression/primary.rs b/src/syntax/expression/primary.rs index ddd4913..5640c2d 100644 --- a/src/syntax/expression/primary.rs +++ b/src/syntax/expression/primary.rs @@ -2,6 +2,7 @@ use crate::{ lexic::token::{Token, TokenType}, syntax::{ast::Expression, ParseResult}, }; +use super::super::utils::Tokenizer; /// This grammar may not be up to date. Refer to the spec for the latest grammar. /// @@ -9,32 +10,22 @@ use crate::{ /// primary = number | string | boolean | identifier | ("(", expression, ")"); /// ``` pub fn try_parse(tokens: &Vec, pos: usize) -> ParseResult { - /* - TODO: Incorporate function_call into the grammar, figure out its precedence. - match function_call::try_parse(tokens, pos) { - super::ParseResult::Ok(function_call, next_pos) => { - return ParseResult::Ok::<_, ()>(Expression::FunctionCall(function_call), next_pos) - } - _ => {} - }; - */ - - match tokens.get(pos) { - Some(token) => match token.token_type { + match tokens.get_significant(pos) { + Some((token, token_pos)) => match token.token_type { TokenType::Number => { - ParseResult::Ok(Expression::Number(Box::new(token.value.clone())), pos + 1) + ParseResult::Ok(Expression::Number(Box::new(token.value.clone())), token_pos + 1) } TokenType::String => { - ParseResult::Ok(Expression::String(Box::new(token.value.clone())), pos + 1) + ParseResult::Ok(Expression::String(Box::new(token.value.clone())), token_pos + 1) } TokenType::Identifier if token.value == "true" || token.value == "false" => { - ParseResult::Ok(Expression::Boolean(token.value == "true"), pos + 1) + ParseResult::Ok(Expression::Boolean(token.value == "true"), token_pos + 1) } TokenType::Identifier => ParseResult::Ok( Expression::Identifier(Box::new(token.value.clone())), - pos + 1, + token_pos + 1, ), - TokenType::LeftParen => parse_parenthesized_expression(tokens, pos), + TokenType::LeftParen => parse_parenthesized_expression(tokens, token_pos), _ => ParseResult::Unmatched, }, None => ParseResult::Unmatched, diff --git a/src/syntax/expression/unary.rs b/src/syntax/expression/unary.rs index 83c0fb0..6cb4470 100644 --- a/src/syntax/expression/unary.rs +++ b/src/syntax/expression/unary.rs @@ -1,13 +1,15 @@ use crate::{ lexic::token::Token, - syntax::{ast::Expression, expression::primary, ParseResult}, + syntax::{ast::Expression, ParseResult}, }; +use super::function_call_expr; + /// Parses an unary expression. /// /// ```ebnf /// unary = ("!" | "-"), expression -/// | primary; +/// | function call expr; /// ``` pub fn try_parse(tokens: &Vec, pos: usize) -> ParseResult { match tokens.get(pos) { @@ -20,6 +22,6 @@ pub fn try_parse(tokens: &Vec, pos: usize) -> ParseResult _ => ParseResult::Unmatched, } } - _ => primary::try_parse(tokens, pos), + _ => function_call_expr::try_parse(tokens, pos), } } diff --git a/src/syntax/functions/function_call.rs b/src/syntax/functions/function_call.rs deleted file mode 100644 index f578388..0000000 --- a/src/syntax/functions/function_call.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::{ - lexic::token::{Token, TokenType}, - syntax::{ast::functions::FunctionCall, utils::parse_token_type, ParseResult}, -}; - -pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParseResult { - let mut current_pos = pos; - - // TODO: Use an expression instead of a fixed identifier - // Parse identifier - let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier) - { - ParseResult::Ok(id, next) => (id, next), - ParseResult::Err(err) => return ParseResult::Err(err), - ParseResult::Mismatch(_) => { - return ParseResult::Unmatched; - } - ParseResult::Unmatched => { - return ParseResult::Unmatched; - } - }; - current_pos = next_pos; - - // Parse arguments list - let (_args_list, next_pos) = match super::arguments_list::try_parse(tokens, current_pos) { - ParseResult::Ok(args, next) => (args, next), - ParseResult::Err(err) => return ParseResult::Err(err), - ParseResult::Mismatch(_) => { - return ParseResult::Unmatched; - } - ParseResult::Unmatched => { - return ParseResult::Unmatched; - } - }; - current_pos = next_pos; - - ParseResult::Ok( - FunctionCall { - identifier: Box::new(identifier.value.clone()), - }, - current_pos, - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::lexic::get_tokens; - - #[test] - fn should_not_parse_identifier_alone() { - let tokens = get_tokens(&String::from("function_name")).unwrap(); - let fun_decl = try_parse(&tokens, 0); - - let ParseResult::Unmatched = fun_decl else { - panic!("Expected an unmatched result: {:?}", fun_decl); - }; - } - - #[test] - fn should_parse_minimal_construct() { - let tokens = get_tokens(&String::from("function_name()")).unwrap(); - let fun_decl = try_parse(&tokens, 0); - - let ParseResult::Ok(_call, next) = fun_decl else { - panic!("Expected a result, got: {:?}", fun_decl); - }; - - assert_eq!(next, 3); - } - - #[test] - fn should_parse_minimal_construct_2() { - let tokens = get_tokens(&String::from("function_name ( )")).unwrap(); - let fun_decl = try_parse(&tokens, 0); - - let ParseResult::Ok(_call, next) = fun_decl else { - panic!("Expected a result, got: {:?}", fun_decl); - }; - - assert_eq!(next, 3); - } - - #[test] - fn should_parse_minimal_construct_3() { - let tokens = get_tokens(&String::from("function_name\n(\n \n)")).unwrap(); - let fun_decl = try_parse(&tokens, 0); - - let ParseResult::Ok(_call, next) = fun_decl else { - panic!("Expected a result, got: {:?}", fun_decl); - }; - - assert_eq!(next, 5); - } -} diff --git a/src/syntax/functions/mod.rs b/src/syntax/functions/mod.rs index 0b74664..f4207d0 100644 --- a/src/syntax/functions/mod.rs +++ b/src/syntax/functions/mod.rs @@ -1,3 +1,3 @@ pub mod arguments_list; -pub mod function_call; + pub mod function_declaration; diff --git a/src/syntax/grammar.md b/src/syntax/grammar.md index 5aaad8c..ed4f933 100644 --- a/src/syntax/grammar.md +++ b/src/syntax/grammar.md @@ -85,11 +85,14 @@ factor = unary, (("/" | "*"), unary)*; unary = ("!" | "-"), expression | primary; -function call = primary, (arguments list)?; +function call expr = primary, (arguments list)? + | primary; primary = number | string | boolean | identifier | ("(", expression, ")"); ``` - +```thp +primary() +``` diff --git a/src/syntax/statement.rs b/src/syntax/statement.rs index a7dfd8c..c2f48f0 100644 --- a/src/syntax/statement.rs +++ b/src/syntax/statement.rs @@ -1,6 +1,11 @@ use crate::lexic::token::Token; -use super::{ast::statement::Statement, binding, functions::function_call, ParseResult}; +use super::{ + ast::{statement::Statement, Expression}, + binding, + expression::function_call_expr, + ParseResult, +}; pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParseResult { None.or_else(|| match binding::try_parse(tokens, pos) { @@ -8,8 +13,13 @@ pub fn try_parse<'a>(tokens: &'a Vec, pos: usize) -> ParseResult Some(ParseResult::Err(err)), _ => None, }) - .or_else(|| match function_call::try_parse(tokens, pos) { - ParseResult::Ok(f, next) => Some(ParseResult::Ok(Statement::FunctionCall(f), next)), + .or_else(|| match function_call_expr::try_parse(tokens, pos) { + ParseResult::Ok(f, next) => { + let Expression::FunctionCall(f) = f else { + return None; + }; + Some(ParseResult::Ok(Statement::FunctionCall(f), next)) + } ParseResult::Err(err) => Some(ParseResult::Err(err)), _ => None, }) diff --git a/src/syntax/utils.rs b/src/syntax/utils.rs index df3fc96..b5057a6 100644 --- a/src/syntax/utils.rs +++ b/src/syntax/utils.rs @@ -5,6 +5,34 @@ use crate::{ use super::ParseResult; +pub trait Tokenizer { + fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>; +} + +impl Tokenizer for Vec { + /// Returns the first non whitespace token at index & the position the found token + fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)> { + let mut current_pos = index; + + // Ignore all whitespace and newlines + loop { + match self.get(current_pos) { + Some(token) => { + if token.token_type == TokenType::INDENT + || token.token_type == TokenType::DEDENT + || token.token_type == TokenType::NewLine + { + current_pos += 1; + } else { + return Some((token, current_pos)); + } + } + None => return None, + } + } + } +} + /// Expects the token at `pos` to be of type `token_type` pub fn try_token_type(tokens: &Vec, pos: usize, token_type: TokenType) -> Result3<&Token> { match tokens.get(pos) {