Add and pass validation test

This commit is contained in:
Jeff 2024-06-26 16:24:41 -04:00
parent 9a2e4f3649
commit 29bbcb019d
6 changed files with 67 additions and 69 deletions

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
mod validation;
use dust_lang::*; use dust_lang::*;
#[test] #[test]

View 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()
}]
);
}

View File

@ -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!(