-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from yazaldefilimone/feat/table
feat: suport table, array, maps
- Loading branch information
Showing
12 changed files
with
832 additions
and
319 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use super::Checker; | ||
use crate::{ | ||
ast::ast, | ||
diagnostics::{Diagnostic, TypeError}, | ||
types::{TableType, Type}, | ||
utils::range::Range, | ||
}; | ||
|
||
pub enum Accessor { | ||
String(String), | ||
Number(usize), | ||
} | ||
|
||
impl<'a> Checker<'a> { | ||
pub fn check_index_expression(&mut self, table_expr: &ast::IndexExpression) -> Result<Type, Diagnostic> { | ||
let base_type = self.check_expression(&table_expr.base)?; | ||
let base_range = table_expr.base.get_range(); | ||
|
||
match base_type { | ||
Type::Table(ref table_type) => { | ||
let acc = self.extract_accessor(&table_expr.index)?; | ||
self.check_index_access(table_type, acc, table_expr.index.get_range()) | ||
} | ||
_ => Err(self.create_diagnostic(TypeError::ExpectedTable(base_type.to_string(), Some(base_range)))), | ||
} | ||
} | ||
|
||
fn check_index_access(&self, table: &TableType, acc: Option<Accessor>, range: Range) -> Result<Type, Diagnostic> { | ||
match acc { | ||
Some(Accessor::String(name)) => self.check_index_access_string(table, &name, range), | ||
Some(Accessor::Number(index)) => self.check_index_access_number(table, index, range), | ||
// todo: return union type based on table values | ||
None => Ok(Type::Unknown), | ||
} | ||
} | ||
|
||
fn check_index_access_string(&self, table: &TableType, name: &str, range: Range) -> Result<Type, Diagnostic> { | ||
if let Some(value_type) = table.get_type(name) { | ||
Ok(value_type.clone()) | ||
} else { | ||
Err(self.create_diagnostic(TypeError::KeyNotFoundInTable(name.to_string(), table.to_string(), Some(range)))) | ||
} | ||
} | ||
|
||
fn check_index_access_number(&self, table: &TableType, index: usize, range: Range) -> Result<Type, Diagnostic> { | ||
if let Some(element_type) = table.get_array_type(index) { | ||
Ok(element_type.clone()) | ||
} else { | ||
Err(self.create_diagnostic(TypeError::KeyNotFoundInTable(index.to_string(), table.to_string(), Some(range)))) | ||
} | ||
} | ||
|
||
fn extract_accessor(&mut self, index_expr: &ast::Expression) -> Result<Option<Accessor>, Diagnostic> { | ||
match index_expr { | ||
ast::Expression::Literal(literal) => match literal { | ||
ast::LiteralExpression::String(string) => Ok(Some(Accessor::String(string.value.clone()))), | ||
ast::LiteralExpression::Number(number) => { | ||
let number = number.value.parse::<usize>(); | ||
// todo: create diagnostic ?? | ||
return if number.is_err() { Ok(None) } else { Ok(Some(Accessor::Number(number.unwrap()))) }; | ||
} | ||
_ => self.handle_non_literal_index(index_expr), | ||
}, | ||
_ => self.handle_non_literal_index(index_expr), | ||
} | ||
} | ||
|
||
fn handle_non_literal_index(&mut self, index_expr: &ast::Expression) -> Result<Option<Accessor>, Diagnostic> { | ||
self.check_expression_index(index_expr)?; | ||
Ok(None) | ||
} | ||
|
||
fn check_expression_index(&mut self, key_expr: &ast::Expression) -> Result<(), Diagnostic> { | ||
let expr_type = self.check_expression(key_expr)?; | ||
match expr_type { | ||
Type::String | Type::Number => Ok(()), | ||
_ => Err( | ||
self.create_diagnostic(TypeError::MismatchedAccessorType(expr_type.to_string(), Some(key_expr.get_range()))), | ||
), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
use super::Checker; | ||
use crate::{ | ||
ast::ast, | ||
diagnostics::{Diagnostic, TypeError}, | ||
types::{TableType, Type}, | ||
utils::range::Range, | ||
}; | ||
|
||
impl<'a> Checker<'a> { | ||
pub fn check_member_expression(&mut self, member: &ast::MemberExpression) -> Result<Type, Diagnostic> { | ||
let base_type = self.check_expression(&member.base)?; | ||
let base_range = member.base.get_range(); | ||
|
||
match base_type { | ||
Type::Table(ref table_type) => self.check_member(table_type, &member.member), | ||
_ => Err(self.create_diagnostic(TypeError::ExpectedTable(base_type.to_string(), Some(base_range)))), | ||
} | ||
} | ||
|
||
fn check_member(&mut self, table: &TableType, member: &ast::Expression) -> Result<Type, Diagnostic> { | ||
match member { | ||
ast::Expression::Literal(literal) => { | ||
if let ast::LiteralExpression::String(string) = literal { | ||
self.check_identifier_member(&string.value, table, string.range.clone()) | ||
} else { | ||
Err(self.create_member_error(member)) | ||
} | ||
} | ||
ast::Expression::Call(call) => self.check_call_member(call, table), | ||
ast::Expression::Identifier(identifier) => { | ||
self.check_identifier_member(&identifier.name, table, identifier.range.clone()) | ||
} | ||
_ => Err(self.create_member_error(member)), | ||
} | ||
} | ||
|
||
fn check_identifier_member(&mut self, name: &str, table: &TableType, range: Range) -> Result<Type, Diagnostic> { | ||
if let Some(member_type) = table.get_type(name) { | ||
Ok(member_type.clone()) | ||
} else { | ||
Err(self.create_not_found_key_error(name, table, range)) | ||
} | ||
} | ||
|
||
fn create_member_error(&mut self, member: &ast::Expression) -> Diagnostic { | ||
let range = member.get_range(); | ||
match self.check_expression(member) { | ||
Ok(member_type) => { | ||
let diagnostic = TypeError::MismatchedKeyType(member_type.to_string(), Some(range)); | ||
self.create_diagnostic(diagnostic) | ||
} | ||
Err(diagnostic) => diagnostic, | ||
} | ||
} | ||
|
||
fn create_not_found_key_error(&self, name: &str, table: &TableType, range: Range) -> Diagnostic { | ||
self.create_diagnostic(TypeError::KeyNotFoundInTable(name.to_string(), table.to_string(), Some(range))) | ||
} | ||
|
||
fn check_call_member(&mut self, call: &ast::CallExpression, table: &TableType) -> Result<Type, Diagnostic> { | ||
let name = call.name.lexeme(); | ||
if let Some(member_type) = table.get_type(name) { | ||
self.check_call_type(member_type, &call.args, call.name.range.clone()) | ||
} else { | ||
Err(self.create_not_found_key_error(name, table, call.get_range())) | ||
} | ||
} | ||
} |
Oops, something went wrong.