diff --git a/CHANGELOG.md b/CHANGELOG.md index 3599ca1..f3eb891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ - [x] Parse for loops - [x] Parse while loops - [x] Typecheck arrays +- [x] Typecheck if/else if/else +- [x] Typecheck for loops ## v0.1.1 diff --git a/src/semantic/checks/block.rs b/src/semantic/checks/block.rs index 5141963..97b1b67 100644 --- a/src/semantic/checks/block.rs +++ b/src/semantic/checks/block.rs @@ -1,9 +1,19 @@ use crate::{ error_handling::MistiError, semantic::{impls::SemanticCheck, symbol_table::SymbolTable}, - syntax::ast::BlockMember, + syntax::ast::{Block, BlockMember}, }; +impl SemanticCheck for Block<'_> { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { + for member in &self.members { + member.check_semantics(scope)?; + } + + Ok(()) + } +} + impl<'a> SemanticCheck for BlockMember<'a> { // TODO: A block may contain a function declaration statement, // but (afaik) those are not allowed inside conditionals/loops diff --git a/src/semantic/checks/conditional.rs b/src/semantic/checks/conditional.rs new file mode 100644 index 0000000..d0e9410 --- /dev/null +++ b/src/semantic/checks/conditional.rs @@ -0,0 +1,57 @@ +use crate::{ + error_handling::{semantic_error::SemanticError, MistiError}, + semantic::{ + impls::SemanticCheck, + symbol_table::SymbolTable, + types::{Type, Typed}, + }, + syntax::ast::{Conditional, Positionable}, +}; + +impl SemanticCheck for Conditional<'_> { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { + let bool_type = Type::Value(String::from("Bool")); + + // Check if condition is a Bool + let if_condition = &self.if_member.condition; + let if_condition_type = if_condition.get_type(scope)?; + if !if_condition_type.equals(&bool_type) { + let (error_start, error_end) = if_condition.get_position(); + return Err(MistiError::Semantic(SemanticError { + error_start, + error_end, + reason: format!( + "Expected a condition of type Bool, found {:?}", + if_condition_type + ), + })); + } + + // Check if block + let if_block = &self.if_member.body; + if_block.check_semantics(scope)?; + + // Check all else if + for else_if_member in self.else_if_members.iter() { + let condition = &else_if_member.condition; + let condition_type = condition.get_type(scope)?; + if !condition_type.equals(&bool_type) { + let (error_start, error_end) = condition.get_position(); + return Err(MistiError::Semantic(SemanticError { + error_start, + error_end, + reason: format!("Expected a condition of type Bool, found {:?}", condition_type), + })); + } + + else_if_member.body.check_semantics(scope)?; + } + + // Check else + if let Some(else_block) = &self.else_block { + else_block.check_semantics(scope)?; + } + + Ok(()) + } +} diff --git a/src/semantic/checks/for_loop.rs b/src/semantic/checks/for_loop.rs new file mode 100644 index 0000000..917a7b8 --- /dev/null +++ b/src/semantic/checks/for_loop.rs @@ -0,0 +1,59 @@ +use crate::{ + error_handling::{semantic_error::SemanticError, MistiError}, + semantic::{ + impls::SemanticCheck, + symbol_table::SymbolTable, + types::{Type, Typed}, + }, + syntax::ast::{loops::ForLoop, Positionable}, +}; + +impl SemanticCheck for ForLoop<'_> { + fn check_semantics(&self, scope: &SymbolTable) -> Result<(), MistiError> { + // TODO: Implement a generic Collection interface? + // Use Traversable from PHP? + // for now this is restricted to arrays + + let collection_type = self.collection.get_type(scope)?; + // This will be a single Type, + let item_type = match collection_type { + Type::Generic(t, type_params) if t == "Array" => { + if type_params.len() != 1 { + unreachable!( + "Compiler error: found an Array[] with more than 1 type parameter: {:?}", + type_params + ) + } + + type_params + } + _ => { + // error, types other than an Array are not supported + let (error_start, error_end) = self.collection.get_position(); + return Err(MistiError::Semantic(SemanticError { + error_start, + error_end, + reason: format!("Only Array[T] are allowed as a for-loop collection."), + })); + } + }; + let item_type = &item_type[0]; + + let loop_scope = SymbolTable::new_from_parent(&scope); + + // Create a new scope, insert key,value + if let Some(key) = self.key { + // Since for now this only supports Array[T], key + // can only be a Int + loop_scope.insert(key.value.clone(), Type::Value("Int".into())); + } + // TODO: Add lifetimes to scoping instead of cloning + loop_scope.insert(self.value.value.clone(), item_type.clone()); + + // Check every statement inside the block + self.body.check_semantics(&loop_scope)?; + + // Ok + Ok(()) + } +} diff --git a/src/semantic/checks/mod.rs b/src/semantic/checks/mod.rs index f9d6579..89c49a2 100644 --- a/src/semantic/checks/mod.rs +++ b/src/semantic/checks/mod.rs @@ -1,5 +1,7 @@ pub mod binding; pub mod block; +pub mod conditional; pub mod expression; +pub mod for_loop; pub mod function_declaration; pub mod top_level_declaration; diff --git a/src/semantic/checks/top_level_declaration.rs b/src/semantic/checks/top_level_declaration.rs index 8551a61..878cda4 100644 --- a/src/semantic/checks/top_level_declaration.rs +++ b/src/semantic/checks/top_level_declaration.rs @@ -22,8 +22,8 @@ impl SemanticCheck for Statement<'_> { match self { Statement::Binding(b) => b.check_semantics(scope), Statement::FnDecl(f) => f.check_semantics(scope), - Statement::Conditional(_) => unimplemented!("check conditional"), - Statement::ForLoop(_) => unimplemented!("check for for loop"), + Statement::Conditional(c) => c.check_semantics(scope), + Statement::ForLoop(f) => f.check_semantics(scope), Statement::WhileLoop(_) => unimplemented!("check for while loop"), } } diff --git a/src/syntax/ast/loops.rs b/src/syntax/ast/loops.rs index 5158a05..9cb3e0b 100644 --- a/src/syntax/ast/loops.rs +++ b/src/syntax/ast/loops.rs @@ -11,6 +11,7 @@ pub struct ForLoop<'a> { pub loop_end: usize, pub key: Option<&'a Token>, pub value: &'a Token, + pub collection: Expression<'a>, pub body: Block<'a>, } diff --git a/src/syntax/parsers/for_loop.rs b/src/syntax/parsers/for_loop.rs index b7bd600..75bac5b 100644 --- a/src/syntax/parsers/for_loop.rs +++ b/src/syntax/parsers/for_loop.rs @@ -145,6 +145,7 @@ impl<'a> Parseable<'a> for ForLoop<'a> { loop_end, key, value, + collection: expr, body: block, }; Ok((for_loop, next))