diff --git a/CHANGELOG.md b/CHANGELOG.md index 15cadd4..3599ca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - [x] Parse arrays - [x] Parse for loops - [x] Parse while loops +- [x] Typecheck arrays ## v0.1.1 diff --git a/src/semantic/checks/expression.rs b/src/semantic/checks/expression.rs index 59e6212..e154fc2 100644 --- a/src/semantic/checks/expression.rs +++ b/src/semantic/checks/expression.rs @@ -137,10 +137,10 @@ impl SemanticCheck for Expression<'_> { // Operators are treated as functions let (op_params, _) = match scope.get_type(&op.value) { Some(Type::Function(params, return_t)) => (params, return_t), - Some(Type::Value(v)) => { - // If a operator is stored as a value, + Some(t) => { + // If a operator is stored as anything else // it's a bug in the compiler - unreachable!("Compiler bug: a binary operator was registered in the symbol table as a value of type {}", v) + unreachable!("Compiler bug: a binary operator was registered in the symbol table as a value of type {:?}", t) } None => { // If the operator is not found its a user error, diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index 167009b..a85b2d1 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -1,7 +1,7 @@ use crate::{ error_handling::{semantic_error::SemanticError, MistiError}, semantic::symbol_table::SymbolTable, - syntax::ast::Expression, + syntax::ast::{Expression, Positionable}, }; use super::{Type, Typed}; @@ -31,12 +31,9 @@ impl Typed for Expression<'_> { Ok(datatype) } Expression::FunctionCall(f) => { - // TODO: Must implement functions as first class citizens - // for this to work with any arbitrary expression. - // for now it justs expects an identifier - - // TODO: Should this check that the type signature is correct? - // or is this done elsewhere? + // TODO: allow arbitrary expressions and + // check that they resolve into a function + // (e.g. object.member) match &*f.function { Expression::Identifier(id) => { @@ -115,6 +112,7 @@ impl Typed for Expression<'_> { } return Err(MistiError::Semantic(SemanticError { + // TODO: fix positions error_start: 0, error_end: 1, reason: format!( @@ -122,7 +120,51 @@ impl Typed for Expression<'_> { ), })); } - Expression::Array(_) => unimplemented!("get type of array"), + Expression::Array(arr) => { + // The first expression found determines the + // type of the array + + // TODO: for now an array must have at least 1 element, + // if the array is empty there is no way to know its type. + // TODO: if the array is empty then its + // datatype should be determined by its usage. + if arr.exps.is_empty() { + return Err(MistiError::Semantic(SemanticError { + error_start: arr.start, + error_end: arr.end, + reason: format!( + "An array must have at least 1 element to determine its type. This will be fixed later." + ), + })); + } + + let mut expressions = arr.exps.iter(); + let first_expr = expressions.next().unwrap(); + let first_type = first_expr.get_type(scope)?; + + // then check that every expression has the same type + for exp in expressions { + let exp_type = exp.get_type(scope)?; + if !exp_type.equals(&first_type) { + // TODO: subtyping + + // error, found an item with a diferent datatype + let (error_start, error_end) = exp.get_position(); + return Err(MistiError::Semantic(SemanticError { + error_start, + error_end, + reason: format!( + "All elements of an array must have the same datatype. Expected {:?}, got {:?}", + first_type, + exp_type, + ), + })); + } + } + + // return the Array type + Ok(Type::Generic("Array".into(), vec![first_type])) + } } } } diff --git a/src/semantic/types/mod.rs b/src/semantic/types/mod.rs index 9ceb48d..13be4d6 100644 --- a/src/semantic/types/mod.rs +++ b/src/semantic/types/mod.rs @@ -13,8 +13,13 @@ pub enum Type { // TODO: Use Type instead of String to allow // arbitrary types Function(Vec, String), + /// The concrete type, the type parameters. + /// + /// E.g.: Array[Int] -> ("Array", vec!["Int"]) + /// + /// E.g.: Map[String, Float] -> ("Map", vec!["String", "Float"]) + Generic(String, Vec), // TODO: tuple, union types - // TODO: generics } impl Type { @@ -25,6 +30,18 @@ impl Type { _ => false, } } + + /// Compares this type to another + pub fn equals(&self, other: &Self) -> bool { + use Type::*; + + match (self, other) { + (Value(v1), Value(v2)) => v1 == v2, + (Function(_, _), Function(_, _)) => unimplemented!("Comparison of 2 function types"), + (Generic(_, _), Generic(_, _)) => unimplemented!("Comparison of 2 generic types"), + _ => false, + } + } } pub trait Typed { diff --git a/src/syntax/ast/loops.rs b/src/syntax/ast/loops.rs index 6efdb95..5158a05 100644 --- a/src/syntax/ast/loops.rs +++ b/src/syntax/ast/loops.rs @@ -24,4 +24,3 @@ pub struct WhileLoop<'a> { pub condition: Expression<'a>, pub body: Block<'a>, } - diff --git a/src/syntax/parsers/expression/factor.rs b/src/syntax/parsers/expression/factor.rs index ea9354a..6974f73 100644 --- a/src/syntax/parsers/expression/factor.rs +++ b/src/syntax/parsers/expression/factor.rs @@ -8,7 +8,7 @@ use crate::{ /// Parses a factor expression. /// /// ```ebnf -/// factor = unary, (("/" | "*"), unary)*; +/// factor = unary, (("/" | "*", "%"), unary)*; /// ``` pub fn try_parse(tokens: &Vec, pos: usize) -> ParsingResult { let (unary, next_pos) = match super::unary::try_parse(tokens, pos) { @@ -25,12 +25,12 @@ fn parse_many<'a>( prev_expr: Expression<'a>, indentation_level: u32, ) -> ParsingResult<'a, Expression<'a>> { - // (("/" | "*"), unary)* + // (("/" | "*" | "%"), unary)* try_binary_op( tokens, pos, prev_expr, - vec!["/", "*"], + vec!["/", "*", "%"], indentation_level, |tokens, next_pos, prev_expr, token, indent_count: u32| { // match next diff --git a/src/syntax/parsers/expression/term.rs b/src/syntax/parsers/expression/term.rs index 6abeb75..528ca97 100644 --- a/src/syntax/parsers/expression/term.rs +++ b/src/syntax/parsers/expression/term.rs @@ -7,7 +7,7 @@ use crate::{ /// Parses a factor expression. /// /// ```ebnf -/// term = factor, (("-" | "+"), factor)*; +/// term = factor, (("-" | "+" | "++"), factor)*; /// ``` pub fn try_parse(tokens: &Vec, pos: usize) -> ParsingResult { let (factor, next_pos) = match super::factor::try_parse(tokens, pos) { @@ -24,13 +24,13 @@ fn parse_many<'a>( prev_expr: Expression<'a>, indentation_level: u32, ) -> ParsingResult<'a, Expression<'a>> { - // term = factor, (("-" | "+"), factor)*; + // term = factor, (("-" | "+" | "++"), factor)*; try_binary_op( tokens, pos, prev_expr, - vec!["+", "-"], + vec!["+", "-", "++"], indentation_level, |tokens, pos, prev_expr, token, indent_count: u32| { // Parse the next factor diff --git a/src/syntax/parsers/statement.rs b/src/syntax/parsers/statement.rs index 18b2409..e20f317 100644 --- a/src/syntax/parsers/statement.rs +++ b/src/syntax/parsers/statement.rs @@ -2,8 +2,9 @@ use crate::{ lexic::token::Token, syntax::{ ast::{ - loops::{ForLoop, WhileLoop}, var_binding::VariableBinding, Conditional, FunctionDeclaration, - Statement, + loops::{ForLoop, WhileLoop}, + var_binding::VariableBinding, + Conditional, FunctionDeclaration, Statement, }, parseable::{Parseable, ParsingError, ParsingResult}, }, diff --git a/src/syntax/parsers/while_loop.rs b/src/syntax/parsers/while_loop.rs index d898986..644de84 100644 --- a/src/syntax/parsers/while_loop.rs +++ b/src/syntax/parsers/while_loop.rs @@ -1,8 +1,11 @@ use crate::{ - error_handling::SyntaxError, lexic::token::{Token, TokenType}, syntax::{ + error_handling::SyntaxError, + lexic::token::{Token, TokenType}, + syntax::{ ast::{loops::WhileLoop, Block, Expression, Positionable}, - parseable::{Parseable, ParsingError, ParsingResult}, utils::parse_token_type, - } + parseable::{Parseable, ParsingError, ParsingResult}, + utils::parse_token_type, + }, }; impl<'a> Parseable<'a> for WhileLoop<'a> { @@ -46,10 +49,7 @@ impl<'a> Parseable<'a> for WhileLoop<'a> { return Err(ParsingError::Err(SyntaxError { error_start: e.position, error_end: e.get_end_position(), - reason: format!( - "Expected a block after the condition, found {}", - e.value - ), + reason: format!("Expected a block after the condition, found {}", e.value), })) } Err(ParsingError::Unmatched) => {