Compare commits

..

No commits in common. "6eef841e84cb4a4447497ae5d3ed59c4caa55fb8" and "4c565df69924cc2d3d2f5579dde3ae585d010ba4" have entirely different histories.

15 changed files with 33 additions and 311 deletions

View File

@ -25,11 +25,6 @@
- Begin work on the code formatter - Begin work on the code formatter
## v0.1.1
- [x] Top level expressions as statements
- [ ] Naively transpile variable bindings
## v0.1.0 ## v0.1.0
- [x] Complete workflow for "Hello world" - [x] Complete workflow for "Hello world"

2
Cargo.lock generated
View File

@ -92,7 +92,7 @@ dependencies = [
[[package]] [[package]]
name = "thp" name = "thp"
version = "0.1.0" version = "0.0.14"
dependencies = [ dependencies = [
"colored", "colored",
"serde", "serde",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "thp" name = "thp"
version = "0.1.0" version = "0.0.14"
edition = "2021" edition = "2021"

View File

@ -1,38 +0,0 @@
use crate::{codegen::Transpilable, php_ast::PhpAssignmentExpression};
impl Transpilable for PhpAssignmentExpression<'_> {
fn transpile(&self) -> String {
match self {
PhpAssignmentExpression::Primary(p) => p.transpile(),
PhpAssignmentExpression::SimpleAssignment(assignment) => {
let variable_name = &assignment.variable;
let expression_str = assignment.assignment.transpile();
format!("${} = {}", variable_name, expression_str)
}
}
}
}
#[cfg(test)]
mod tests {
use crate::{
codegen::Transpilable,
php_ast::{PhpAssignmentExpression, PhpPrimaryExpression, PhpSimpleAssignment},
};
#[test]
fn should_gen_assignment() {
let variable = String::from("name");
let value = String::from("John");
let assignment = PhpPrimaryExpression::StringLiteral(&value);
let ast = PhpAssignmentExpression::SimpleAssignment(PhpSimpleAssignment {
variable,
assignment,
});
let output = ast.transpile();
assert_eq!("$name = \"John\"", output)
}
}

View File

@ -1,2 +1 @@
mod assignment;
mod primary_expression; mod primary_expression;

View File

@ -6,7 +6,6 @@ impl Transpilable for PhpPrimaryExpression<'_> {
PhpPrimaryExpression::IntegerLiteral(value) => value.to_string(), PhpPrimaryExpression::IntegerLiteral(value) => value.to_string(),
PhpPrimaryExpression::FloatingLiteral(value) => value.to_string(), PhpPrimaryExpression::FloatingLiteral(value) => value.to_string(),
PhpPrimaryExpression::StringLiteral(value) => format!("\"{}\"", value), PhpPrimaryExpression::StringLiteral(value) => format!("\"{}\"", value),
PhpPrimaryExpression::Variable(name) => format!("${}", name),
} }
} }
} }
@ -59,13 +58,4 @@ mod tests {
assert_eq!("322.644", output) assert_eq!("322.644", output)
} }
#[test]
fn should_transpile_variable() {
let input = String::from("name");
let ast = PhpPrimaryExpression::Variable(&input);
let output = ast.transpile();
assert_eq!("$name", output)
}
} }

View File

@ -8,7 +8,7 @@ pub mod statement_list;
impl Transpilable for PhpExpression<'_> { impl Transpilable for PhpExpression<'_> {
fn transpile(&self) -> String { fn transpile(&self) -> String {
match self { match self {
PhpExpression::Assignment(p) => p.transpile(), PhpExpression::PrimaryExpression(p) => p.transpile(),
} }
} }
} }

View File

@ -20,10 +20,6 @@ impl Transpilable for PhpStatement<'_> {
format!("echo {};", expressions_str) format!("echo {};", expressions_str)
} }
PhpStatement::PhpExpressionStatement(expr) => {
let expr_str = expr.transpile();
format!("{};", expr_str)
}
} }
} }
} }
@ -32,10 +28,7 @@ impl Transpilable for PhpStatement<'_> {
mod tests { mod tests {
use crate::{ use crate::{
codegen::Transpilable, codegen::Transpilable,
php_ast::{ php_ast::{PhpExpression, PhpExpressionList, PhpPrimaryExpression, PhpStatement},
PhpAssignmentExpression, PhpExpression, PhpExpressionList, PhpPrimaryExpression,
PhpStatement,
},
}; };
#[test] #[test]
@ -54,9 +47,7 @@ mod tests {
let input = String::from("322"); let input = String::from("322");
let exp_1 = PhpPrimaryExpression::FloatingLiteral(&input); let exp_1 = PhpPrimaryExpression::FloatingLiteral(&input);
let expressions = PhpExpressionList { let expressions = PhpExpressionList {
expressions: vec![PhpExpression::Assignment(PhpAssignmentExpression::Primary( expressions: vec![PhpExpression::PrimaryExpression(exp_1)],
exp_1,
))],
}; };
let ast = PhpStatement::PhpEchoStatement(expressions); let ast = PhpStatement::PhpEchoStatement(expressions);
let output = ast.transpile(); let output = ast.transpile();
@ -74,8 +65,8 @@ mod tests {
let expressions = PhpExpressionList { let expressions = PhpExpressionList {
expressions: vec![ expressions: vec![
PhpExpression::Assignment(PhpAssignmentExpression::Primary(exp_1)), PhpExpression::PrimaryExpression(exp_1),
PhpExpression::Assignment(PhpAssignmentExpression::Primary(exp_2)), PhpExpression::PrimaryExpression(exp_2),
], ],
}; };
let ast = PhpStatement::PhpEchoStatement(expressions); let ast = PhpStatement::PhpEchoStatement(expressions);
@ -83,16 +74,4 @@ mod tests {
assert_eq!("echo 322, \"Hai world\";", output) assert_eq!("echo 322, \"Hai world\";", output)
} }
#[test]
fn should_gen_expression_stmt() {
let input = String::from("Hi!");
let exp_1 = PhpPrimaryExpression::StringLiteral(&input);
let ast = PhpStatement::PhpExpressionStatement(PhpExpression::Assignment(
PhpAssignmentExpression::Primary(exp_1),
));
let output = ast.transpile();
assert_eq!("\"Hi!\";", output)
}
} }

View File

@ -14,12 +14,7 @@ impl Transpilable for PhpAst<'_> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{codegen::Transpilable, php_ast::PhpAst};
codegen::Transpilable,
php_ast::{
PhpAssignmentExpression, PhpAst, PhpExpression, PhpPrimaryExpression, PhpStatement,
},
};
#[test] #[test]
fn should_transpile_empty_file() { fn should_transpile_empty_file() {
@ -28,19 +23,4 @@ mod tests {
assert_eq!("<?php\n", output); assert_eq!("<?php\n", output);
} }
#[test]
fn should_transpile_expr_statement() {
let value = String::from("Hello world!");
let ast = PhpAst {
statements: vec![PhpStatement::PhpExpressionStatement(
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::StringLiteral(&value),
)),
)],
};
let output = ast.transpile();
assert_eq!("<?php\n\"Hello world!\";", output);
}
} }

View File

@ -19,7 +19,6 @@ pub struct PhpAst<'a> {
/// echo-statement /// echo-statement
pub enum PhpStatement<'a> { pub enum PhpStatement<'a> {
PhpEchoStatement(PhpExpressionList<'a>), PhpEchoStatement(PhpExpressionList<'a>),
PhpExpressionStatement(PhpExpression<'a>),
} }
pub struct PhpExpressionList<'a> { pub struct PhpExpressionList<'a> {
@ -27,30 +26,15 @@ pub struct PhpExpressionList<'a> {
} }
pub enum PhpExpression<'a> { pub enum PhpExpression<'a> {
Assignment(PhpAssignmentExpression<'a>), PrimaryExpression(PhpPrimaryExpression<'a>),
}
pub enum PhpAssignmentExpression<'a> {
Primary(PhpPrimaryExpression<'a>),
SimpleAssignment(PhpSimpleAssignment<'a>),
}
pub struct PhpSimpleAssignment<'a> {
pub variable: String,
pub assignment: PhpPrimaryExpression<'a>,
} }
/// https://phplang.org/spec/19-grammar.html#grammar-primary-expression /// https://phplang.org/spec/19-grammar.html#grammar-primary-expression
/// ///
/// primary-expression: /// primary-expression:
/// literal /// literal
/// variable
pub enum PhpPrimaryExpression<'a> { pub enum PhpPrimaryExpression<'a> {
IntegerLiteral(&'a String), IntegerLiteral(&'a String),
FloatingLiteral(&'a String), FloatingLiteral(&'a String),
StringLiteral(&'a String), StringLiteral(&'a String),
/// https://phplang.org/spec/19-grammar.html#grammar-variable
///
/// Supports only variable -> callable-variable -> simple-variable -> variable-name
Variable(&'a String),
} }

View File

@ -1,8 +1,5 @@
use super::super::PhpExpression; use super::super::PhpExpression;
use crate::{ use crate::{php_ast::PhpPrimaryExpression, syntax::ast::Expression};
php_ast::{PhpAssignmentExpression, PhpPrimaryExpression},
syntax::ast::Expression,
};
use super::PHPTransformable; use super::PHPTransformable;
@ -14,76 +11,9 @@ impl<'a> PHPTransformable<'a> for Expression<'_> {
match self { match self {
Expression::String(value) => { Expression::String(value) => {
let expr = PhpPrimaryExpression::StringLiteral(value); let expr = PhpPrimaryExpression::StringLiteral(value);
PhpExpression::Assignment(PhpAssignmentExpression::Primary(expr)) PhpExpression::PrimaryExpression(expr)
}
Expression::Int(value) => {
let expr = PhpPrimaryExpression::IntegerLiteral(value);
PhpExpression::Assignment(PhpAssignmentExpression::Primary(expr))
}
Expression::Float(value) => {
let expr = PhpPrimaryExpression::FloatingLiteral(value);
PhpExpression::Assignment(PhpAssignmentExpression::Primary(expr))
} }
_ => todo!("transformation for expression: {:?}", self), _ => todo!("transformation for expression: {:?}", self),
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::{
php_ast::{
transformers::PHPTransformable, PhpAssignmentExpression, PhpExpression,
PhpPrimaryExpression,
},
syntax::ast::Expression,
};
#[test]
fn should_transform_string() {
let value = String::from("Hello");
let input = Expression::String(&value);
let output = input.into_php_ast();
match output {
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::StringLiteral(value),
)) => {
assert_eq!("Hello", value)
}
_ => panic!("Expected a String literal"),
}
}
#[test]
fn should_transform_int() {
let value = String::from("322");
let input = Expression::Int(&value);
let output = input.into_php_ast();
match output {
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::IntegerLiteral(value),
)) => {
assert_eq!("322", value)
}
_ => panic!("Expected a Int literal"),
}
}
#[test]
fn should_transform_float() {
let value = String::from("322.644");
let input = Expression::Float(&value);
let output = input.into_php_ast();
match output {
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::FloatingLiteral(value),
)) => {
assert_eq!("322.644", value)
}
_ => panic!("Expected a Float literal"),
}
}
}

View File

@ -1,7 +1,5 @@
use super::super::PhpAst; use super::super::PhpAst;
use crate::php_ast::{ use crate::php_ast::{PhpExpression, PhpExpressionList, PhpPrimaryExpression, PhpStatement};
PhpAssignmentExpression, PhpExpression, PhpExpressionList, PhpPrimaryExpression, PhpStatement,
};
use crate::syntax::ast::{Expression, ModuleAST, ModuleMembers}; use crate::syntax::ast::{Expression, ModuleAST, ModuleMembers};
use super::PHPTransformable; use super::PHPTransformable;
@ -22,7 +20,6 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
// TODO: This should be done by the Expression transformer // TODO: This should be done by the Expression transformer
match expr { match expr {
Expression::FunctionCall(fc) => { Expression::FunctionCall(fc) => {
// TODO: This definitely needs refactoring
let function_expr: &Expression = &*fc.function; let function_expr: &Expression = &*fc.function;
match function_expr { match function_expr {
Expression::Identifier(id) if *id == "print" => { Expression::Identifier(id) if *id == "print" => {
@ -37,7 +34,7 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
match e { match e {
Expression::String(v) => { Expression::String(v) => {
expressions.push( expressions.push(
PhpExpression::Assignment(PhpAssignmentExpression::Primary(PhpPrimaryExpression::StringLiteral(v))) PhpExpression::PrimaryExpression(PhpPrimaryExpression::StringLiteral(v))
) )
}, },
_ => todo!("Non string expressions not supported") _ => todo!("Non string expressions not supported")
@ -51,27 +48,6 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
_ => todo!("Not implemented: AST transformation for function call that is not an identifier") _ => todo!("Not implemented: AST transformation for function call that is not an identifier")
} }
} }
Expression::Int(value) => {
php_statements.push(PhpStatement::PhpExpressionStatement(
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::IntegerLiteral(value),
)),
));
}
Expression::Float(value) => {
php_statements.push(PhpStatement::PhpExpressionStatement(
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::FloatingLiteral(value),
)),
));
}
Expression::String(value) => {
php_statements.push(PhpStatement::PhpExpressionStatement(
PhpExpression::Assignment(PhpAssignmentExpression::Primary(
PhpPrimaryExpression::StringLiteral(value),
)),
));
}
_ => { _ => {
todo!("not implemented: AST transform for expression {:?}", expr) todo!("not implemented: AST transform for expression {:?}", expr)
} }
@ -85,18 +61,3 @@ impl<'a> PHPTransformable<'a> for ModuleAST<'_> {
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::{php_ast::transformers::PHPTransformable, syntax::ast::ModuleAST};
#[test]
fn should_transform_empty_ast() {
let input = ModuleAST {
productions: vec![],
};
let output = input.into_php_ast();
assert!(output.statements.is_empty())
}
}

View File

@ -1,8 +1,5 @@
use super::super::PhpStatement; use super::super::PhpStatement;
use crate::{ use crate::syntax::ast::Statement;
php_ast::{PhpAssignmentExpression, PhpExpression, PhpSimpleAssignment},
syntax::ast::Statement,
};
use super::PHPTransformable; use super::PHPTransformable;
@ -12,76 +9,7 @@ impl<'a> PHPTransformable<'a> for Statement<'_> {
fn into_php_ast(&'a self) -> Self::Item { fn into_php_ast(&'a self) -> Self::Item {
match self { match self {
Statement::Binding(b) => {
// This is a PhpExpression, but a PhpPrimaryExpression is needed
let binding_expr = b.expression.into_php_ast();
// TODO: Somehow fix this...
// the function above `into_php_ast` should somehow
// 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"),
};
PhpStatement::PhpExpressionStatement(PhpExpression::Assignment(
PhpAssignmentExpression::SimpleAssignment(PhpSimpleAssignment {
variable: b.identifier.value.clone(),
assignment: binding_primary_expr,
}),
))
}
_ => todo!("transformation for statement: {:?}", self), _ => todo!("transformation for statement: {:?}", self),
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::{
lexic::token::{Token, TokenType},
php_ast::{
transformers::PHPTransformable, PhpAssignmentExpression, PhpExpression,
PhpPrimaryExpression, PhpStatement,
},
syntax::ast::{var_binding::VariableBinding, Expression, Statement},
};
#[test]
fn should_transform_binding() {
let identifier_token = Token {
token_type: TokenType::Identifier,
value: "name".into(),
position: 0,
};
let expr_value = String::from("Hello");
let expression = Expression::String(&expr_value);
let binding = Statement::Binding(VariableBinding {
datatype: None,
identifier: &identifier_token,
expression,
is_mutable: false,
});
let output = binding.into_php_ast();
match output {
PhpStatement::PhpExpressionStatement(PhpExpression::Assignment(
PhpAssignmentExpression::SimpleAssignment(assignment),
)) => {
assert_eq!("name", assignment.variable);
match assignment.assignment {
PhpPrimaryExpression::StringLiteral(value) => {
assert_eq!("Hello", value);
}
_ => panic!("Expected a String literal as the value of the assignment"),
}
}
_ => panic!("Expected an ExpressionStatement"),
}
}
}

View File

@ -33,6 +33,22 @@ impl SemanticCheck for Statement<'_> {
// TODO: Move to its own file when it grows // TODO: Move to its own file when it grows
impl SemanticCheck for Expression<'_> { impl SemanticCheck for Expression<'_> {
fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> {
// How to get the global definition into the symbol table?
// maybe just when creating the symbol table inject all
// the global elements at once?
// Store the global elements as binary/JSON
// and load them along with the symbol table
// then for efficiency they could be grouped by module?
// and stored as binary files?
// then the binary files are searched for and loaded when
// requested?
// For a function call:
// check that the function exists
// check its signature
// check parameters
match self { match self {
Expression::FunctionCall(f) => { Expression::FunctionCall(f) => {
let fun = &*f.function; let fun = &*f.function;
@ -88,11 +104,7 @@ impl SemanticCheck for Expression<'_> {
} }
} }
} }
Expression::Int(_) => {} _ => todo!("Check semantics for expression other than function call"),
Expression::Float(_) => {}
Expression::String(_) => {}
Expression::Boolean(_) => {}
_ => todo!("Check semantics for expression other than function call and primitive"),
} }
Ok(()) Ok(())

View File

@ -62,6 +62,8 @@ pub struct Parameter<'a> {
pub enum Expression<'a> { pub enum Expression<'a> {
Int(&'a String), Int(&'a String),
Float(&'a String), Float(&'a String),
// TODO: Specify if this contains or not the original quotes ""
// TODO: After this fix where neccesary
String(&'a String), String(&'a String),
Boolean(bool), Boolean(bool),
Identifier(&'a String), Identifier(&'a String),