1
0

Add new tests for type checking

This commit is contained in:
Jeff 2023-12-15 17:27:29 -05:00
parent ce4d366bab
commit fa7fb57600
7 changed files with 144 additions and 61 deletions

View File

@ -68,17 +68,30 @@ impl AbstractTree for Assignment {
match operator { match operator {
AssignmentOperator::Equal => { 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 => { AssignmentOperator::PlusEqual => {
let identifier_type = identifier.expected_type(context)?; let identifier_type = identifier.expected_type(context)?;
if let Type::List(item_type) = type_definition.inner() { if let Type::List(item_type) = type_definition.inner() {
item_type.check(&identifier_type)?; item_type
item_type.check(&statement_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 { } else {
type_definition.inner().check(&identifier_type)?; type_definition
type_definition.inner().check(&statement_type)?; .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!(), AssignmentOperator::MinusEqual => todo!(),
@ -118,8 +131,6 @@ impl AbstractTree for Assignment {
}; };
if let Some(type_defintion) = &self.type_definition { 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()))?; context.set(key.clone(), new_value, Some(type_defintion.inner().clone()))?;
} else { } else {
context.set(key.clone(), new_value, None)?; context.set(key.clone(), new_value, None)?;
@ -135,7 +146,7 @@ impl AbstractTree for Assignment {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{evaluate, Error, List, Value}; use crate::{evaluate, Error, List, Type, Value};
#[test] #[test]
fn simple_assignment() { fn simple_assignment() {
@ -167,18 +178,19 @@ mod tests {
#[test] #[test]
fn list_add_wrong_type() { fn list_add_wrong_type() {
let test = evaluate( let result = evaluate(
" "
x <[str]> = [] x <[str]> = []
x += 1 x += 1
", ",
) );
.unwrap_err();
match test { assert_eq!(
Error::WithContext { .. } => {} Err(Error::TypeCheck {
Error::TypeCheck { .. } => {} expected: Type::String,
_ => panic!(), actual: Type::Integer
} }),
result
)
} }
} }

View File

@ -49,11 +49,7 @@ impl AbstractTree for FunctionCall {
expected_type expected_type
.check(&expression_type) .check(&expression_type)
.map_err(|error| Error::WithContext { .map_err(|error| error.at_node(child, source))?;
error: Box::new(error),
location: child.start_position(),
source: source[child.byte_range()].to_string(),
})?;
} }
arguments.push(expression); arguments.push(expression);

View File

@ -83,7 +83,14 @@ impl Type {
| (Type::Float, Type::Number) | (Type::Float, Type::Number)
| (Type::String, Type::String) => Ok(()), | (Type::String, Type::String) => Ok(()),
(Type::List(self_item_type), Type::List(other_item_type)) => { (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 { Type::Function {
@ -100,9 +107,7 @@ impl Type {
.zip(other_parameter_types.iter()); .zip(other_parameter_types.iter());
for (self_parameter_type, other_parameter_type) in parameter_type_pairs { for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
let check = self_parameter_type.check(&other_parameter_type); if self_parameter_type.check(&other_parameter_type).is_err() {
if let Err(Error::TypeCheck { .. }) = check {
return Err(Error::TypeCheck { return Err(Error::TypeCheck {
expected: self.clone(), expected: self.clone(),
actual: other.clone(), actual: other.clone(),
@ -110,17 +115,15 @@ impl Type {
} }
} }
let check = self_return_type.check(other_return_type); if self_return_type.check(other_return_type).is_err() {
Err(Error::TypeCheck {
if let Err(Error::TypeCheck { .. }) = check {
return Err(Error::TypeCheck {
expected: self.clone(), expected: self.clone(),
actual: other.clone(), actual: other.clone(),
}); })
} } else {
Ok(()) Ok(())
} }
}
_ => Err(Error::TypeCheck { _ => Err(Error::TypeCheck {
expected: self.clone(), expected: self.clone(),
actual: other.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
);
}
}

View File

@ -156,11 +156,11 @@ pub enum Error {
} }
impl 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::WithContext {
error: Box::new(self), error: Box::new(self),
location, location: node.start_position(),
source, source: source[node.byte_range()].to_string(),
} }
} }

View File

@ -50,16 +50,6 @@ impl Function {
pub fn call(&self, arguments: &[Expression], source: &str, context: &Map) -> Result<Value> { pub fn call(&self, arguments: &[Expression], source: &str, context: &Map) -> Result<Value> {
let function_context = Map::clone_from(context)?; 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() { if self.parameters.len() != arguments.len() {
return Err(Error::ExpectedArgumentAmount { return Err(Error::ExpectedArgumentAmount {
function_name: "", function_name: "",
@ -68,27 +58,18 @@ impl Function {
}); });
} }
let parameter_argument_pairs = self let parameter_argument_pairs = self.parameters.iter().zip(arguments.iter());
.parameters
.iter()
.zip(parameter_types.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 = expression.run(source, context)?;
let value_type = value.r#type();
argument_type.check(&value_type)?;
let key = identifier.inner().clone(); 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)?; let return_value = self.body.run(source, &function_context)?;
return_type.check(&return_value.r#type())?;
Ok(return_value) Ok(return_value)
} }
} }

View File

@ -53,10 +53,6 @@ impl Map {
.write()? .write()?
.insert(key, (value, value_type.clone())); .insert(key, (value, value_type.clone()));
if let Some((_previous_value, previous_type)) = previous.clone() {
previous_type.check(&value_type)?;
}
Ok(previous) Ok(previous)
} }
} }

View File

@ -114,3 +114,52 @@ Complex Function Call
(expression (expression
(value (value
(integer))))))))))) (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))))))))))))