diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index 0dd7425..e0e7668 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -58,17 +58,24 @@ impl AbstractTree for Assignment { let statement_node = node.child(child_count - 1).unwrap(); let statement = Statement::from_syntax_node(source, statement_node, context)?; + if let Some((_previous_value, previous_type)) = context.variables()?.get(identifier.inner()) + { + let type_check = previous_type.check(&statement.expected_type(context)?); + + if let Err(error) = type_check { + return Err(error.with_context( + statement_node.start_position(), + source[statement_node.byte_range()].to_string(), + )); + } + } + if let Some(type_definition) = &type_definition { let statement_type = statement.expected_type(context)?; match operator { AssignmentOperator::Equal => { - type_definition.inner().check( - &statement_type, - context, - statement_node, - source, - )?; + type_definition.inner().check(&statement_type)?; } AssignmentOperator::PlusEqual => { let identifier_type = identifier.expected_type(context)?; @@ -76,21 +83,11 @@ impl AbstractTree for Assignment { if let Type::List(item_type) = type_definition.inner() { println!("{item_type}"); - item_type.check(&identifier_type, context, identifier_node, source)?; - item_type.check(&statement_type, context, statement_node, source)?; + item_type.check(&identifier_type)?; + item_type.check(&statement_type)?; } else { - type_definition.inner().check( - &identifier_type, - context, - identifier_node, - source, - )?; - type_definition.inner().check( - &statement_type, - context, - statement_node, - source, - )?; + type_definition.inner().check(&identifier_type)?; + type_definition.inner().check(&statement_type)?; } } AssignmentOperator::MinusEqual => todo!(), @@ -128,11 +125,8 @@ impl AbstractTree for Assignment { } AssignmentOperator::Equal => value, }; - let new_value_type = new_value.r#type(context)?; - context - .variables_mut()? - .insert(key.clone(), (new_value, new_value_type)); + context.set(key.clone(), new_value)?; Ok(Value::Empty) } diff --git a/src/abstract_tree/for.rs b/src/abstract_tree/for.rs index 65e7ce5..6421f0a 100644 --- a/src/abstract_tree/for.rs +++ b/src/abstract_tree/for.rs @@ -59,7 +59,7 @@ impl AbstractTree for For { iter_context .variables_mut()? - .insert(key.clone(), (value.clone(), value.r#type(context)?)); + .insert(key.clone(), (value.clone(), value.r#type())); self.block.run(source, &mut iter_context).map(|_value| ()) })?; @@ -69,7 +69,7 @@ impl AbstractTree for For { for value in values.iter() { loop_context .variables_mut()? - .insert(key.clone(), (value.clone(), value.r#type(context)?)); + .insert(key.clone(), (value.clone(), value.r#type())); self.block.run(source, &mut loop_context.clone())?; } diff --git a/src/abstract_tree/function_call.rs b/src/abstract_tree/function_call.rs index ea12a62..17e68c3 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -52,9 +52,9 @@ impl AbstractTree for FunctionCall { let argument_type = argument.expected_type(context)?; if let Type::Function { return_type, .. } = argument_type { - r#type.check(&return_type, context, node, source)?; + r#type.check(&return_type)?; } else { - r#type.check(&argument_type, context, node, source)?; + r#type.check(&argument_type)?; } } } diff --git a/src/abstract_tree/identifier.rs b/src/abstract_tree/identifier.rs index 5145189..c8a70a7 100644 --- a/src/abstract_tree/identifier.rs +++ b/src/abstract_tree/identifier.rs @@ -43,7 +43,7 @@ impl AbstractTree for Identifier { fn expected_type(&self, context: &Map) -> Result { if let Some((value, _)) = context.variables()?.get(&self.0) { - value.r#type(context) + Ok(value.r#type()) } else { for built_in_function in BUILT_IN_FUNCTIONS { if self.0 == built_in_function.name() { diff --git a/src/abstract_tree/index_assignment.rs b/src/abstract_tree/index_assignment.rs index 61e7a77..4830ac3 100644 --- a/src/abstract_tree/index_assignment.rs +++ b/src/abstract_tree/index_assignment.rs @@ -85,7 +85,7 @@ impl AbstractTree for IndexAssignment { } AssignmentOperator::Equal => value, }; - let new_value_type = new_value.r#type(context)?; + let new_value_type = new_value.r#type(); index_context .variables_mut()? diff --git a/src/abstract_tree/type_defintion.rs b/src/abstract_tree/type_defintion.rs index 46bb084..8957e3a 100644 --- a/src/abstract_tree/type_defintion.rs +++ b/src/abstract_tree/type_defintion.rs @@ -67,7 +67,7 @@ pub enum Type { } impl Type { - pub fn check(&self, other: &Type, context: &Map, node: Node, source: &str) -> Result<()> { + pub fn check(&self, other: &Type) -> Result<()> { match (self, other) { (Type::Any, _) | (_, Type::Any) @@ -83,7 +83,7 @@ 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, context, node, source) + self_item_type.check(&other_item_type) } ( Type::Function { @@ -100,18 +100,16 @@ impl Type { .zip(other_parameter_types.iter()); for (self_parameter_type, other_parameter_type) in parameter_type_pairs { - self_parameter_type.check(&other_parameter_type, context, node, source)?; + self_parameter_type.check(&other_parameter_type)?; } - self_return_type.check(other_return_type, context, node, source)?; + self_return_type.check(other_return_type)?; Ok(()) } _ => Err(Error::TypeCheck { expected: self.clone(), actual: other.clone(), - location: node.start_position(), - source: source[node.byte_range()].to_string(), }), } } diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index e459274..906b1b0 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -132,7 +132,7 @@ impl AbstractTree for ValueNode { for (key, statement) in key_statement_pairs { let value = statement.run(source, context)?; - let value_type = value.r#type(context)?; + let value_type = value.r#type(); variables.insert(key.clone(), (value, value_type)); } diff --git a/src/built_in_functions/type.rs b/src/built_in_functions/type.rs index 4ac9582..388e679 100644 --- a/src/built_in_functions/type.rs +++ b/src/built_in_functions/type.rs @@ -7,11 +7,11 @@ impl BuiltInFunction for TypeFunction { "type" } - fn run(&self, arguments: &[Value], context: &Map) -> Result { + fn run(&self, arguments: &[Value], _context: &Map) -> Result { Error::expect_argument_amount(self, 1, arguments.len())?; if arguments.len() == 1 { - let type_definition = arguments.first().unwrap().r#type(context)?; + let type_definition = arguments.first().unwrap().r#type(); let type_text = type_definition.to_string(); let text_without_brackets = &type_text[1..type_text.len() - 1]; @@ -20,7 +20,7 @@ impl BuiltInFunction for TypeFunction { let mut answers = Vec::new(); for value in arguments { - let type_definition = value.r#type(context)?; + let type_definition = value.r#type(); let type_text = type_definition.to_string(); let text_without_brackets = &type_text[1..type_text.len() - 1]; let text_as_value = Value::String(text_without_brackets.to_string()); diff --git a/src/error.rs b/src/error.rs index dca82bb..26f07f8 100644 --- a/src/error.rs +++ b/src/error.rs @@ -13,6 +13,12 @@ pub type Result = std::result::Result; #[derive(Clone, PartialEq)] pub enum Error { + WithContext { + error: Box, + location: Point, + source: String, + }, + UnexpectedSyntaxNode { expected: &'static str, actual: &'static str, @@ -23,13 +29,6 @@ pub enum Error { TypeCheck { expected: Type, actual: Type, - location: Point, - source: String, - }, - - RuntimeTypeCheck { - expected: Type, - actual: Type, }, /// The 'assert' macro did not resolve successfully. @@ -148,6 +147,14 @@ pub enum Error { } impl Error { + pub fn with_context(self, location: Point, source: String) -> Self { + Error::WithContext { + error: Box::new(self), + location, + source, + } + } + pub fn expect_syntax_node(source: &str, expected: &'static str, actual: Node) -> Result<()> { if expected == actual.kind() { Ok(()) @@ -368,19 +375,15 @@ impl fmt::Display for Error { Syntax { source, location } => { write!(f, "Syntax error at {location}, this is not valid: {source}") } - TypeCheck { - expected, - actual, + TypeCheck { expected, actual } => write!( + f, + "Type check error. Expected type {expected} but got type {actual}." + ), + WithContext { + error, location, source, - } => write!( - f, - "Type check error at {location}. Expected type {expected} but got type {actual}: {source}." - ), - RuntimeTypeCheck { expected, actual } => write!( - f, - "Type check error. Expected type {expected} but got value with type {actual}." - ), + } => write!(f, "{error} Occured at {location}: \"{source}\""), } } } diff --git a/src/main.rs b/src/main.rs index 3201348..75fb151 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ use tree_sitter::Parser as TSParser; use std::{borrow::Cow, fs::read_to_string}; -use dust_lang::{evaluate_with_context, language, Interpreter, Map, Value}; +use dust_lang::{evaluate_with_context, language, Interpreter, Map, Type, Value}; /// Command-line arguments to be parsed. #[derive(Parser, Debug)] @@ -63,16 +63,16 @@ fn main() { context .variables_mut() .unwrap() - .insert("input".to_string(), Value::String(input)); + .insert("input".to_string(), (Value::String(input), Type::String)); } if let Some(path) = args.input_path { let file_contents = read_to_string(path).unwrap(); - context - .variables_mut() - .unwrap() - .insert("input".to_string(), Value::String(file_contents)); + context.variables_mut().unwrap().insert( + "input".to_string(), + (Value::String(file_contents), Type::String), + ); } let mut parser = TSParser::new(); diff --git a/src/value/function.rs b/src/value/function.rs index 63cc4ab..0aebc08 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -76,7 +76,7 @@ impl Function { for ((identifier, argument_type), expression) in parameter_argument_pairs { let value = expression.run(source, context)?; - let value_type = value.r#type(context)?; + let value_type = value.r#type(); match argument_type { Type::Any => {} diff --git a/src/value/map.rs b/src/value/map.rs index 1d71bb0..8dd8d68 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -41,6 +41,20 @@ impl Map { Ok(self.variables.read()?) } + pub fn set(&self, key: String, value: Value) -> Result> { + let value_type = value.r#type(); + let previous = self + .variables + .write()? + .insert(key, (value, value_type.clone())); + + if let Some((_previous_value, previous_type)) = previous.clone() { + previous_type.check(&value_type)?; + } + + Ok(previous) + } + pub fn variables_mut(&self) -> Result>> { Ok(self.variables.write()?) } diff --git a/src/value/mod.rs b/src/value/mod.rs index dc6cc98..cea810e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -42,17 +42,17 @@ pub enum Value { } impl Value { - pub fn r#type(&self, context: &Map) -> Result { + pub fn r#type(&self) -> Type { let r#type = match self { Value::List(list) => { let mut previous_type = None; for value in list.items().iter() { - let value_type = value.r#type(context)?; + let value_type = value.r#type(); if let Some(previous) = &previous_type { if &value_type != previous { - return Ok(Type::List(Box::new(Type::Any))); + return Type::List(Box::new(Type::Any)); } } @@ -74,7 +74,7 @@ impl Value { Value::Empty => Type::Empty, }; - Ok(r#type) + r#type } pub fn is_string(&self) -> bool {