Add seperate interpreter step for type checking
This commit is contained in:
parent
d1ac97507b
commit
4b22221322
@ -26,7 +26,6 @@ impl AbstractTree for Assignment {
|
||||
|
||||
let identifier_node = node.child(0).unwrap();
|
||||
let identifier = Identifier::from_syntax_node(source, identifier_node, context)?;
|
||||
let identifier_type = identifier.expected_type(context)?;
|
||||
|
||||
let type_node = node.child(1).unwrap();
|
||||
let type_definition = if type_node.kind() == "type_definition" {
|
||||
@ -54,43 +53,6 @@ impl AbstractTree for Assignment {
|
||||
|
||||
let statement_node = node.child(child_count - 1).unwrap();
|
||||
let statement = Statement::from_syntax_node(source, statement_node, context)?;
|
||||
let statement_type = statement.expected_type(context)?;
|
||||
|
||||
if let Some(type_definition) = &type_definition {
|
||||
match operator {
|
||||
AssignmentOperator::Equal => {
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = type_definition.inner() {
|
||||
item_type
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
} else {
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&identifier_type)
|
||||
.map_err(|error| error.at_node(identifier_node, source))?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
} else {
|
||||
match operator {
|
||||
AssignmentOperator::Equal => {}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = identifier_type {
|
||||
item_type
|
||||
.check(&statement_type)
|
||||
.map_err(|error| error.at_node(statement_node, source))?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
let variable_key = identifier.inner().clone();
|
||||
let variable_type = if let Some(definition) = &type_definition {
|
||||
@ -98,7 +60,7 @@ impl AbstractTree for Assignment {
|
||||
} else if let Some((_, r#type)) = context.variables()?.get(identifier.inner()) {
|
||||
r#type.clone()
|
||||
} else {
|
||||
statement_type
|
||||
statement.expected_type(context)?
|
||||
};
|
||||
|
||||
context.set(variable_key, Value::none(), Some(variable_type))?;
|
||||
@ -111,6 +73,40 @@ impl AbstractTree for Assignment {
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, context: &Map) -> Result<()> {
|
||||
let statement_type = self.statement.expected_type(context)?;
|
||||
|
||||
if let Some(type_definition) = &self.type_definition {
|
||||
match self.operator {
|
||||
AssignmentOperator::Equal => {
|
||||
type_definition.inner().check(&statement_type)?;
|
||||
}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = type_definition.inner() {
|
||||
item_type.check(&statement_type)?;
|
||||
} else {
|
||||
type_definition
|
||||
.inner()
|
||||
.check(&self.identifier.expected_type(context)?)?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
} else {
|
||||
match self.operator {
|
||||
AssignmentOperator::Equal => {}
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Type::List(item_type) = self.identifier.expected_type(context)? {
|
||||
item_type.check(&statement_type)?;
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let key = self.identifier.inner();
|
||||
let value = self.statement.run(source, context)?;
|
||||
|
@ -50,6 +50,18 @@ impl AbstractTree for Block {
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
for statement in &self.statements {
|
||||
if let Statement::Return(inner_statement) = statement {
|
||||
return inner_statement.check_type(_source, _context);
|
||||
} else {
|
||||
statement.check_type(_source, _context)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
if self.is_async {
|
||||
let statements = &self.statements;
|
||||
|
@ -140,6 +140,10 @@ impl AbstractTree for BuiltInValue {
|
||||
Ok(built_in_value)
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
Ok(self.get().clone())
|
||||
}
|
||||
|
@ -65,27 +65,39 @@ impl AbstractTree for Expression {
|
||||
Ok(expression)
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
match self {
|
||||
Expression::Value(value_node) => value_node.run(source, context),
|
||||
Expression::Identifier(identifier) => identifier.run(source, context),
|
||||
Expression::Math(math) => math.run(source, context),
|
||||
Expression::Logic(logic) => logic.run(source, context),
|
||||
Expression::FunctionCall(function_call) => function_call.run(source, context),
|
||||
Expression::Index(index) => index.run(source, context),
|
||||
Expression::Yield(r#yield) => r#yield.run(source, context),
|
||||
Expression::Value(value_node) => value_node.run(_source, _context),
|
||||
Expression::Identifier(identifier) => identifier.run(_source, _context),
|
||||
Expression::Math(math) => math.run(_source, _context),
|
||||
Expression::Logic(logic) => logic.run(_source, _context),
|
||||
Expression::FunctionCall(function_call) => function_call.run(_source, _context),
|
||||
Expression::Index(index) => index.run(_source, _context),
|
||||
Expression::Yield(r#yield) => r#yield.run(_source, _context),
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
match self {
|
||||
Expression::Value(value_node) => value_node.expected_type(context),
|
||||
Expression::Identifier(identifier) => identifier.expected_type(context),
|
||||
Expression::Math(math) => math.expected_type(context),
|
||||
Expression::Logic(logic) => logic.expected_type(context),
|
||||
Expression::FunctionCall(function_call) => function_call.expected_type(context),
|
||||
Expression::Index(index) => index.expected_type(context),
|
||||
Expression::Yield(r#yield) => r#yield.expected_type(context),
|
||||
Expression::Value(value_node) => value_node.check_type(_source, _context),
|
||||
Expression::Identifier(identifier) => identifier.check_type(_source, _context),
|
||||
Expression::Math(math) => math.check_type(_source, _context),
|
||||
Expression::Logic(logic) => logic.check_type(_source, _context),
|
||||
Expression::FunctionCall(function_call) => function_call.check_type(_source, _context),
|
||||
Expression::Index(index) => index.check_type(_source, _context),
|
||||
Expression::Yield(r#yield) => r#yield.check_type(_source, _context),
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
match self {
|
||||
Expression::Value(value_node) => value_node.expected_type(_context),
|
||||
Expression::Identifier(identifier) => identifier.expected_type(_context),
|
||||
Expression::Math(math) => math.expected_type(_context),
|
||||
Expression::Logic(logic) => logic.expected_type(_context),
|
||||
Expression::FunctionCall(function_call) => function_call.expected_type(_context),
|
||||
Expression::Index(index) => index.expected_type(_context),
|
||||
Expression::Yield(r#yield) => r#yield.expected_type(_context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,10 @@ impl AbstractTree for For {
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
Ok(Type::None)
|
||||
}
|
||||
|
@ -25,9 +25,7 @@ impl AbstractTree for FunctionCall {
|
||||
let function_node = node.child(0).unwrap();
|
||||
let function_expression =
|
||||
FunctionExpression::from_syntax_node(source, function_node, context)?;
|
||||
let function_type = function_expression.expected_type(context)?;
|
||||
|
||||
let mut minimum_parameter_count = 0;
|
||||
let mut arguments = Vec::new();
|
||||
|
||||
for index in 2..node.child_count() - 1 {
|
||||
@ -35,48 +33,53 @@ impl AbstractTree for FunctionCall {
|
||||
|
||||
if child.is_named() {
|
||||
let expression = Expression::from_syntax_node(source, child, context)?;
|
||||
let expression_type = expression.expected_type(context)?;
|
||||
let argument_index = arguments.len();
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types, ..
|
||||
} = &function_type
|
||||
{
|
||||
if let Some(r#type) = parameter_types.get(argument_index) {
|
||||
if let Type::Option(_) = r#type {
|
||||
} else {
|
||||
minimum_parameter_count += 1;
|
||||
}
|
||||
|
||||
r#type
|
||||
.check(&expression_type)
|
||||
.map_err(|error| error.at_node(child, source))?;
|
||||
}
|
||||
}
|
||||
|
||||
arguments.push(expression);
|
||||
}
|
||||
}
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types: _, ..
|
||||
} = &function_type
|
||||
{
|
||||
if arguments.len() < minimum_parameter_count {
|
||||
return Err(Error::ExpectedFunctionArgumentMinimum {
|
||||
source: source[function_node.byte_range()].to_string(),
|
||||
minumum_expected: minimum_parameter_count,
|
||||
actual: arguments.len(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(FunctionCall {
|
||||
function_expression,
|
||||
arguments,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, context: &Map) -> Result<()> {
|
||||
let function_type = self.function_expression.expected_type(context)?;
|
||||
let mut minimum_parameter_count = 0;
|
||||
|
||||
for (index, expression) in self.arguments.iter().enumerate() {
|
||||
if let Type::Function {
|
||||
parameter_types, ..
|
||||
} = &function_type
|
||||
{
|
||||
if let Some(r#type) = parameter_types.get(index) {
|
||||
if let Type::Option(_) = r#type {
|
||||
} else {
|
||||
minimum_parameter_count += 1;
|
||||
}
|
||||
|
||||
r#type.check(&expression.expected_type(context)?)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Type::Function {
|
||||
parameter_types: _, ..
|
||||
} = &function_type
|
||||
{
|
||||
if self.arguments.len() < minimum_parameter_count {
|
||||
return Err(Error::ExpectedFunctionArgumentMinimum {
|
||||
source: "TODO".to_string(),
|
||||
minumum_expected: minimum_parameter_count,
|
||||
actual: self.arguments.len(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let (name, value) = match &self.function_expression {
|
||||
FunctionExpression::Identifier(identifier) => {
|
||||
|
@ -64,6 +64,10 @@ impl AbstractTree for FunctionExpression {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
match self {
|
||||
FunctionExpression::Identifier(identifier) => identifier.expected_type(context),
|
||||
|
@ -43,6 +43,10 @@ impl AbstractTree for Identifier {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
if let Some((_value, r#type)) = context.variables()?.get(&self.0) {
|
||||
Ok(r#type.clone())
|
||||
|
@ -81,6 +81,10 @@ impl AbstractTree for IfElse {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
self.if_block.expected_type(context)
|
||||
}
|
||||
|
@ -89,6 +89,10 @@ impl AbstractTree for Index {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
match self.collection.expected_type(context)? {
|
||||
Type::List(item_type) => Ok(*item_type.clone()),
|
||||
|
@ -91,6 +91,10 @@ impl AbstractTree for IndexAssignment {
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
Ok(Type::None)
|
||||
}
|
||||
|
@ -48,6 +48,10 @@ impl AbstractTree for IndexExpression {
|
||||
Ok(abstract_node)
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
match self {
|
||||
IndexExpression::Value(value_node) => value_node.run(source, context),
|
||||
|
@ -88,6 +88,10 @@ impl AbstractTree for Logic {
|
||||
Ok(Value::Boolean(result))
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Map) -> Result<Type> {
|
||||
Ok(Type::Boolean)
|
||||
}
|
||||
|
@ -76,6 +76,10 @@ impl AbstractTree for Match {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
let (_, first_statement) = self.options.first().unwrap();
|
||||
|
||||
|
@ -62,6 +62,10 @@ impl AbstractTree for Math {
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
self.left.expected_type(context)
|
||||
}
|
||||
|
@ -59,6 +59,18 @@ impl AbstractTree for Root {
|
||||
Ok(Root { statements })
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
for statement in &self.statements {
|
||||
if let Statement::Return(inner_statement) = statement {
|
||||
return inner_statement.check_type(_source, _context);
|
||||
} else {
|
||||
statement.check_type(_source, _context)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let mut value = Value::none();
|
||||
|
||||
@ -95,5 +107,8 @@ pub trait AbstractTree: Sized {
|
||||
/// Execute dust code by traversing the tree.
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value>;
|
||||
|
||||
/// Verify the type compatibility of this node.
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()>;
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type>;
|
||||
}
|
||||
|
@ -80,6 +80,22 @@ impl AbstractTree for Statement {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
match self {
|
||||
Statement::Assignment(assignment) => assignment.check_type(_source, _context),
|
||||
Statement::Expression(expression) => expression.check_type(_source, _context),
|
||||
Statement::IfElse(if_else) => if_else.check_type(_source, _context),
|
||||
Statement::Match(r#match) => r#match.check_type(_source, _context),
|
||||
Statement::While(r#while) => r#while.check_type(_source, _context),
|
||||
Statement::Block(block) => block.check_type(_source, _context),
|
||||
Statement::For(r#for) => r#for.check_type(_source, _context),
|
||||
Statement::IndexAssignment(index_assignment) => {
|
||||
index_assignment.check_type(_source, _context)
|
||||
}
|
||||
Statement::Return(statement) => statement.check_type(_source, _context),
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
match self {
|
||||
Statement::Assignment(assignment) => assignment.expected_type(context),
|
||||
|
@ -38,6 +38,10 @@ impl AbstractTree for TypeDefinition {
|
||||
self.r#type.run(source, context)
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
self.r#type.check_type(_source, _context)
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Map) -> Result<Type> {
|
||||
self.r#type.expected_type(context)
|
||||
}
|
||||
@ -239,6 +243,10 @@ impl AbstractTree for Type {
|
||||
Ok(r#type)
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
Ok(Value::none())
|
||||
}
|
||||
|
@ -77,10 +77,6 @@ impl AbstractTree for ValueNode {
|
||||
let statement =
|
||||
Statement::from_syntax_node(source, child_syntax_node, context)?;
|
||||
|
||||
if let Some(type_definition) = ¤t_type {
|
||||
type_definition.check(&statement.expected_type(context)?)?;
|
||||
}
|
||||
|
||||
child_nodes.insert(current_key.clone(), (statement, current_type.clone()));
|
||||
}
|
||||
}
|
||||
@ -122,6 +118,38 @@ impl AbstractTree for ValueNode {
|
||||
Ok(value_node)
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, context: &Map) -> Result<()> {
|
||||
match self {
|
||||
ValueNode::Function(function) => function.check_type(_source, context)?,
|
||||
ValueNode::List(expression_list) => {
|
||||
for expression in expression_list {
|
||||
expression.check_type(_source, context)?;
|
||||
}
|
||||
}
|
||||
ValueNode::BuiltInValue(built_in_value) => {
|
||||
built_in_value.check_type(_source, context)?
|
||||
}
|
||||
ValueNode::Map(map) => {
|
||||
for (_, (statement, r#type)) in map {
|
||||
statement.check_type(_source, context)?;
|
||||
|
||||
if let Some(r#type) = r#type {
|
||||
r#type.check_type(_source, context)?;
|
||||
r#type.check(&statement.expected_type(context)?)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ValueNode::Option(option) => {
|
||||
if let Some(expression) = option {
|
||||
expression.check_type(_source, context)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
let value = match self {
|
||||
ValueNode::Boolean(value_source) => Value::Boolean(value_source.parse().unwrap()),
|
||||
|
@ -25,6 +25,11 @@ impl AbstractTree for While {
|
||||
Ok(While { expression, block })
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
self.expression.check_type(_source, _context)?;
|
||||
self.block.check_type(_source, _context)
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
while self.expression.run(source, context)?.as_boolean()? {
|
||||
self.block.run(source, context)?;
|
||||
|
@ -44,6 +44,10 @@ impl AbstractTree for Yield {
|
||||
Ok(Yield { call })
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
|
||||
self.call.check_type(_source, _context)
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Map) -> Result<Value> {
|
||||
self.call.run(source, context)
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ impl Interpreter {
|
||||
};
|
||||
|
||||
if let Some(abstract_tree) = &self.abstract_tree {
|
||||
abstract_tree.check_type(source, &self.context)?;
|
||||
abstract_tree.run(source, &self.context)
|
||||
} else {
|
||||
Ok(Value::none())
|
||||
|
@ -93,11 +93,6 @@ impl AbstractTree for Function {
|
||||
let body_node = node.child(child_count - 1).unwrap();
|
||||
let body = Block::from_syntax_node(source, body_node, &function_context)?;
|
||||
|
||||
return_type
|
||||
.inner()
|
||||
.check(&body.expected_type(&function_context)?)
|
||||
.map_err(|error| error.at_node(body_node, source))?;
|
||||
|
||||
let r#type = Type::function(parameter_types, return_type.take_inner());
|
||||
|
||||
Ok(Self::ContextDefined(ContextDefinedFunction::new(
|
||||
@ -105,6 +100,19 @@ impl AbstractTree for Function {
|
||||
)))
|
||||
}
|
||||
|
||||
fn check_type(&self, _source: &str, context: &Map) -> Result<()> {
|
||||
match self {
|
||||
Function::BuiltIn(_built_in_function) => {}
|
||||
Function::ContextDefined(function) => {
|
||||
function
|
||||
.return_type()
|
||||
.check(&function.body.expected_type(&context)?)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Map) -> Result<Value> {
|
||||
Ok(Value::Function(self.clone()))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user