Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: impl io stdlib and union #27

Merged
merged 2 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 267 additions & 69 deletions src/ast/ast.rs

Large diffs are not rendered by default.

94 changes: 19 additions & 75 deletions src/ast/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ impl Token {
Token::new(TokenKind::BlockComment(comment), range)
}

pub fn is_triple_dot(&self) -> bool {
match &self.kind {
TokenKind::TripleDot => true,
_ => false,
}
}

pub fn new_keyword_or_identifier(range: Range, keyword: String) -> Token {
match keyword.as_str() {
"function" => Token::new(TokenKind::Function, range),
Expand Down Expand Up @@ -149,10 +156,11 @@ pub enum TokenKind {
impl Token {
pub fn lexeme(&self) -> &str {
match &self.kind {
TokenKind::Identifier(name) => name,
TokenKind::Number(number) => number,
TokenKind::String(string) => string,
_ => panic!("Invalid token"),
TokenKind::Identifier(name) => name.as_str(),
TokenKind::Number(number) => number.as_str(),
TokenKind::String(string) => string.as_str(),
TokenKind::TripleDot => "...",
_ => "",
}
}

Expand All @@ -162,81 +170,17 @@ impl Token {
_ => false,
}
}

pub fn is_identifier(&self) -> bool {
match &self.kind {
TokenKind::Identifier(_) => true,
_ => false,
}
}
pub fn is_comment(&self) -> bool {
match &self.kind {
TokenKind::Comment(_) | TokenKind::BlockComment(_) => true,
_ => false,
}
}
}

impl TokenKind {
pub fn to_string(&self) -> &str {
let text = match self {
TokenKind::Identifier(name) => name,
TokenKind::Number(number) => number.as_str(),
TokenKind::String(string) => string.as_str(),
TokenKind::EOF => "EOF",
TokenKind::Function => "function",
TokenKind::Local => "local",
TokenKind::If => "if",
TokenKind::Then => "then",
TokenKind::Else => "else",
TokenKind::ElseIf => "elseif",
TokenKind::End => "end",
TokenKind::While => "while",
TokenKind::Do => "do",
TokenKind::For => "for",
TokenKind::In => "in",
TokenKind::Repeat => "repeat",
TokenKind::Until => "until",
TokenKind::Return => "return",
TokenKind::Break => "break",
TokenKind::True => "true",
TokenKind::False => "false",
TokenKind::Nil => "nil",
TokenKind::Type => "type",
TokenKind::Enum => "enum",
TokenKind::Continue => "continue",
TokenKind::Assign => "=",
TokenKind::PlusAssign => "+=",
TokenKind::MinusAssign => "-=",
TokenKind::StarAssign => "*=",
TokenKind::SlashAssign => "/=",
TokenKind::NotEqual => "~=",
TokenKind::LessEqual => "<=",
TokenKind::GreaterEqual => ">=",
TokenKind::DoubleDot => "..",
TokenKind::TripleDot => "...",
TokenKind::LeftParen => "(",
TokenKind::RightParen => ")",
TokenKind::LeftBrace => "{",
TokenKind::RightBrace => "}",
TokenKind::LeftBracket => "[",
TokenKind::RightBracket => "]",
TokenKind::Comma => ",",
TokenKind::Semicolon => ";",
TokenKind::Colon => ":",
TokenKind::DoubleColon => "::",
TokenKind::Dot => ".",
TokenKind::Tilde => "~",
TokenKind::Hash => "#",
TokenKind::Plus => "+",
TokenKind::Minus => "-",
TokenKind::Star => "*",
TokenKind::Slash => "/",
TokenKind::DoubleSlash => "//",
TokenKind::Not => "not",
TokenKind::Percent => "%",
TokenKind::Equal => "==",
TokenKind::Less => "<",
TokenKind::Greater => ">",
TokenKind::Or => "or",
TokenKind::And => "and",
TokenKind::Comment(_) => "comment",
TokenKind::BlockComment(_) => "block comment",
TokenKind::Require => "require",
};
return text;
}
}
87 changes: 87 additions & 0 deletions src/checker/assign_variables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use super::{type_utils::CheckResult, Checker};
use crate::{ast::ast, diagnostics::TypeError, types::Type};

impl<'a> Checker<'a> {
// expression = expression
//
pub fn assign_variables(&mut self, left: &ast::Expression, right: Option<&ast::Expression>) -> CheckResult<()> {
match left {
ast::Expression::Identifier(identifier) => self.assign_identifier(identifier, right),
ast::Expression::Variable(variable) => self.assign_variable(variable, right),
ast::Expression::Index(index) => self.assign_index(index, right),
ast::Expression::Member(member) => self.assign_member(member, right),
// todo: implement more assign expressions
// left[expression] = expression
// left.expression = expression
// ...
_ => todo!("Implement more assign expressions"),
}
}

// age = 20
//
pub fn assign_identifier(&mut self, left: &ast::Identifier, right: Option<&ast::Expression>) -> CheckResult<()> {
let lexeme = left.name.as_str();

let right_type = match right {
Some(right) => self.check_expression(right)?.unwrap_or(Type::Nil),
None => Type::Nil,
};

let range = left.range.clone();
let left_hand_side = &(lexeme, None);
self.check_shadowing(lexeme, false, &range)?;
self.declare_global_variable(&left_hand_side, right_type, range)?;
Ok(())
}

// age:number = 20
//
pub fn assign_variable(&mut self, left: &ast::Variable, right: Option<&ast::Expression>) -> CheckResult<()> {
let lexeme = left.name.lexeme();

let right_type = match right {
Some(right) => self.check_expression(right)?.unwrap_or(Type::Nil),
None => Type::Nil,
};

let range = left.get_range();
let left_hand_side = &(lexeme, left.ty.clone());
self.check_shadowing(lexeme, false, &range)?;
self.declare_global_variable(&left_hand_side, right_type, range)?;
Ok(())
}

// a[1] = b[2]
pub fn assign_index(&mut self, index: &ast::IndexExpression, right: Option<&ast::Expression>) -> CheckResult<()> {
let index_type = self.check_index_expression(index)?.unwrap_or(Type::Nil);
let right_type = match right {
Some(right) => self.check_expression(right)?.unwrap_or(Type::Nil),
None => Type::Nil,
};
let range = index.get_range();

if !index_type.check_match(&right_type) {
let diagnostic = TypeError::TypeMismatchAssignment(index_type.to_string(), right_type.to_string(), Some(range));
return Err(self.create_diagnostic(diagnostic));
}
Ok(())
}

// a.b = b.c
pub fn assign_member(&mut self, member: &ast::MemberExpression, right: Option<&ast::Expression>) -> CheckResult<()> {
let member_type = self.check_member_expression(member)?.unwrap_or(Type::Nil);

let right_type = match right {
Some(right) => self.check_expression(right)?.unwrap_or(Type::Nil),
None => Type::Nil,
};
let range = member.get_range();

if !member_type.check_match(&right_type) {
let diagnostic = TypeError::TypeMismatchAssignment(member_type.to_string(), right_type.to_string(), Some(range));
return Err(self.create_diagnostic(diagnostic));
}
Ok(())
}
}
12 changes: 6 additions & 6 deletions src/checker/binding.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
use crate::{diagnostics::Diagnostic, types::FunctionType, utils::range::Range};
use crate::{types::FunctionType, utils::range::Range};

use super::Checker;
use super::{type_utils::CheckResult, Checker};

type FnType = FunctionType;

impl<'a> Checker<'a> {
pub fn bind_function(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> Result<(), Diagnostic> {
pub fn bind_function(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> CheckResult<()> {
self.validate_function_arity(expected, found, rg)?;
self.validate_function_parameters(expected, found, rg)?;
self.validate_function_return_type(expected, found, rg)
}

fn validate_function_arity(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> Result<(), Diagnostic> {
fn validate_function_arity(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> CheckResult<()> {
if expected.params.len() != found.params.len() {
return Err(self.create_function_arity_mismatch(expected.params.len(), found.params.len(), rg.clone()));
}
Ok(())
}

fn validate_function_parameters(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> Result<(), Diagnostic> {
fn validate_function_parameters(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> CheckResult<()> {
for (expected_param, found_param) in expected.params.iter().zip(found.params.iter()) {
if !expected_param.check_match(found_param) {
return Err(self.create_type_mismatch(expected_param.to_owned(), found_param.to_owned(), rg.clone()));
Expand All @@ -27,7 +27,7 @@ impl<'a> Checker<'a> {
Ok(())
}

fn validate_function_return_type(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> Result<(), Diagnostic> {
fn validate_function_return_type(&mut self, expected: &FnType, found: &FnType, rg: &Range) -> CheckResult<()> {
let expected_return_type = *expected.return_type.clone();
let found_return_type = *found.return_type.clone();

Expand Down
16 changes: 16 additions & 0 deletions src/checker/check_assign_expression.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use super::{type_utils::CheckResult, Checker};
use crate::{ast::ast::AssignExpression, types::Type};

impl<'a> Checker<'a> {
pub fn check_assign_expression(&mut self, assign: &AssignExpression) -> CheckResult<Option<Type>> {
let result = assign.left.iter().enumerate().map::<CheckResult<()>, _>(|(position, variable)| {
let left_expression = variable;
let right_expression = assign.right.get(position);
self.assign_variables(left_expression, right_expression)?;
return Ok(());
});
// assign don't return a type
let _ = result.collect::<CheckResult<Vec<()>>>()?;
return Ok(None);
}
}
11 changes: 0 additions & 11 deletions src/checker/check_assign_statement.rs

This file was deleted.

23 changes: 9 additions & 14 deletions src/checker/check_binary_expression.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
use super::Checker;
use crate::{
ast::ast,
diagnostics::{Diagnostic, TypeError},
types::Type,
};
use super::{type_utils::CheckResult, Checker};
use crate::{ast::ast, diagnostics::TypeError, types::Type};

impl<'a> Checker<'a> {
pub fn check_binary_expression(&mut self, binary_expr: &ast::BinaryExpression) -> Result<Type, Diagnostic> {
let left_t = self.check_expression(&binary_expr.left)?;
let right_t = self.check_expression(&binary_expr.right)?;

let left_type = self.check_type(left_t)?;
let right_type = self.check_type(right_t)?;
pub fn check_binary_expression(&mut self, binary_expr: &ast::BinaryExpression) -> CheckResult<Option<Type>> {
let left_type = self.check_expression(&binary_expr.left)?.unwrap();
let right_type = self.check_expression(&binary_expr.right)?.unwrap();
if left_type.supports_operator(&binary_expr.operator) && right_type.supports_operator(&binary_expr.operator) {
return Ok(left_type.get_operator_result_type(&right_type, &binary_expr.operator));
let result_type = left_type.get_operator_result_type(&right_type, &binary_expr.operator);
return Ok(Some(result_type));
}

let range = binary_expr.get_range();
let diagnostic = TypeError::UnsupportedOperator(
left_type.to_string(),
binary_expr.operator.to_owned(),
right_type.to_string(),
binary_expr.operator.to_str().to_owned(),
Some(range),
);

Expand Down
18 changes: 12 additions & 6 deletions src/checker/check_block_statement.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
use std::collections::HashSet;

use super::type_utils::CheckResult;
use super::Checker;
use crate::ast::ast;
use crate::diagnostics::Diagnostic;
use crate::types::Type;

impl<'a> Checker<'a> {
pub fn check_block_statement(&mut self, block: &ast::BlockStatement) -> Result<Type, Diagnostic> {
let mut last_t = Type::Nil;
pub fn check_block_statement(&mut self, block: &ast::BlockStatement) -> CheckResult<Option<Type>> {
let mut last_type = HashSet::new();
for statement in &block.statements {
match self.check_statement(statement) {
Ok(ty) => last_t = ty,
Ok(Some(ty)) => {
last_type.insert(ty);
}
Ok(None) => {}
Err(diag) => self.diagnostics.add(diag),
}
};
}
Ok(last_t)
let array_type = self.create_type_based_array(last_type.into_iter().collect());
Ok(array_type)
}
}
Loading
Loading