Add and pass validation test
This commit is contained in:
parent
9a2e4f3649
commit
29bbcb019d
@ -9,7 +9,7 @@ use crate::{
|
||||
Context, Value,
|
||||
};
|
||||
|
||||
use super::{AbstractNode, Evaluation, Expression, Statement, Type, TypeConstructor, WithPosition};
|
||||
use super::{AbstractNode, Evaluation, Statement, Type, TypeConstructor, WithPosition};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Assignment {
|
||||
@ -54,46 +54,10 @@ impl AbstractNode for Assignment {
|
||||
context.set_type(self.identifier.node.clone(), r#type)?;
|
||||
} else {
|
||||
return Err(ValidationError::CannotAssignToNone(
|
||||
self.statement.position(),
|
||||
self.statement.last_evaluated_statement().position(),
|
||||
));
|
||||
};
|
||||
|
||||
let relevant_statement = self.statement.last_evaluated_statement();
|
||||
|
||||
if let (Some(constructor), Statement::Expression(Expression::FunctionCall(function_call))) =
|
||||
(&self.constructor, relevant_statement)
|
||||
{
|
||||
let declared_type = constructor.clone().construct(context)?;
|
||||
let function_type = function_call
|
||||
.node
|
||||
.function_expression()
|
||||
.expected_type(context)?;
|
||||
|
||||
if let Some(Type::Function {
|
||||
return_type,
|
||||
type_parameters: Some(type_parameters),
|
||||
..
|
||||
}) = function_type
|
||||
{
|
||||
if let Some(Type::Generic { identifier, .. }) = return_type.map(|r#box| *r#box) {
|
||||
let returned_parameter = type_parameters
|
||||
.into_iter()
|
||||
.find(|parameter| parameter == &identifier);
|
||||
|
||||
if let Some(parameter) = returned_parameter {
|
||||
context.set_type(parameter, declared_type)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(ValidationError::ExpectedFunction {
|
||||
actual: function_type.unwrap(),
|
||||
position: function_call.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -132,13 +132,13 @@ impl AbstractNode for FunctionCall {
|
||||
(None, None) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ValidationError::ExpectedFunction {
|
||||
actual: function_node_type,
|
||||
position: self.function_expression.position(),
|
||||
})
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(ValidationError::ExpectedFunction {
|
||||
actual: function_node_type,
|
||||
position: self.function_expression.position(),
|
||||
})
|
||||
}
|
||||
|
||||
fn evaluate(
|
||||
@ -244,12 +244,12 @@ impl AbstractNode for FunctionCall {
|
||||
.map(|option| option.map(|value| Evaluation::Return(value)));
|
||||
}
|
||||
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::ExpectedFunction {
|
||||
actual: value.r#type(context)?,
|
||||
position: function_position,
|
||||
},
|
||||
));
|
||||
))
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
||||
|
@ -12,7 +12,10 @@ use std::{
|
||||
vec,
|
||||
};
|
||||
|
||||
use abstract_tree::{AbstractTree, Type};
|
||||
pub use abstract_tree::Type;
|
||||
pub use value::Value;
|
||||
|
||||
use abstract_tree::AbstractTree;
|
||||
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||
use chumsky::prelude::*;
|
||||
use context::Context;
|
||||
@ -20,7 +23,6 @@ use error::{DustError, RuntimeError, TypeConflict, ValidationError};
|
||||
use lexer::{lex, Token};
|
||||
use parser::{parse, parser};
|
||||
use rayon::prelude::*;
|
||||
pub use value::Value;
|
||||
|
||||
pub fn interpret(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> {
|
||||
let interpreter = Interpreter::new(Context::new(None));
|
||||
|
@ -1,3 +1,5 @@
|
||||
mod validation;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
|
51
dust-lang/tests/validation.rs
Normal file
51
dust-lang/tests/validation.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use dust_lang::{
|
||||
error::{DustError, TypeConflict, ValidationError},
|
||||
*,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn set_variable_with_type_error() {
|
||||
assert_eq!(
|
||||
interpret("test", "foobar: str = true")
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![DustError::Validation {
|
||||
error: ValidationError::TypeCheck {
|
||||
conflict: TypeConflict {
|
||||
actual: Type::Boolean,
|
||||
expected: Type::String
|
||||
},
|
||||
actual_position: (14, 18).into(),
|
||||
expected_position: Some((8, 11).into())
|
||||
},
|
||||
position: (0, 18).into()
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_return_type_error() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
"test",
|
||||
"
|
||||
foo = fn () -> str { 'foo' }
|
||||
|
||||
bar: int = foo()
|
||||
"
|
||||
)
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![DustError::Validation {
|
||||
error: ValidationError::TypeCheck {
|
||||
conflict: TypeConflict {
|
||||
actual: Type::String,
|
||||
expected: Type::Integer
|
||||
},
|
||||
actual_position: (66, 71).into(),
|
||||
expected_position: Some((60, 63).into())
|
||||
},
|
||||
position: (55, 71).into()
|
||||
}]
|
||||
);
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use dust_lang::{
|
||||
abstract_tree::{Block, Expression, Statement, Type, WithPos},
|
||||
context::Context,
|
||||
error::{DustError, TypeConflict, ValidationError},
|
||||
identifier::Identifier,
|
||||
*,
|
||||
};
|
||||
@ -22,26 +21,6 @@ fn set_variable_with_type() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_variable_with_type_error() {
|
||||
assert_eq!(
|
||||
interpret("test", "foobar: str = true")
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![DustError::Validation {
|
||||
error: ValidationError::TypeCheck {
|
||||
conflict: TypeConflict {
|
||||
actual: Type::Boolean,
|
||||
expected: Type::String
|
||||
},
|
||||
actual_position: (14, 18).into(),
|
||||
expected_position: Some((8, 11).into())
|
||||
},
|
||||
position: (0, 18).into()
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_variable() {
|
||||
assert_eq!(
|
||||
|
Loading…
Reference in New Issue
Block a user