Add new tests for type checking
This commit is contained in:
parent
ce4d366bab
commit
fa7fb57600
@ -68,17 +68,30 @@ impl AbstractTree for Assignment {
|
||||
|
||||
match operator {
|
||||
AssignmentOperator::Equal => {
|
||||
type_definition.inner().check(&statement_type)?;
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
let identifier_type = identifier.expected_type(context)?;
|
||||
|
||||
if let Type::List(item_type) = type_definition.inner() {
|
||||
item_type.check(&identifier_type)?;
|
||||
item_type.check(&statement_type)?;
|
||||
item_type
|
||||
.check(&identifier_type)
|
||||
.map_err(|error| error.at_node(identifier_node, source))?;
|
||||
item_type
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
} else {
|
||||
type_definition.inner().check(&identifier_type)?;
|
||||
type_definition.inner().check(&statement_type)?;
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&identifier_type)
|
||||
.map_err(|error| error.at_node(identifier_node, source))?;
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
@ -118,8 +131,6 @@ impl AbstractTree for Assignment {
|
||||
};
|
||||
|
||||
if let Some(type_defintion) = &self.type_definition {
|
||||
type_defintion.inner().check(&new_value.r#type())?;
|
||||
|
||||
context.set(key.clone(), new_value, Some(type_defintion.inner().clone()))?;
|
||||
} else {
|
||||
context.set(key.clone(), new_value, None)?;
|
||||
@ -135,7 +146,7 @@ impl AbstractTree for Assignment {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{evaluate, Error, List, Value};
|
||||
use crate::{evaluate, Error, List, Type, Value};
|
||||
|
||||
#[test]
|
||||
fn simple_assignment() {
|
||||
@ -167,18 +178,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn list_add_wrong_type() {
|
||||
let test = evaluate(
|
||||
let result = evaluate(
|
||||
"
|
||||
x <[str]> = []
|
||||
x += 1
|
||||
",
|
||||
)
|
||||
.unwrap_err();
|
||||
);
|
||||
|
||||
match test {
|
||||
Error::WithContext { .. } => {}
|
||||
Error::TypeCheck { .. } => {}
|
||||
_ => panic!(),
|
||||
}
|
||||
assert_eq!(
|
||||
Err(Error::TypeCheck {
|
||||
expected: Type::String,
|
||||
actual: Type::Integer
|
||||
}),
|
||||
result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -49,11 +49,7 @@ impl AbstractTree for FunctionCall {
|
||||
|
||||
expected_type
|
||||
.check(&expression_type)
|
||||
.map_err(|error| Error::WithContext {
|
||||
error: Box::new(error),
|
||||
location: child.start_position(),
|
||||
source: source[child.byte_range()].to_string(),
|
||||
})?;
|
||||
.map_err(|error| error.at_node(child, source))?;
|
||||
}
|
||||
|
||||
arguments.push(expression);
|
||||
|
@ -83,7 +83,14 @@ impl Type {
|
||||
| (Type::Float, Type::Number)
|
||||
| (Type::String, Type::String) => Ok(()),
|
||||
(Type::List(self_item_type), Type::List(other_item_type)) => {
|
||||
self_item_type.check(&other_item_type)
|
||||
if self_item_type.check(&other_item_type).is_err() {
|
||||
Err(Error::TypeCheck {
|
||||
expected: self.clone(),
|
||||
actual: other.clone(),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
(
|
||||
Type::Function {
|
||||
@ -100,9 +107,7 @@ impl Type {
|
||||
.zip(other_parameter_types.iter());
|
||||
|
||||
for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
|
||||
let check = self_parameter_type.check(&other_parameter_type);
|
||||
|
||||
if let Err(Error::TypeCheck { .. }) = check {
|
||||
if self_parameter_type.check(&other_parameter_type).is_err() {
|
||||
return Err(Error::TypeCheck {
|
||||
expected: self.clone(),
|
||||
actual: other.clone(),
|
||||
@ -110,17 +115,15 @@ impl Type {
|
||||
}
|
||||
}
|
||||
|
||||
let check = self_return_type.check(other_return_type);
|
||||
|
||||
if let Err(Error::TypeCheck { .. }) = check {
|
||||
return Err(Error::TypeCheck {
|
||||
if self_return_type.check(other_return_type).is_err() {
|
||||
Err(Error::TypeCheck {
|
||||
expected: self.clone(),
|
||||
actual: other.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => Err(Error::TypeCheck {
|
||||
expected: self.clone(),
|
||||
actual: other.clone(),
|
||||
@ -229,3 +232,49 @@ impl Display for Type {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::evaluate;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_type_check() {
|
||||
let result = evaluate("x <bool> = 1");
|
||||
|
||||
assert_eq!(
|
||||
Err(Error::TypeCheck {
|
||||
expected: Type::Boolean,
|
||||
actual: Type::Integer
|
||||
}),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn callback_type_check() {
|
||||
let result = evaluate(
|
||||
"
|
||||
x = (fn cb <() -> bool>) <bool> {
|
||||
(cb)
|
||||
}
|
||||
(x (fn) <int> { 1 })
|
||||
",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Err(Error::TypeCheck {
|
||||
expected: Type::Function {
|
||||
parameter_types: vec![],
|
||||
return_type: Box::new(Type::Boolean),
|
||||
},
|
||||
actual: Type::Function {
|
||||
parameter_types: vec![],
|
||||
return_type: Box::new(Type::Integer),
|
||||
},
|
||||
}),
|
||||
result
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -156,11 +156,11 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn with_context(self, location: Point, source: String) -> Self {
|
||||
pub fn at_node(self, node: Node, source: &str) -> Self {
|
||||
Error::WithContext {
|
||||
error: Box::new(self),
|
||||
location,
|
||||
source,
|
||||
location: node.start_position(),
|
||||
source: source[node.byte_range()].to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,16 +50,6 @@ impl Function {
|
||||
pub fn call(&self, arguments: &[Expression], source: &str, context: &Map) -> Result<Value> {
|
||||
let function_context = Map::clone_from(context)?;
|
||||
|
||||
let (parameter_types, return_type) = if let Type::Function {
|
||||
parameter_types,
|
||||
return_type,
|
||||
} = &self.r#type
|
||||
{
|
||||
(parameter_types, return_type)
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
|
||||
if self.parameters.len() != arguments.len() {
|
||||
return Err(Error::ExpectedArgumentAmount {
|
||||
function_name: "",
|
||||
@ -68,27 +58,18 @@ impl Function {
|
||||
});
|
||||
}
|
||||
|
||||
let parameter_argument_pairs = self
|
||||
.parameters
|
||||
.iter()
|
||||
.zip(parameter_types.iter())
|
||||
.zip(arguments.iter());
|
||||
let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
|
||||
|
||||
for ((identifier, argument_type), expression) in parameter_argument_pairs {
|
||||
for (identifier, expression) in parameter_argument_pairs {
|
||||
let value = expression.run(source, context)?;
|
||||
let value_type = value.r#type();
|
||||
|
||||
argument_type.check(&value_type)?;
|
||||
|
||||
let key = identifier.inner().clone();
|
||||
|
||||
function_context.set(key, value, Some(value_type))?;
|
||||
function_context.set(key, value, None)?;
|
||||
}
|
||||
|
||||
let return_value = self.body.run(source, &function_context)?;
|
||||
|
||||
return_type.check(&return_value.r#type())?;
|
||||
|
||||
Ok(return_value)
|
||||
}
|
||||
}
|
||||
|
@ -53,10 +53,6 @@ impl Map {
|
||||
.write()?
|
||||
.insert(key, (value, value_type.clone()));
|
||||
|
||||
if let Some((_previous_value, previous_type)) = previous.clone() {
|
||||
previous_type.check(&value_type)?;
|
||||
}
|
||||
|
||||
Ok(previous)
|
||||
}
|
||||
}
|
||||
|
@ -114,3 +114,52 @@ Complex Function Call
|
||||
(expression
|
||||
(value
|
||||
(integer)))))))))))
|
||||
|
||||
================================================================================
|
||||
Callback Function Call
|
||||
================================================================================
|
||||
|
||||
x = (fn cb <() -> bool>) <bool> {
|
||||
(cb)
|
||||
}
|
||||
|
||||
(x (fn) <bool> { true })
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
(root
|
||||
(statement
|
||||
(assignment
|
||||
(identifier)
|
||||
(assignment_operator)
|
||||
(statement
|
||||
(expression
|
||||
(value
|
||||
(function
|
||||
(identifier)
|
||||
(type_definition
|
||||
(type
|
||||
(type)))
|
||||
(type_definition
|
||||
(type))
|
||||
(block
|
||||
(statement
|
||||
(expression
|
||||
(function_call
|
||||
(expression
|
||||
(identifier))))))))))))
|
||||
(statement
|
||||
(expression
|
||||
(function_call
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(value
|
||||
(function
|
||||
(type_definition
|
||||
(type))
|
||||
(block
|
||||
(statement
|
||||
(expression
|
||||
(value
|
||||
(boolean))))))))))))
|
||||
|
Loading…
Reference in New Issue
Block a user