Skip to content

Commit

Permalink
feat: suport table, array, maps
Browse files Browse the repository at this point in the history
  • Loading branch information
yazaldefilimone committed Aug 11, 2024
1 parent 2cd77d6 commit 72ae2d3
Show file tree
Hide file tree
Showing 12 changed files with 831 additions and 318 deletions.
68 changes: 68 additions & 0 deletions src/ast/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ pub enum Expression {
Binary(BinaryExpression),
Require(RequireExpression),
Function(FunctionExpression),
Table(TableExpression),
Member(MemberExpression),
Index(IndexExpression),
}

impl Expression {
Expand All @@ -349,6 +352,18 @@ impl Expression {
Expression::Grouped(GroupedExpression::new(expressions, range))
}

pub fn new_table(values: Vec<(Expression, Option<Expression>)>, range: Range) -> Self {
Expression::Table(TableExpression::new(values, range))
}

pub fn new_member(base: Expression, member: Expression) -> Self {
Expression::Member(MemberExpression::new(Box::new(base), Box::new(member)))
}

pub fn new_index(base: Expression, index: Expression, bracket_range: Range) -> Self {
Expression::Index(IndexExpression::new(Box::new(base), Box::new(index), bracket_range))
}

pub fn new_function(
arguments: Vec<(Token, Option<Type>)>,
return_type: Option<Type>,
Expand All @@ -369,6 +384,9 @@ impl Expression {
Expression::Grouped(grouped) => grouped.get_range(),
Expression::Unary(unary) => unary.get_range(),
Expression::Function(function) => function.get_range(),
Expression::Table(table) => table.get_range(),
Expression::Member(member) => member.get_range(),
Expression::Index(index) => index.get_range(),
}
}

Expand Down Expand Up @@ -468,6 +486,56 @@ impl UnaryExpression {
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MemberExpression {
pub base: Box<Expression>,
pub member: Box<Expression>,
}
impl MemberExpression {
pub fn new(base: Box<Expression>, member: Box<Expression>) -> Self {
MemberExpression { base, member }
}

pub fn get_range(&self) -> Range {
let left_range = self.base.get_range();
let right_range = self.member.get_range();
create_middle_range(&left_range, &right_range)
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct IndexExpression {
pub base: Box<Expression>,
pub index: Box<Expression>,
pub bracket_range: Range,
}
impl IndexExpression {
pub fn new(base: Box<Expression>, index: Box<Expression>, bracket_range: Range) -> Self {
IndexExpression { base, index, bracket_range }
}

pub fn get_range(&self) -> Range {
let left_range = self.base.get_range();
let right_range = self.index.get_range();
create_middle_range(&left_range, &right_range)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct TableExpression {
pub values: Vec<(Expression, Option<Expression>)>,
pub range: Range,
}

impl TableExpression {
pub fn new(values: Vec<(Expression, Option<Expression>)>, range: Range) -> Self {
TableExpression { values, range }
}

pub fn get_range(&self) -> Range {
self.range.clone()
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FunctionExpression {
pub arguments: Vec<(Token, Option<Type>)>,
Expand Down
104 changes: 61 additions & 43 deletions src/checker/check_call_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,72 +3,90 @@ use crate::{
ast::ast,
diagnostics::{Diagnostic, TypeError},
types::Type,
utils::range::Range,
};

impl<'a> Checker<'a> {
pub fn check_call_expression(&mut self, call_expr: &ast::CallExpression) -> Result<Type, Diagnostic> {
let name = call_expr.name.lexeme();

let (defined, scope_pointer) = self.ctx.defined_in_any_scope(name);

if !defined {
let diagnostic = TypeError::UndeclaredVariable(name.to_string(), Some(call_expr.name.range.clone()));
return Err(self.create_diagnostic(diagnostic));
return Err(
self.create_diagnostic(TypeError::UndeclaredVariable(name.to_string(), Some(call_expr.name.range.clone()))),
);
}

let call_type = self.ctx.get_variable(name, Some(scope_pointer));

if call_type.is_none() {
let diagnostic = TypeError::UndeclaredVariable(name.to_string(), Some(call_expr.name.range.clone()));
return Err(self.create_diagnostic(diagnostic));
}
match call_type.unwrap() {
Type::Function(call_type) => {
let return_t = *call_type.return_type.clone();
self.check_call_arguments(&call_expr.args, &call_type.params.to_vec())?;
return Ok(return_t);
}
Type::Unknown => {
return Ok(Type::Unknown);
let call_type = match self.ctx.get_variable(name, Some(scope_pointer)) {
Some(call_type) => call_type.clone(),
None => {
return Err(
self.create_diagnostic(TypeError::UndeclaredVariable(name.to_string(), Some(call_expr.name.range.clone()))),
);
}
_ => {
let diagnostic = TypeError::ExpectedFunction(name.to_string(), Some(call_expr.name.range.clone()));
return Err(self.create_diagnostic(diagnostic));
};

self.check_call_type(&call_type, &call_expr.args, call_expr.get_range())
}

pub fn check_call_type(
&mut self,
call_type: &Type,
args: &ast::Expression,
range: Range,
) -> Result<Type, Diagnostic> {
match call_type {
Type::Function(func_type) => {
self.check_call_arguments(args, &func_type.params)?;
Ok(*func_type.return_type.clone())
}
Type::Unknown => Ok(Type::Unknown),
_ => Err(self.create_diagnostic(TypeError::ExpectedFunction(call_type.to_string(), Some(range)))),
}
}

pub fn check_call_arguments(&mut self, args_call: &ast::Expression, params_tt: &[Type]) -> Result<(), Diagnostic> {
if let ast::Expression::Grouped(ast::GroupedExpression { expressions, range }) = args_call {
if expressions.len() != params_tt.len() {
let diagnostic = TypeError::FunctionArityMismatch(params_tt.len(), expressions.len(), Some(range.clone()));
return Err(self.create_diagnostic(diagnostic));
pub fn check_call_arguments(&mut self, args: &ast::Expression, params: &[Type]) -> Result<(), Diagnostic> {
if let ast::Expression::Grouped(ast::GroupedExpression { expressions, range }) = args {
if expressions.len() != params.len() {
return Err(self.create_diagnostic(TypeError::FunctionArityMismatch(
params.len(),
expressions.len(),
Some(range.clone()),
)));
}

for (arg_expr, param_t) in expressions.iter().zip(params_tt.iter()) {
let param_t = self.check_type(param_t.clone())?;
let arg_t = self.check_expression(arg_expr)?;
if !arg_t.check_match(&param_t) {
let range = arg_expr.get_range();
let diagnostic = TypeError::MismatchedTypes(param_t.to_string(), arg_t.to_string(), Some(range.clone()));
return Err(self.create_diagnostic(diagnostic));
for (arg_expr, param_type) in expressions.iter().zip(params.iter()) {
let inferred_type = self.check_expression(arg_expr)?;
let param_type_checked = self.check_type(param_type.clone())?;

if !inferred_type.check_match(&param_type_checked) {
return Err(self.create_diagnostic(TypeError::MismatchedTypes(
param_type_checked.to_string(),
inferred_type.to_string(),
Some(arg_expr.get_range()),
)));
}
}
return Ok(());
Ok(())
} else {
self.check_single_argument(args, params)
}
}

let arg_t = self.check_expression(args_call)?;

if params_tt.len() != 1 {
let range = args_call.get_range();
let diagnostic = TypeError::FunctionArityMismatch(params_tt.len(), 1, Some(range));
return Err(self.create_diagnostic(diagnostic));
fn check_single_argument(&mut self, arg: &ast::Expression, params: &[Type]) -> Result<(), Diagnostic> {
if params.len() != 1 {
return Err(self.create_diagnostic(TypeError::FunctionArityMismatch(params.len(), 1, Some(arg.get_range()))));
}

let param_tt = params_tt.first().unwrap();
let param_type = self.check_type(params.first().unwrap().clone())?;
let arg_type = self.check_expression(arg)?;

if !arg_t.check_match(&param_tt) {
let diagnostic = TypeError::MismatchedTypes(param_tt.to_string(), arg_t.to_string(), None);
return Err(self.create_diagnostic(diagnostic));
if !arg_type.check_match(&param_type) {
return Err(self.create_diagnostic(TypeError::MismatchedTypes(
param_type.to_string(),
arg_type.to_string(),
Some(arg.get_range()),
)));
}

Ok(())
Expand Down
3 changes: 3 additions & 0 deletions src/checker/check_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ impl<'a> Checker<'a> {
ast::Expression::Unary(unary_expr) => self.check_unary_expression(unary_expr),
ast::Expression::Grouped(grup_expr) => self.check_grouped_expression(grup_expr),
ast::Expression::Function(function) => self.check_function_expression(function),
ast::Expression::Table(table) => self.check_table_expression(table),
ast::Expression::Member(member) => self.check_member_expression(member),
ast::Expression::Index(index) => self.check_index_expression(index),
}
}
}
82 changes: 82 additions & 0 deletions src/checker/check_index_expression.rs
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()))),
),
}
}
}
68 changes: 68 additions & 0 deletions src/checker/check_member_expression.rs
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()))
}
}
}
Loading

0 comments on commit 72ae2d3

Please sign in to comment.