diff --git a/CHANGELOG.md b/CHANGELOG.md index 26c2500..ef7683a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,13 @@ - Change REPL to execute code only after `;;` is found - Forward the code generated by the REPL to the PHP repl + +## v0.1.3 + +- [ ] Test semantic analysis +- [ ] Generate php code from current AST + + ## v0.1.2 - [x] Parse conditionals diff --git a/src/codegen/php/expression/mod.rs b/src/codegen/php/expression/mod.rs index acf6352..0e0b362 100644 --- a/src/codegen/php/expression/mod.rs +++ b/src/codegen/php/expression/mod.rs @@ -10,6 +10,11 @@ impl Transpilable for PExpresssion<'_> { Primary(p) => p.transpile(), Assignment(a) => a.transpile(), FunctionCall(f) => f.transpile(), + BinaryOp(left, right, op) => { + let left_str = left.transpile(); + let right_str = right.transpile(); + format!("{} {} {}", left_str, op, right_str) + } } } } diff --git a/src/php_ast/mod.rs b/src/php_ast/mod.rs index a9d3802..c4b1007 100644 --- a/src/php_ast/mod.rs +++ b/src/php_ast/mod.rs @@ -32,6 +32,7 @@ pub enum PExpresssion<'a> { Primary(PPrimary<'a>), /// This comes from a THP binding Assignment(PSimpleAssignment<'a>), + BinaryOp(Box>, Box>, &'a String), } pub struct PSimpleAssignment<'a> { diff --git a/src/php_ast/transformers/expression.rs b/src/php_ast/transformers/expression.rs index f377f8b..020e2e2 100644 --- a/src/php_ast/transformers/expression.rs +++ b/src/php_ast/transformers/expression.rs @@ -36,7 +36,14 @@ impl<'a> PHPTransformable<'a> for Expression<'_> { PExpresssion::Primary(PPrimary::BoolLiteral(b.value == "true")) } Expression::UnaryOperator(_, _) => unimplemented!("transform unary op into php"), - Expression::BinaryOperator(_, _, _) => unimplemented!("transform binary op into php"), + Expression::BinaryOperator(left_expr, right_expr, op) => { + // For now assume that any THP operator directly maps to a PHP operator... + + let left_value = left_expr.into_php_ast(); + let right_value = right_expr.into_php_ast(); + + PExpresssion::BinaryOp(Box::new(left_value), Box::new(right_value), &op.value) + } Expression::Array(_) => unimplemented!("transform array into php"), } } diff --git a/src/semantic/checks/binding.rs b/src/semantic/checks/binding.rs index 44342fc..fd6c7c3 100644 --- a/src/semantic/checks/binding.rs +++ b/src/semantic/checks/binding.rs @@ -32,6 +32,8 @@ impl SemanticCheck for VariableBinding<'_> { return Err(econtainer); } + self.expression.check_semantics(scope)?; + // This gets the datatype of the assigned expression, // to compare it later with the declared datatype. let expression_datatype = self.expression.get_type(scope)?; diff --git a/src/semantic/checks/expression.rs b/src/semantic/checks/expression.rs index 0f2f798..0ea1e2c 100644 --- a/src/semantic/checks/expression.rs +++ b/src/semantic/checks/expression.rs @@ -261,7 +261,7 @@ impl SemanticCheck for Expression<'_> { let label = ErrorLabel { message: format!( "Expected a {}, got a {:?} on the right side of the {} operator", - op_params[1], left_expr_type, op.value + op_params[1], right_expr_type, op.value ), start: error_start, end: error_end, diff --git a/src/semantic/checks/function_declaration.rs b/src/semantic/checks/function_declaration.rs index edd9c94..b6567b2 100644 --- a/src/semantic/checks/function_declaration.rs +++ b/src/semantic/checks/function_declaration.rs @@ -7,7 +7,7 @@ use crate::{ impl SemanticCheck for FunctionDeclaration<'_> { fn check_semantics( &self, - scope: &crate::semantic::symbol_table::SymbolTable, + scope: &SymbolTable, ) -> Result<(), crate::error_handling::MistiError> { let function_name = self.identifier.value.clone(); diff --git a/src/semantic/std.rs b/src/semantic/std.rs index edde927..a499851 100644 --- a/src/semantic/std.rs +++ b/src/semantic/std.rs @@ -2,11 +2,21 @@ //! by directly inserting the definitions into the //! Symbol Table -use super::{symbol_table::SymbolTable, types::Type}; +use super::{ + symbol_table::SymbolTable, + types::{ + global::{INT, STRING, VOID}, + Type, + }, +}; /// Populates the symbol table with the stdlib pub fn populate(table: &mut SymbolTable) { // print: (String) -> (Void) - let print_fn = Type::Function(vec!["String".into()], "Void".into()); + let print_fn = Type::Function(vec![STRING.into()], VOID.into()); table.insert("print".into(), print_fn); + + // + operator (Int, Int) -> Int + let plus_op = Type::Function(vec![INT.into(), INT.into()], INT.into()); + table.insert("+".into(), plus_op); } diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index 1a6c074..df72d2c 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -13,7 +13,6 @@ use crate::{ use super::{Type, Typed}; impl Typed for Expression<'_> { - /// Attempts to get the datatype for an expression. fn get_type(&self, scope: &SymbolTable) -> Result { match self { Expression::Int(_) => Ok(Type::Value("Int".into())), @@ -162,32 +161,32 @@ impl Typed for Expression<'_> { unreachable!("Illegal state: Found an unexpected unary operator during semantic analysis: {}", op.value); } - Expression::BinaryOperator(exp1, exp2, operator) => { - let t1 = exp1.get_type(scope)?; - let t2 = exp2.get_type(scope)?; - - // TODO: There's definitely a better way to do this - // maybe store operators as functions? - if operator.value == "+" && t1.is_value("Int") && t2.is_value("Int") { - return Ok(Type::Value("Int".into())); - } else if operator.value == "-" && t1.is_value("Int") && t2.is_value("Int") { - return Ok(Type::Value("Int".into())); + Expression::BinaryOperator(_, _, operator) => { + match scope.get_type(&operator.value) { + Some(Type::Function(_, return_type)) => Ok(Type::Value(return_type)), + Some(_) => { + unreachable!( + "Compiler error: The operator {} was defined but it wasn't a function", + operator.value + ) + } + None => { + let label = ErrorLabel { + message: format!("Unsupported binary operator"), + // TODO: Fix positioning + start: 0, + end: 1, + }; + let econtainer = ErrorContainer { + error_code: SEMANTIC_MISMATCHED_TYPES, + error_offset: 0, + labels: vec![label], + note: None, + help: None, + }; + return Err(econtainer); + } } - - let label = ErrorLabel { - message: format!("Unsupported binary operator"), - // TODO: Fix positioning - start: 0, - end: 1, - }; - let econtainer = ErrorContainer { - error_code: SEMANTIC_MISMATCHED_TYPES, - error_offset: 0, - labels: vec![label], - note: None, - help: None, - }; - return Err(econtainer); } Expression::Array(arr) => { // The first expression found determines the diff --git a/src/semantic/types/global.rs b/src/semantic/types/global.rs new file mode 100644 index 0000000..459445c --- /dev/null +++ b/src/semantic/types/global.rs @@ -0,0 +1,3 @@ +pub const STRING: &str = "String"; +pub const INT: &str = "Int"; +pub const VOID: &str = "Void"; diff --git a/src/semantic/types/mod.rs b/src/semantic/types/mod.rs index 13be4d6..09e9fc7 100644 --- a/src/semantic/types/mod.rs +++ b/src/semantic/types/mod.rs @@ -6,6 +6,7 @@ use crate::error_handling::MistiError; use super::symbol_table::SymbolTable; mod expression; +pub mod global; #[derive(Debug, Clone, PartialEq)] pub enum Type { @@ -45,5 +46,9 @@ impl Type { } pub trait Typed { + /// Returns the datatype of this value. + /// + /// This function does not perform typechecking, it only returns a type. + /// Typeckecking is done by the trait SemanticCheck fn get_type(&self, scope: &SymbolTable) -> Result; }