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 for loops
- [x] Parse while loops
- [x] Typecheck arrays
## v0.1.1

View File

@ -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,

View File

@ -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]))
}
}
}
}

View File

@ -13,8 +13,13 @@ pub enum Type {
// TODO: Use Type instead of String to allow
// arbitrary types
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: 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 {

View File

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

View File

@ -8,7 +8,7 @@ use crate::{
/// Parses a factor expression.
///
/// ```ebnf
/// factor = unary, (("/" | "*"), unary)*;
/// factor = unary, (("/" | "*", "%"), unary)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
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

View File

@ -7,7 +7,7 @@ use crate::{
/// Parses a factor expression.
///
/// ```ebnf
/// term = factor, (("-" | "+"), factor)*;
/// term = factor, (("-" | "+" | "++"), factor)*;
/// ```
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParsingResult<Expression> {
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

View File

@ -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},
},

View File

@ -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) => {