feat: typecheck arrays
This commit is contained in:
parent
438802d011
commit
441db4bcad
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -24,4 +24,3 @@ pub struct WhileLoop<'a> {
|
|||||||
pub condition: Expression<'a>,
|
pub condition: Expression<'a>,
|
||||||
pub body: Block<'a>,
|
pub body: Block<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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},
|
||||||
},
|
},
|
||||||
|
@ -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) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user