feat: semantic check for binary operator

master
Araozu 2024-08-27 08:14:15 -05:00
parent ee9b12253d
commit f0cde4a28e
4 changed files with 94 additions and 11 deletions

View File

@ -29,6 +29,7 @@
- [x] Simple type checking - [x] Simple type checking
- [x] Check for conflicting identifiers at the current scope - [x] Check for conflicting identifiers at the current scope
- [x] Semantic check for unary operator - [x] Semantic check for unary operator
- [x] Semantic check for binary operator
## v0.1.0 ## v0.1.0

View File

@ -52,6 +52,8 @@ impl SemanticCheck for Expression<'_> {
})); }));
} }
} }
Ok(())
} }
_ => { _ => {
let (error_start, error_end) = fun.get_position(); let (error_start, error_end) = fun.get_position();
@ -68,11 +70,11 @@ impl SemanticCheck for Expression<'_> {
} }
// These are empty because they have nothing to check, // These are empty because they have nothing to check,
// their existance alone is correct // their existance alone is correct
Expression::Int(_) => {} Expression::Int(_) => Ok(()),
Expression::Float(_) => {} Expression::Float(_) => Ok(()),
Expression::String(_) => {} Expression::String(_) => Ok(()),
Expression::Boolean(_) => {} Expression::Boolean(_) => Ok(()),
Expression::Identifier(_) => {} Expression::Identifier(_) => Ok(()),
Expression::UnaryOperator(operator, expression) => { Expression::UnaryOperator(operator, expression) => {
// There are a limited amount of unary operators, // There are a limited amount of unary operators,
// so their checking is not generalized // so their checking is not generalized
@ -81,6 +83,7 @@ impl SemanticCheck for Expression<'_> {
("!", Type::Value(t)) => { ("!", Type::Value(t)) => {
if t == "Bool" { if t == "Bool" {
// Ok, empty // Ok, empty
return Ok(());
} else { } else {
// Error: unary negation can only be applied to a Bool // Error: unary negation can only be applied to a Bool
let (error_start, error_end) = expression.get_position(); let (error_start, error_end) = expression.get_position();
@ -100,6 +103,29 @@ impl SemanticCheck for Expression<'_> {
reason: format!("Expected a Bool, got a function",), reason: format!("Expected a Bool, got a function",),
})); }));
} }
("-", Type::Value(t)) => {
if t == "Int" || t == "Float" {
// Ok, empty
return Ok(());
} else {
// Error: unary negation can only be applied to a Number
let (error_start, error_end) = expression.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Expected a Float or Int, got a {}", t),
}));
}
}
("-", Type::Function(_, _)) => {
// Error: unary negation can only be applied to a Bool
let (error_start, error_end) = expression.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("Expected a Float or Int, got a function",),
}));
}
(op, _) => { (op, _) => {
// Compiler error: something that shouldn't be // Compiler error: something that shouldn't be
// parsed as a unary operator was found. // parsed as a unary operator was found.
@ -107,11 +133,68 @@ impl SemanticCheck for Expression<'_> {
} }
} }
} }
Expression::BinaryOperator(_, _, _) => unimplemented!(), Expression::BinaryOperator(left_expr, right_expr, op) => {
// 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,
// 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)
}
None => {
// If the operator is not found its a user error,
// because we allow arbitrary operators
let (error_start, error_end) = (op.position, op.get_end_position());
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!("The binary operator {} does not exist", op.value),
}));
}
};
if op_params.len() != 2 {
// If an operator has any other number
// of parameters, it's a bug in the compiler
unreachable!(
"Compiler bug: a binary operator didn't have 2 parameters: {:?}",
op_params
)
} }
let left_expr_type = left_expr.get_type(scope)?;
let right_expr_type = right_expr.get_type(scope)?;
if !left_expr_type.is_value(&op_params[0]) {
let (error_start, error_end) = left_expr.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
"Expected a {}, got a {:?} on the left side of the {} operator",
op_params[0], left_expr_type, op.value
),
}));
}
if !right_expr_type.is_value(&op_params[1]) {
let (error_start, error_end) = right_expr.get_position();
return Err(MistiError::Semantic(SemanticError {
error_start,
error_end,
reason: format!(
"Expected a {}, got a {:?} on the right side of the {} operator",
op_params[1], left_expr_type, op.value
),
}));
}
// After all these checks, we are ok
Ok(()) Ok(())
} }
}
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -58,7 +58,7 @@ impl Typed for Expression<'_> {
})), })),
} }
} }
_ => todo!("Get datatype of an expression that resolves into a function call"), _ => unimplemented!("Get datatype of an expression that resolves into a function call"),
} }
} }
Expression::UnaryOperator(op, exp) => { Expression::UnaryOperator(op, exp) => {

View File

@ -53,8 +53,7 @@ impl<'a> Parseable<'a> for ModuleAST<'a> {
// Ignore comments, if any // Ignore comments, if any
if let Some(s) = tokens.get(current_pos) { if let Some(s) = tokens.get(current_pos) {
if s.token_type == TokenType::Comment if s.token_type == TokenType::Comment || s.token_type == TokenType::MultilineComment
|| s.token_type == TokenType::MultilineComment
{ {
current_pos += 1; current_pos += 1;
continue; continue;