feat: semantic check for binary operator
This commit is contained in:
parent
ee9b12253d
commit
f0cde4a28e
@ -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
|
||||||
|
@ -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)]
|
||||||
|
@ -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) => {
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user