Compare commits

...

2 Commits

Author SHA1 Message Date
Araozu 02f3c9aee6 Use expressions as a function call callable 2024-01-02 12:45:45 -05:00
Araozu 6b65cd4fd4 Simple codegen for function calls 2024-01-02 09:50:48 -05:00
18 changed files with 141 additions and 129 deletions

View File

@ -13,11 +13,11 @@ impl Transpilable for Expression {
match self { match self {
Expression::Number(value) => format!("{}", value), Expression::Number(value) => format!("{}", value),
Expression::String(value) => { Expression::String(value) => {
format!("\"{}\"", *value) format!("{}", *value)
} }
Expression::Boolean(value) => String::from(if *value { "true" } else { "false" }), Expression::Boolean(value) => String::from(if *value { "true" } else { "false" }),
Expression::Identifier(value) => format!("{}", *value), Expression::Identifier(value) => format!("{}", *value),
Expression::FunctionCall(_) => todo!("FunctionCall codegen is not implemented yet"), Expression::FunctionCall(f) => f.transpile(),
Expression::BinaryOperator(_, _, _) => { Expression::BinaryOperator(_, _, _) => {
todo!("BinaryOperator codegen is not implemented yet") todo!("BinaryOperator codegen is not implemented yet")
} }
@ -44,7 +44,7 @@ mod tests {
#[test] #[test]
fn should_transpile_string() { fn should_transpile_string() {
let str = String::from("Hello world"); let str = String::from("\"Hello world\"");
let exp = Expression::String(Box::new(str)); let exp = Expression::String(Box::new(str));
let result = exp.transpile(); let result = exp.transpile();

View File

@ -0,0 +1,9 @@
use crate::syntax::ast::functions::FunctionCall;
use super::Transpilable;
impl Transpilable for FunctionCall {
fn transpile(&self) -> String {
format!("{}();", self.function.transpile())
}
}

View File

@ -32,7 +32,7 @@ mod tests {
TopLevelDeclaration::FunctionDeclaration(fun_decl) => { TopLevelDeclaration::FunctionDeclaration(fun_decl) => {
let transpiled = fun_decl.transpile(); let transpiled = fun_decl.transpile();
assert_eq!("function id() {}", transpiled); assert_eq!("function id() {\n\n}", transpiled);
} }
} }
} }

View File

@ -3,6 +3,7 @@ use crate::syntax::ast::ModuleAST;
mod binding; mod binding;
mod block; mod block;
mod expression; mod expression;
mod function_call;
mod function_declaration; mod function_declaration;
mod module_ast; mod module_ast;
mod statement; mod statement;

View File

@ -37,6 +37,6 @@ mod tests {
let result = module.transpile(); let result = module.transpile();
assert_eq!("$identifier = 322;", result); assert_eq!("<?php\n\n$identifier = 322;\n", result);
} }
} }

View File

@ -4,6 +4,9 @@ use super::Transpilable;
impl Transpilable for Statement { impl Transpilable for Statement {
fn transpile(&self) -> String { fn transpile(&self) -> String {
String::from("// TODO (statement)") match self {
Statement::Binding(binding) => binding.transpile(),
Statement::FunctionCall(function_call) => function_call.transpile(),
}
} }
} }

View File

@ -12,7 +12,7 @@ pub fn scan(chars: &Vec<char>, start_pos: usize) -> LexResult {
Some(c) if *c == ' ' => match look_ahead_for_new_line(chars, start_pos + 1) { Some(c) if *c == ' ' => match look_ahead_for_new_line(chars, start_pos + 1) {
Some(next_pos) => scan(chars, next_pos), Some(next_pos) => scan(chars, next_pos),
None => { 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) LexResult::Some(token, start_pos)
} }
}, },

View File

@ -1,6 +1,8 @@
use super::Expression;
#[derive(Debug)] #[derive(Debug)]
pub struct FunctionCall { pub struct FunctionCall {
pub identifier: Box<String>, pub function: Box<Expression>,
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -104,4 +104,19 @@ mod tests {
assert_eq!(block.statements.len(), 2); 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);
}
} }

View File

@ -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<Token>, pos: usize) -> ParseResult<Expression, ()> {
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 {
}

View File

@ -4,6 +4,7 @@ use crate::lexic::token::Token;
mod comparison; mod comparison;
mod equality; mod equality;
mod factor; mod factor;
pub mod function_call_expr;
mod primary; mod primary;
mod term; mod term;
mod unary; mod unary;

View File

@ -2,6 +2,7 @@ use crate::{
lexic::token::{Token, TokenType}, lexic::token::{Token, TokenType},
syntax::{ast::Expression, ParseResult}, 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. /// 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, ")"); /// primary = number | string | boolean | identifier | ("(", expression, ")");
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
/* match tokens.get_significant(pos) {
TODO: Incorporate function_call into the grammar, figure out its precedence. Some((token, token_pos)) => match token.token_type {
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 {
TokenType::Number => { 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 => { 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" => { 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( TokenType::Identifier => ParseResult::Ok(
Expression::Identifier(Box::new(token.value.clone())), 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, _ => ParseResult::Unmatched,
}, },
None => ParseResult::Unmatched, None => ParseResult::Unmatched,

View File

@ -1,13 +1,15 @@
use crate::{ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ast::Expression, expression::primary, ParseResult}, syntax::{ast::Expression, ParseResult},
}; };
use super::function_call_expr;
/// Parses an unary expression. /// Parses an unary expression.
/// ///
/// ```ebnf /// ```ebnf
/// unary = ("!" | "-"), expression /// unary = ("!" | "-"), expression
/// | primary; /// | function call expr;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
match tokens.get(pos) { match tokens.get(pos) {
@ -20,6 +22,6 @@ pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()>
_ => ParseResult::Unmatched, _ => ParseResult::Unmatched,
} }
} }
_ => primary::try_parse(tokens, pos), _ => function_call_expr::try_parse(tokens, pos),
} }
} }

View File

@ -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<Token>, pos: usize) -> ParseResult<FunctionCall, ()> {
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);
}
}

View File

@ -1,3 +1,3 @@
pub mod arguments_list; pub mod arguments_list;
pub mod function_call;
pub mod function_declaration; pub mod function_declaration;

View File

@ -85,11 +85,14 @@ factor = unary, (("/" | "*"), unary)*;
unary = ("!" | "-"), expression unary = ("!" | "-"), expression
| primary; | primary;
function call = primary, (arguments list)?; function call expr = primary, (arguments list)?
| primary;
primary = number | string | boolean | identifier | ("(", expression, ")"); primary = number | string | boolean | identifier | ("(", expression, ")");
``` ```
```thp
primary()
```

View File

@ -1,6 +1,11 @@
use crate::lexic::token::Token; 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<Token>, pos: usize) -> ParseResult<Statement, ()> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Statement, ()> {
None.or_else(|| match binding::try_parse(tokens, pos) { None.or_else(|| match binding::try_parse(tokens, pos) {
@ -8,8 +13,13 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Statemen
ParseResult::Err(err) => Some(ParseResult::Err(err)), ParseResult::Err(err) => Some(ParseResult::Err(err)),
_ => None, _ => None,
}) })
.or_else(|| match function_call::try_parse(tokens, pos) { .or_else(|| match function_call_expr::try_parse(tokens, pos) {
ParseResult::Ok(f, next) => Some(ParseResult::Ok(Statement::FunctionCall(f), next)), 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)), ParseResult::Err(err) => Some(ParseResult::Err(err)),
_ => None, _ => None,
}) })

View File

@ -5,6 +5,34 @@ use crate::{
use super::ParseResult; use super::ParseResult;
pub trait Tokenizer {
fn get_significant<'a>(&'a self, index: usize) -> Option<(&'a Token, usize)>;
}
impl Tokenizer for Vec<Token> {
/// 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` /// Expects the token at `pos` to be of type `token_type`
pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> { pub fn try_token_type(tokens: &Vec<Token>, pos: usize, token_type: TokenType) -> Result3<&Token> {
match tokens.get(pos) { match tokens.get(pos) {