Add and pass validation test
This commit is contained in:
parent
9a2e4f3649
commit
29bbcb019d
@ -9,7 +9,7 @@ use crate::{
|
|||||||
Context, Value,
|
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)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct Assignment {
|
pub struct Assignment {
|
||||||
@ -54,46 +54,10 @@ impl AbstractNode for Assignment {
|
|||||||
context.set_type(self.identifier.node.clone(), r#type)?;
|
context.set_type(self.identifier.node.clone(), r#type)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(ValidationError::CannotAssignToNone(
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,13 +132,13 @@ impl AbstractNode for FunctionCall {
|
|||||||
(None, None) => {}
|
(None, None) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
return Ok(());
|
||||||
} else {
|
|
||||||
Err(ValidationError::ExpectedFunction {
|
|
||||||
actual: function_node_type,
|
|
||||||
position: self.function_expression.position(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Err(ValidationError::ExpectedFunction {
|
||||||
|
actual: function_node_type,
|
||||||
|
position: self.function_expression.position(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate(
|
fn evaluate(
|
||||||
@ -244,12 +244,12 @@ impl AbstractNode for FunctionCall {
|
|||||||
.map(|option| option.map(|value| Evaluation::Return(value)));
|
.map(|option| option.map(|value| Evaluation::Return(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(RuntimeError::ValidationFailure(
|
Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::ExpectedFunction {
|
ValidationError::ExpectedFunction {
|
||||||
actual: value.r#type(context)?,
|
actual: value.r#type(context)?,
|
||||||
position: function_position,
|
position: function_position,
|
||||||
},
|
},
|
||||||
));
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
fn expected_type(&self, context: &Context) -> Result<Option<Type>, ValidationError> {
|
||||||
|
@ -12,7 +12,10 @@ use std::{
|
|||||||
vec,
|
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 ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
use context::Context;
|
use context::Context;
|
||||||
@ -20,7 +23,6 @@ use error::{DustError, RuntimeError, TypeConflict, ValidationError};
|
|||||||
use lexer::{lex, Token};
|
use lexer::{lex, Token};
|
||||||
use parser::{parse, parser};
|
use parser::{parse, parser};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
pub use value::Value;
|
|
||||||
|
|
||||||
pub fn interpret(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> {
|
pub fn interpret(source_id: &str, source: &str) -> Result<Option<Value>, InterpreterError> {
|
||||||
let interpreter = Interpreter::new(Context::new(None));
|
let interpreter = Interpreter::new(Context::new(None));
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
mod validation;
|
||||||
|
|
||||||
use dust_lang::*;
|
use dust_lang::*;
|
||||||
|
|
||||||
#[test]
|
#[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::{
|
use dust_lang::{
|
||||||
abstract_tree::{Block, Expression, Statement, Type, WithPos},
|
abstract_tree::{Block, Expression, Statement, Type, WithPos},
|
||||||
context::Context,
|
context::Context,
|
||||||
error::{DustError, TypeConflict, ValidationError},
|
|
||||||
identifier::Identifier,
|
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]
|
#[test]
|
||||||
fn function_variable() {
|
fn function_variable() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user