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 {
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
)
}
}

View File

@ -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);

View File

@ -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,16 +115,14 @@ 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(())
}
Ok(())
}
_ => Err(Error::TypeCheck {
expected: self.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 {
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(),
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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))))))))))))