refactor: revert to old ast transformation strategy

master
Araozu 2024-08-26 16:13:54 -05:00
parent 912384c856
commit 974c380eaf
15 changed files with 203 additions and 86 deletions

View File

@ -1,18 +1,16 @@
use crate::{codegen::Transpilable, php_ast::PhpAssignmentExpression}; use crate::{
codegen::Transpilable,
php_ast::{php_ast_2::PSimpleAssignment, PhpAssignmentExpression},
};
impl Transpilable for PhpAssignmentExpression<'_> { impl Transpilable for PSimpleAssignment<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
match self { let variable_name = self.variable;
PhpAssignmentExpression::Primary(p) => p.transpile(), let expression_str = self.assignment.transpile();
PhpAssignmentExpression::SimpleAssignment(assignment) => {
let variable_name = &assignment.variable;
let expression_str = assignment.assignment.transpile();
format!("${} = {}", variable_name, expression_str) format!("${} = {}", variable_name, expression_str)
} }
} }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,2 +1,15 @@
use crate::{codegen::Transpilable, php_ast::php_ast_2::PExpresssion};
use PExpresssion::*;
mod assignment; mod assignment;
mod primary_expression; mod primary_expression;
impl Transpilable for PExpresssion<'_> {
fn transpile(&self) -> String {
match self {
Primary(p) => p.transpile(),
Assignment(a) => a.transpile(),
FunctionCall(f) => f.transpile(),
}
}
}

View File

@ -1,12 +1,16 @@
use crate::{codegen::Transpilable, php_ast::PhpPrimaryExpression}; use crate::{
codegen::Transpilable,
php_ast::{php_ast_2::PPrimary, PhpPrimaryExpression},
};
impl Transpilable for PhpPrimaryExpression<'_> { impl Transpilable for PPrimary<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
match self { match self {
PhpPrimaryExpression::IntegerLiteral(value) => value.to_string(), PPrimary::IntegerLiteral(value) => value.to_string(),
PhpPrimaryExpression::FloatingLiteral(value) => value.to_string(), PPrimary::FloatingLiteral(value) => value.to_string(),
PhpPrimaryExpression::StringLiteral(value) => format!("\"{}\"", value), PPrimary::StringLiteral(value) => format!("\"{}\"", value),
PhpPrimaryExpression::Variable(name) => format!("${}", name), PPrimary::Variable(name) => format!("${}", name),
PPrimary::Symbol(name) => format!("{}", name),
} }
} }
} }
@ -15,6 +19,7 @@ impl Transpilable for PhpPrimaryExpression<'_> {
mod tests { mod tests {
use crate::{codegen::Transpilable, php_ast::PhpPrimaryExpression}; use crate::{codegen::Transpilable, php_ast::PhpPrimaryExpression};
/*
#[test] #[test]
fn should_transpile_empty_string() { fn should_transpile_empty_string() {
let input = String::from(""); let input = String::from("");
@ -68,4 +73,5 @@ mod tests {
assert_eq!("$name", output) assert_eq!("$name", output)
} }
*/
} }

View File

@ -0,0 +1,9 @@
use crate::{codegen::Transpilable, php_ast::php_ast_2::PFunctionCall};
impl Transpilable for PFunctionCall<'_> {
fn transpile(&self) -> String {
let args: Vec<_> = self.arguments.iter().map(|a| a.transpile()).collect();
format!("{}({})", self.function_name, args.join(", "))
}
}

View File

@ -4,11 +4,4 @@ use crate::php_ast::PhpExpression;
mod expression; mod expression;
pub mod statement; pub mod statement;
pub mod statement_list; pub mod statement_list;
mod function;
impl Transpilable for PhpExpression<'_> {
fn transpile(&self) -> String {
match self {
PhpExpression::Assignment(p) => p.transpile(),
}
}
}

View File

@ -1,26 +1,12 @@
use crate::{codegen::Transpilable, php_ast::PhpStatement}; use crate::{
codegen::Transpilable,
mod echo_statement; php_ast::{php_ast_2::PStatement, PhpStatement},
impl Transpilable for PhpStatement<'_> {
fn transpile(&self) -> String {
match self {
PhpStatement::PhpEchoStatement(expr_list) => {
let expressions_vec = expr_list
.expressions
.iter()
.map(|e| e.transpile())
.collect::<Vec<_>>();
let expressions_str = if expressions_vec.is_empty() {
"\"\"".into()
} else {
expressions_vec.join(", ")
}; };
format!("echo {};", expressions_str) impl Transpilable for PStatement<'_> {
} fn transpile(&self) -> String {
PhpStatement::PhpExpressionStatement(expr) => { match self {
PStatement::ExpressionStatement(expr) => {
let expr_str = expr.transpile(); let expr_str = expr.transpile();
format!("{};", expr_str) format!("{};", expr_str)
} }
@ -38,6 +24,7 @@ mod tests {
}, },
}; };
/*
#[test] #[test]
fn should_gen_empty_echo_statement() { fn should_gen_empty_echo_statement() {
let expressions = PhpExpressionList { let expressions = PhpExpressionList {
@ -95,4 +82,5 @@ mod tests {
assert_eq!("\"Hi!\";", output) assert_eq!("\"Hi!\";", output)
} }
*/
} }

View File

@ -1,6 +1,9 @@
use crate::{codegen::Transpilable, php_ast::PhpAst}; use crate::{
codegen::Transpilable,
php_ast::{php_ast_2::PFile, PhpAst},
};
impl Transpilable for PhpAst<'_> { impl Transpilable for PFile<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
let mut fragments = vec![String::from("<?php\n")]; let mut fragments = vec![String::from("<?php\n")];
@ -17,13 +20,14 @@ mod tests {
use crate::{ use crate::{
codegen::Transpilable, codegen::Transpilable,
php_ast::{ php_ast::{
PhpAssignmentExpression, PhpAst, PhpExpression, PhpPrimaryExpression, PhpStatement, php_ast_2::PFile, PhpAssignmentExpression, PhpAst, PhpExpression, PhpPrimaryExpression,
PhpStatement,
}, },
}; };
#[test] #[test]
fn should_transpile_empty_file() { fn should_transpile_empty_file() {
let ast = PhpAst { statements: vec![] }; let ast = PFile { statements: vec![] };
let output = ast.transpile(); let output = ast.transpile();
assert_eq!("<?php\n", output); assert_eq!("<?php\n", output);

View File

@ -7,6 +7,8 @@ use crate::codegen::Transpilable;
/// THP /// THP
pub mod transformers; pub mod transformers;
pub mod php_ast_2;
type TranspilableBox<'a> = Box<(dyn Transpilable + 'a)>; type TranspilableBox<'a> = Box<(dyn Transpilable + 'a)>;
/// Represents `statement-list` on the grammar, /// Represents `statement-list` on the grammar,

56
src/php_ast/php_ast_2.rs Normal file
View File

@ -0,0 +1,56 @@
/// A single PHP source code file
pub struct PFile<'a> {
pub statements: Vec<PStatement<'a>>,
}
/// A PHP statement
pub enum PStatement<'a> {
ExpressionStatement(PExpressionStatement<'a>),
}
/// A statement composed of a single expression,
/// whose value is discarded
///
/// ## Examples
///
/// ```php
/// 10;
/// "hello";
/// ```
pub type PExpressionStatement<'a> = PExpresssion<'a>;
/// A generic PHP expression
pub enum PExpresssion<'a> {
FunctionCall(PFunctionCall<'a>),
Primary(PPrimary<'a>),
/// This comes from a THP binding
Assignment(PSimpleAssignment<'a>),
}
pub struct PSimpleAssignment<'a> {
pub variable: &'a String,
pub assignment: Box<PExpresssion<'a>>,
}
/// A function call as an expression
pub struct PFunctionCall<'a> {
/// Arbitrary expressions that compute into
/// a function not supported
pub function_name: &'a String,
pub arguments: Vec<PExpresssion<'a>>,
}
/// A Primary expression: literals and variables
pub enum PPrimary<'a> {
IntegerLiteral(&'a String),
FloatingLiteral(&'a String),
StringLiteral(&'a String),
/// https://phplang.org/spec/19-grammar.html#grammar-variable
///
/// Supports only variable -> callable-variable -> simple-variable -> variable-name
///
/// This is a $variable
Variable(&'a String),
/// This is a symbol, e.g. a function name
Symbol(&'a String),
}

View File

@ -1,6 +1,11 @@
use super::super::PhpExpression; use super::super::PhpExpression;
use crate::{ use crate::{
codegen::Transpilable, php_ast::{PhpAssignmentExpression, PhpPrimaryExpression}, syntax::ast::Expression codegen::Transpilable,
php_ast::{
php_ast_2::{PExpresssion, PFunctionCall, PPrimary},
PhpAssignmentExpression, PhpPrimaryExpression,
},
syntax::ast::Expression,
}; };
// TODO: next rewrite the test to use the output of Transpilable? // TODO: next rewrite the test to use the output of Transpilable?
@ -9,19 +14,30 @@ use super::PHPTransformable;
/// Transforms a THP expression into a PHP expression /// Transforms a THP expression into a PHP expression
impl<'a> PHPTransformable<'a> for Expression<'_> { impl<'a> PHPTransformable<'a> for Expression<'_> {
fn into_php_ast(&'a self) -> Box<(dyn Transpilable + 'a)> { type Item = PExpresssion<'a>;
fn into_php_ast(&'a self) -> PExpresssion<'a> {
match self { match self {
Expression::String(value) => { Expression::String(value) => {
let expr = PhpPrimaryExpression::StringLiteral(&value.value); let expr = PPrimary::StringLiteral(&value.value);
Box::new(PhpExpression::Assignment(PhpAssignmentExpression::Primary(expr)))
PExpresssion::Primary(expr)
} }
Expression::Int(value) => { Expression::Int(value) => {
let expr = PhpPrimaryExpression::IntegerLiteral(&value.value); let expr = PPrimary::IntegerLiteral(&value.value);
Box::new(PhpExpression::Assignment(PhpAssignmentExpression::Primary(expr))) PExpresssion::Primary(expr)
} }
Expression::Float(value) => { Expression::Float(value) => {
let expr = PhpPrimaryExpression::FloatingLiteral(&value.value); let expr = PPrimary::FloatingLiteral(&value.value);
Box::new(PhpExpression::Assignment(PhpAssignmentExpression::Primary(expr))) PExpresssion::Primary(expr)
}
Expression::FunctionCall(f) => {
let fn_call_expr = f.into_php_ast();
PExpresssion::FunctionCall(fn_call_expr)
}
Expression::Identifier(i) => {
PExpresssion::Primary(PPrimary::Variable(&i.value))
} }
_ => todo!("transformation for expression: {:?}", self), _ => todo!("transformation for expression: {:?}", self),
} }

View File

@ -0,0 +1,29 @@
use crate::{
php_ast::php_ast_2::PFunctionCall,
syntax::ast::{functions::FunctionCall, Expression},
};
use super::PHPTransformable;
impl<'a> PHPTransformable<'a> for FunctionCall<'a> {
type Item = PFunctionCall<'a>;
fn into_php_ast(&'a self) -> Self::Item {
let function_expr = match *self.function {
Expression::Identifier(i) => &i.value,
_ => panic!("Cannot use an arbitrary expression as a function, only identifiers (for now)"),
};
let expressions: Vec<_> = self
.arguments
.arguments
.iter()
.map(|a| a.into_php_ast())
.collect();
PFunctionCall {
function_name: function_expr,
arguments: expressions,
}
}
}

View File

@ -3,8 +3,11 @@ use crate::codegen::Transpilable;
pub mod expression; pub mod expression;
pub mod module_ast; pub mod module_ast;
pub mod statement; pub mod statement;
pub mod functions;
/// Implemented by AST nodes that can be transformed to PHP /// Implemented by AST nodes that can be transformed to PHP
pub trait PHPTransformable<'a> { pub trait PHPTransformable<'a> {
fn into_php_ast(&'a self) -> Box<(dyn Transpilable + 'a)>; type Item;
fn into_php_ast(&'a self) -> Self::Item;
} }

View File

@ -1,5 +1,6 @@
use super::super::PhpAst; use super::super::PhpAst;
use crate::codegen::Transpilable; use crate::codegen::Transpilable;
use crate::php_ast::php_ast_2::{PFile, PStatement};
use crate::php_ast::{ use crate::php_ast::{
PhpAssignmentExpression, PhpExpression, PhpExpressionList, PhpPrimaryExpression, PhpStatement, PhpAssignmentExpression, PhpExpression, PhpExpressionList, PhpPrimaryExpression, PhpStatement,
}; };
@ -9,8 +10,10 @@ use super::PHPTransformable;
/// Transforms a THP AST into a PHP AST /// Transforms a THP AST into a PHP AST
impl<'a> PHPTransformable<'a> for ModuleAST<'_> { impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
fn into_php_ast(&'a self) -> Box<(dyn Transpilable + 'a)>{ type Item = PFile<'a>;
let mut php_statements = Vec::<_>::new();
fn into_php_ast(&'a self) -> PFile<'a> {
let mut php_statements = Vec::<PStatement>::new();
for production in self.productions.iter() { for production in self.productions.iter() {
match production { match production {
@ -18,9 +21,16 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
php_statements.push(stmt.into_php_ast()); php_statements.push(stmt.into_php_ast());
} }
ModuleMembers::Expr(expr) => { ModuleMembers::Expr(expr) => {
// TODO: This should be done by the Expression transformer let p_expression = expr.into_php_ast();
php_statements.push(PStatement::ExpressionStatement(p_expression));
/*
match expr { match expr {
Expression::FunctionCall(fc) => { Expression::FunctionCall(fc) => {
// TODO: This definitely needs refactoring // TODO: This definitely needs refactoring
let function_expr: &Expression = &*fc.function; let function_expr: &Expression = &*fc.function;
match function_expr { match function_expr {
@ -75,13 +85,14 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
todo!("not implemented: AST transform for expression {:?}", expr) todo!("not implemented: AST transform for expression {:?}", expr)
} }
} }
*/
} }
} }
} }
Box::new(PhpAst { PFile {
statements: php_statements, statements: php_statements,
}) }
} }
} }

View File

@ -1,38 +1,28 @@
use super::super::PhpStatement; use super::super::PhpStatement;
use crate::{ use crate::{
codegen::Transpilable, php_ast::{PhpAssignmentExpression, PhpExpression, PhpSimpleAssignment}, syntax::ast::Statement codegen::Transpilable,
php_ast::{
php_ast_2::{PExpresssion, PSimpleAssignment, PStatement},
PhpAssignmentExpression, PhpExpression, PhpSimpleAssignment,
},
syntax::ast::Statement,
}; };
use super::PHPTransformable; use super::PHPTransformable;
/// Transforms a THP expression into a PHP expression /// Transforms a THP expression into a PHP expression
impl<'a> PHPTransformable<'a> for Statement<'_> { impl<'a> PHPTransformable<'a> for Statement<'_> {
fn into_php_ast(&'a self) -> Box<(dyn Transpilable + 'a)>{ type Item = PStatement<'a>;
fn into_php_ast(&'a self) -> PStatement<'a> {
match self { match self {
Statement::Binding(b) => { Statement::Binding(b) => {
// This is a PhpExpression, but a PhpPrimaryExpression is needed
let binding_expr = b.expression.into_php_ast(); let binding_expr = b.expression.into_php_ast();
/* PStatement::ExpressionStatement(PExpresssion::Assignment(PSimpleAssignment {
// TODO: Somehow fix this... variable: &b.identifier.value,
// the function above `into_php_ast` should somehow assignment: Box::new(binding_expr),
// return what I need? Or should return something general and }))
// then i decide how to transform it here?
// if it reaches this point in the pipeline, is it
// safe to assume that any AST is correct, since
// semantic analysis (supposedly) did its job?
let binding_primary_expr = match binding_expr {
PhpExpression::Assignment(PhpAssignmentExpression::Primary(p)) => p,
_ => unreachable!("Expected a PrimaryExpression during AST transformation"),
};
*/
Box::new(PhpStatement::PhpExpressionStatement(PhpExpression::Assignment(
PhpAssignmentExpression::SimpleAssignment(PhpSimpleAssignment {
variable: b.identifier.value.clone(),
assignment: binding_expr,
}),
)))
} }
_ => todo!("transformation for statement: {:?}", self), _ => todo!("transformation for statement: {:?}", self),
} }