feat: typecheck conditionals and for loops
This commit is contained in:
parent
441db4bcad
commit
d4f34b62e2
@ -26,6 +26,8 @@
|
|||||||
- [x] Parse for loops
|
- [x] Parse for loops
|
||||||
- [x] Parse while loops
|
- [x] Parse while loops
|
||||||
- [x] Typecheck arrays
|
- [x] Typecheck arrays
|
||||||
|
- [x] Typecheck if/else if/else
|
||||||
|
- [x] Typecheck for loops
|
||||||
|
|
||||||
|
|
||||||
## v0.1.1
|
## v0.1.1
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error_handling::MistiError,
|
error_handling::MistiError,
|
||||||
semantic::{impls::SemanticCheck, symbol_table::SymbolTable},
|
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> {
|
impl<'a> SemanticCheck for BlockMember<'a> {
|
||||||
// TODO: A block may contain a function declaration statement,
|
// TODO: A block may contain a function declaration statement,
|
||||||
// but (afaik) those are not allowed inside conditionals/loops
|
// but (afaik) those are not allowed inside conditionals/loops
|
||||||
|
57
src/semantic/checks/conditional.rs
Normal file
57
src/semantic/checks/conditional.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
59
src/semantic/checks/for_loop.rs
Normal file
59
src/semantic/checks/for_loop.rs
Normal file
@ -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(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
pub mod binding;
|
pub mod binding;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod conditional;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
|
pub mod for_loop;
|
||||||
pub mod function_declaration;
|
pub mod function_declaration;
|
||||||
pub mod top_level_declaration;
|
pub mod top_level_declaration;
|
||||||
|
@ -22,8 +22,8 @@ impl SemanticCheck for Statement<'_> {
|
|||||||
match self {
|
match self {
|
||||||
Statement::Binding(b) => b.check_semantics(scope),
|
Statement::Binding(b) => b.check_semantics(scope),
|
||||||
Statement::FnDecl(f) => f.check_semantics(scope),
|
Statement::FnDecl(f) => f.check_semantics(scope),
|
||||||
Statement::Conditional(_) => unimplemented!("check conditional"),
|
Statement::Conditional(c) => c.check_semantics(scope),
|
||||||
Statement::ForLoop(_) => unimplemented!("check for for loop"),
|
Statement::ForLoop(f) => f.check_semantics(scope),
|
||||||
Statement::WhileLoop(_) => unimplemented!("check for while loop"),
|
Statement::WhileLoop(_) => unimplemented!("check for while loop"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ pub struct ForLoop<'a> {
|
|||||||
pub loop_end: usize,
|
pub loop_end: usize,
|
||||||
pub key: Option<&'a Token>,
|
pub key: Option<&'a Token>,
|
||||||
pub value: &'a Token,
|
pub value: &'a Token,
|
||||||
|
pub collection: Expression<'a>,
|
||||||
pub body: Block<'a>,
|
pub body: Block<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +145,7 @@ impl<'a> Parseable<'a> for ForLoop<'a> {
|
|||||||
loop_end,
|
loop_end,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
|
collection: expr,
|
||||||
body: block,
|
body: block,
|
||||||
};
|
};
|
||||||
Ok((for_loop, next))
|
Ok((for_loop, next))
|
||||||
|
Loading…
Reference in New Issue
Block a user