Compare commits
2 Commits
61de3b100f
...
02f3c9aee6
Author | SHA1 | Date | |
---|---|---|---|
|
02f3c9aee6 | ||
|
6b65cd4fd4 |
@ -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();
|
||||||
|
|
||||||
|
9
src/codegen/function_call.rs
Normal file
9
src/codegen/function_call.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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)]
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
42
src/syntax/expression/function_call_expr.rs
Normal file
42
src/syntax/expression/function_call_expr.rs
Normal 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 {
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +1,3 @@
|
|||||||
pub mod arguments_list;
|
pub mod arguments_list;
|
||||||
pub mod function_call;
|
|
||||||
pub mod function_declaration;
|
pub mod function_declaration;
|
||||||
|
@ -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()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
})
|
})
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user