feat: typecheck arrays

master
Araozu 2024-08-29 09:12:08 -05:00
parent 438802d011
commit 441db4bcad
9 changed files with 88 additions and 28 deletions

View File

@ -25,6 +25,7 @@
- [x] Parse arrays - [x] Parse arrays
- [x] Parse for loops - [x] Parse for loops
- [x] Parse while loops - [x] Parse while loops
- [x] Typecheck arrays
## v0.1.1 ## v0.1.1

View File

@ -137,10 +137,10 @@ impl SemanticCheck for Expression<'_> {
// Operators are treated as functions // Operators are treated as functions
let (op_params, _) = match scope.get_type(&op.value) { let (op_params, _) = match scope.get_type(&op.value) {
Some(Type::Function(params, return_t)) => (params, return_t), Some(Type::Function(params, return_t)) => (params, return_t),
Some(Type::Value(v)) => { Some(t) => {
// If a operator is stored as a value, // If a operator is stored as anything else
// it's a bug in the compiler // 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 => { None => {
// If the operator is not found its a user error, // If the operator is not found its a user error,

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
error_handling::{semantic_error::SemanticError, MistiError}, error_handling::{semantic_error::SemanticError, MistiError},
semantic::symbol_table::SymbolTable, semantic::symbol_table::SymbolTable,
syntax::ast::Expression, syntax::ast::{Expression, Positionable},
}; };
use super::{Type, Typed}; use super::{Type, Typed};
@ -31,12 +31,9 @@ impl Typed for Expression<'_> {
Ok(datatype) Ok(datatype)
} }
Expression::FunctionCall(f) => { Expression::FunctionCall(f) => {
// TODO: Must implement functions as first class citizens // TODO: allow arbitrary expressions and
// for this to work with any arbitrary expression. // check that they resolve into a function
// for now it justs expects an identifier // (e.g. object.member)
// TODO: Should this check that the type signature is correct?
// or is this done elsewhere?
match &*f.function { match &*f.function {
Expression::Identifier(id) => { Expression::Identifier(id) => {
@ -115,6 +112,7 @@ impl Typed for Expression<'_> {
} }
return Err(MistiError::Semantic(SemanticError { return Err(MistiError::Semantic(SemanticError {
// TODO: fix positions
error_start: 0, error_start: 0,
error_end: 1, error_end: 1,
reason: format!( 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]))
}
} }
} }
} }

View File

@ -13,8 +13,13 @@ pub enum Type {
// TODO: Use Type instead of String to allow // TODO: Use Type instead of String to allow
// arbitrary types // arbitrary types
Function(Vec<String>, String), Function(Vec<String>, 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<Type>),
// TODO: tuple, union types // TODO: tuple, union types
// TODO: generics
} }
impl Type { impl Type {
@ -25,6 +30,18 @@ impl Type {
_ => false, _ => 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 { pub trait Typed {

View File

@ -24,4 +24,3 @@ pub struct WhileLoop<'a> {
pub condition: Expression<'a>, pub condition: Expression<'a>,
pub body: Block<'a>, pub body: Block<'a>,
} }

View File

@ -8,7 +8,7 @@ use crate::{
/// Parses a factor expression. /// Parses a factor expression.
/// ///
/// ```ebnf /// ```ebnf
/// factor = unary, (("/" | "*"), unary)*; /// factor = unary, (("/" | "*", "%"), unary)*;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (unary, next_pos) = match super::unary::try_parse(tokens, pos) { let (unary, next_pos) = match super::unary::try_parse(tokens, pos) {
@ -25,12 +25,12 @@ fn parse_many<'a>(
prev_expr: Expression<'a>, prev_expr: Expression<'a>,
indentation_level: u32, indentation_level: u32,
) -> ParsingResult<'a, Expression<'a>> { ) -> ParsingResult<'a, Expression<'a>> {
// (("/" | "*"), unary)* // (("/" | "*" | "%"), unary)*
try_binary_op( try_binary_op(
tokens, tokens,
pos, pos,
prev_expr, prev_expr,
vec!["/", "*"], vec!["/", "*", "%"],
indentation_level, indentation_level,
|tokens, next_pos, prev_expr, token, indent_count: u32| { |tokens, next_pos, prev_expr, token, indent_count: u32| {
// match next // match next

View File

@ -7,7 +7,7 @@ use crate::{
/// Parses a factor expression. /// Parses a factor expression.
/// ///
/// ```ebnf /// ```ebnf
/// term = factor, (("-" | "+"), factor)*; /// term = factor, (("-" | "+" | "++"), factor)*;
/// ``` /// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> { pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
let (factor, next_pos) = match super::factor::try_parse(tokens, pos) { let (factor, next_pos) = match super::factor::try_parse(tokens, pos) {
@ -24,13 +24,13 @@ fn parse_many<'a>(
prev_expr: Expression<'a>, prev_expr: Expression<'a>,
indentation_level: u32, indentation_level: u32,
) -> ParsingResult<'a, Expression<'a>> { ) -> ParsingResult<'a, Expression<'a>> {
// term = factor, (("-" | "+"), factor)*; // term = factor, (("-" | "+" | "++"), factor)*;
try_binary_op( try_binary_op(
tokens, tokens,
pos, pos,
prev_expr, prev_expr,
vec!["+", "-"], vec!["+", "-", "++"],
indentation_level, indentation_level,
|tokens, pos, prev_expr, token, indent_count: u32| { |tokens, pos, prev_expr, token, indent_count: u32| {
// Parse the next factor // Parse the next factor

View File

@ -2,8 +2,9 @@ use crate::{
lexic::token::Token, lexic::token::Token,
syntax::{ syntax::{
ast::{ ast::{
loops::{ForLoop, WhileLoop}, var_binding::VariableBinding, Conditional, FunctionDeclaration, loops::{ForLoop, WhileLoop},
Statement, var_binding::VariableBinding,
Conditional, FunctionDeclaration, Statement,
}, },
parseable::{Parseable, ParsingError, ParsingResult}, parseable::{Parseable, ParsingError, ParsingResult},
}, },

View File

@ -1,8 +1,11 @@
use crate::{ use crate::{
error_handling::SyntaxError, lexic::token::{Token, TokenType}, syntax::{ error_handling::SyntaxError,
lexic::token::{Token, TokenType},
syntax::{
ast::{loops::WhileLoop, Block, Expression, Positionable}, 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> { impl<'a> Parseable<'a> for WhileLoop<'a> {
@ -46,10 +49,7 @@ impl<'a> Parseable<'a> for WhileLoop<'a> {
return Err(ParsingError::Err(SyntaxError { return Err(ParsingError::Err(SyntaxError {
error_start: e.position, error_start: e.position,
error_end: e.get_end_position(), error_end: e.get_end_position(),
reason: format!( reason: format!("Expected a block after the condition, found {}", e.value),
"Expected a block after the condition, found {}",
e.value
),
})) }))
} }
Err(ParsingError::Unmatched) => { Err(ParsingError::Unmatched) => {