From fe7cfe9d5f8d36d786db4ea03d324a4838083ace Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 27 Jul 2024 17:34:32 -0500 Subject: [PATCH] feat: Minimal conversion of THP->PHP AST --- CHANGELOG.md | 4 +-- src/cli/tokenize.rs | 8 ++--- src/codegen/mod.rs | 10 +++--- src/codegen/php/mod.rs | 27 ++++++++++++++ src/php_ast/mod.rs | 19 +++++++++- src/php_ast/transformers/expression.rs | 16 +++++++++ src/php_ast/transformers/mod.rs | 10 ++++++ src/php_ast/transformers/module_ast.rs | 50 ++++++++++++++++++++++++++ src/php_ast/transformers/statement.rs | 15 ++++++++ src/repl/mod.rs | 9 ++++- 10 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 src/codegen/php/mod.rs create mode 100644 src/php_ast/transformers/expression.rs create mode 100644 src/php_ast/transformers/mod.rs create mode 100644 src/php_ast/transformers/module_ast.rs create mode 100644 src/php_ast/transformers/statement.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 807ca9e..562439f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,8 +25,8 @@ ## v0.0.14 -- [ ] Define a minimal PHP AST -- [ ] Transform THP AST into PHP AST +- [x] Define a minimal PHP AST +- [x] Transform THP AST into PHP AST - [ ] Implement minimal codegen for the PHP AST - [ ] Remove old codegen - [ ] Finish the workflow for a hello world diff --git a/src/cli/tokenize.rs b/src/cli/tokenize.rs index f5ad719..8948489 100644 --- a/src/cli/tokenize.rs +++ b/src/cli/tokenize.rs @@ -1,5 +1,5 @@ -use std::io::{self, BufRead}; use crate::lexic::get_tokens; +use std::io::{self, BufRead}; pub fn tokenize_command(_options: Vec) -> Result<(), ()> { // Get the input from stdin @@ -8,12 +8,10 @@ pub fn tokenize_command(_options: Vec) -> Result<(), ()> { let mut lines = Vec::new(); for line in stdin.lock().lines() { match line { - Ok(line) => { - lines.push(line) - } + Ok(line) => lines.push(line), Err(reason) => { eprintln!("Error reading input: {}", reason); - return Err(()) + return Err(()); } } } diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index b861b7d..6dc92a2 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1,5 +1,5 @@ -use crate::syntax::ast::ModuleAST; - +// TODO: These are for the THP AST. Eventually replace this +// with the PHP AST mod binding; mod block; mod expression; @@ -9,13 +9,15 @@ mod module_ast; mod statement; mod top_level_construct; +mod php; + /// Trait that the AST and its nodes implement to support transformation to PHP -trait Transpilable { +pub trait Transpilable { /// Transforms this struct into PHP fn transpile(&self) -> String; } /// Transforms an AST to its representation in PHP -pub fn codegen<'a>(ast: &'a ModuleAST) -> String { +pub fn codegen<'a>(ast: &'a impl Transpilable) -> String { ast.transpile() } diff --git a/src/codegen/php/mod.rs b/src/codegen/php/mod.rs new file mode 100644 index 0000000..ed7552c --- /dev/null +++ b/src/codegen/php/mod.rs @@ -0,0 +1,27 @@ +use crate::php_ast::{PhpAst, PhpStatement}; + +use super::Transpilable; + +impl Transpilable for PhpAst<'_> { + fn transpile(&self) -> String { + let mut fragments = vec![String::from(" { + fn transpile(&self) -> String { + match self { + PhpStatement::PhpEchoStatement(expr_list) => { + // TODO: Actually generate parameters from the expr_list + "echo \"\";".into() + } + } + } +} + diff --git a/src/php_ast/mod.rs b/src/php_ast/mod.rs index 2f9a696..163c26e 100644 --- a/src/php_ast/mod.rs +++ b/src/php_ast/mod.rs @@ -1,3 +1,20 @@ // Follows https://phplang.org/spec/19-grammar.html#syntactic-grammar -struct PhpAst {} +pub mod transformers; + +/// Represents `statement-list` on the grammar +pub struct PhpAst<'a> { + pub statements: Vec>, +} + +pub enum PhpStatement<'a> { + PhpEchoStatement(PhpExpressionList<'a>), +} + +pub struct PhpExpressionList<'a> { + pub expressions: Vec>, +} + +pub enum PhpExpression<'a> { + String(&'a String), +} diff --git a/src/php_ast/transformers/expression.rs b/src/php_ast/transformers/expression.rs new file mode 100644 index 0000000..e9ea6ed --- /dev/null +++ b/src/php_ast/transformers/expression.rs @@ -0,0 +1,16 @@ +use super::super::PhpExpression; +use crate::syntax::ast::Expression; + +use super::PHPTransformable; + +/// Transforms a THP expression into a PHP expression +impl<'a> PHPTransformable<'a> for Expression<'_> { + type Item = PhpExpression<'a>; + + fn into_php_ast(&'a self) -> Self::Item { + match self { + Expression::String(value) => PhpExpression::String(value), + _ => todo!("transformation for expression: {:?}", self), + } + } +} diff --git a/src/php_ast/transformers/mod.rs b/src/php_ast/transformers/mod.rs new file mode 100644 index 0000000..da7d790 --- /dev/null +++ b/src/php_ast/transformers/mod.rs @@ -0,0 +1,10 @@ +pub mod expression; +pub mod module_ast; +pub mod statement; + +/// Implemented by AST nodes that can be transformed to PHP +pub trait PHPTransformable<'a> { + type Item; + + fn into_php_ast(&'a self) -> Self::Item; +} diff --git a/src/php_ast/transformers/module_ast.rs b/src/php_ast/transformers/module_ast.rs new file mode 100644 index 0000000..a4a5a70 --- /dev/null +++ b/src/php_ast/transformers/module_ast.rs @@ -0,0 +1,50 @@ +use super::super::PhpAst; +use crate::php_ast::{PhpExpressionList, PhpStatement}; +use crate::syntax::ast::{Expression, ModuleAST, ModuleMembers}; + +use super::PHPTransformable; + +/// Transforms a THP AST into a PHP AST +impl<'a> PHPTransformable<'a> for ModuleAST<'_> { + type Item = PhpAst<'a>; + + fn into_php_ast(&'a self) -> Self::Item { + let mut php_statements = Vec::::new(); + + for production in self.productions.iter() { + match production { + ModuleMembers::Stmt(stmt) => { + php_statements.push(stmt.into_php_ast()); + } + ModuleMembers::Expr(expr) => { + // TODO: a print() function call is technically an + // expression in the AST, but PHP expects it to be an statement. + // transform beforehand? + + match expr { + Expression::FunctionCall(fc) => { + let function_expr: &Expression = &*fc.function; + match function_expr { + Expression::Identifier(id) if *id == "print" => { + // transform to print() expression + // no parameters supported + php_statements.push(PhpStatement::PhpEchoStatement(PhpExpressionList { + expressions: vec![] + })); + }, + _ => todo!("Not implemented: AST transformation for function call that is not an identifier") + } + } + _ => { + todo!("not implemented: AST transform for expression {:?}", expr) + } + } + } + } + } + + PhpAst { + statements: php_statements, + } + } +} diff --git a/src/php_ast/transformers/statement.rs b/src/php_ast/transformers/statement.rs new file mode 100644 index 0000000..f6d1da8 --- /dev/null +++ b/src/php_ast/transformers/statement.rs @@ -0,0 +1,15 @@ +use super::super::PhpStatement; +use crate::syntax::ast::Statement; + +use super::PHPTransformable; + +/// Transforms a THP expression into a PHP expression +impl<'a> PHPTransformable<'a> for Statement<'_> { + type Item = PhpStatement<'a>; + + fn into_php_ast(&'a self) -> Self::Item { + match self { + _ => todo!("transformation for statement: {:?}", self), + } + } +} diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 407c1ac..309250d 100755 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -2,6 +2,7 @@ use std::io::{self, Write}; use colored::Colorize; +use crate::codegen::Transpilable; use crate::error_handling::PrintableError; use crate::lexic::token::Token; @@ -9,6 +10,8 @@ use super::codegen; use super::lexic; use super::syntax; +use crate::php_ast::transformers::PHPTransformable; + /// Executes Lexical analysis, handles errors and calls build_ast for the next phase fn compile(input: &String) { let tokens = lexic::get_tokens(input); @@ -32,7 +35,9 @@ fn build_ast(input: &String, tokens: Vec) { match ast { Ok(ast) => { + /* let res1 = crate::semantic::check_semantics(&ast); + TODO: Disabled to test the PHP codegen. Reenable match res1 { Ok(_) => {} Err(reason) => { @@ -42,8 +47,10 @@ fn build_ast(input: &String, tokens: Vec) { return; } } + */ - let js_code = codegen::codegen(&ast); + let php_ast = ast.into_php_ast(); + let js_code = php_ast.transpile(); println!("{}", js_code) } Err(reason) => {