From 3dc78a7066b38a1d32d4bb501d2d82e0992beb21 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 30 Nov 2023 02:09:55 -0500 Subject: [PATCH] Implement runtime type checking --- src/abstract_tree/assignment.rs | 32 ++++++++++++++++----------- src/abstract_tree/function_call.rs | 2 +- src/abstract_tree/type_defintion.rs | 6 ++++- src/abstract_tree/value_node.rs | 4 ++-- src/built_in_functions/fs.rs | 6 ++--- src/built_in_functions/mod.rs | 14 ++++++++---- src/built_in_functions/output.rs | 4 ++-- src/built_in_functions/type.rs | 34 +++++++++++++++++++++++++++++ src/error.rs | 4 ++-- src/value/mod.rs | 8 +++++-- 10 files changed, 84 insertions(+), 30 deletions(-) create mode 100644 src/built_in_functions/type.rs diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index c93542b..8ef6c03 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -56,12 +56,6 @@ impl AbstractTree for Assignment { let statement_node = node.child_by_field_name("statement").unwrap(); let statement = Statement::from_syntax_node(source, statement_node, context)?; - if let Some(type_defintion) = &type_definition { - let statement_type = statement.expected_type(context)?; - - type_defintion.check(&statement_type, context)?; - } - Ok(Assignment { identifier, type_definition, @@ -77,6 +71,16 @@ impl AbstractTree for Assignment { let new_value = match self.operator { AssignmentOperator::PlusEqual => { if let Some(mut previous_value) = context.variables()?.get(key).cloned() { + if let Ok(list) = previous_value.as_list() { + let first_item_type = if let Some(first) = list.items().first() { + first.r#type(context)? + } else { + TypeDefinition::new(Type::Any) + }; + + first_item_type.check(&value.r#type(context)?, context)?; + } + previous_value += value; previous_value } else { @@ -91,15 +95,17 @@ impl AbstractTree for Assignment { return Err(Error::VariableIdentifierNotFound(key.clone())); } } - AssignmentOperator::Equal => value, + AssignmentOperator::Equal => { + if let Some(type_definition) = &self.type_definition { + let new_value_type = value.r#type(context)?; + + type_definition.check(&new_value_type, context)?; + } + + value + } }; - if let Some(type_definition) = &self.type_definition { - let new_value_type = new_value.r#type(context)?; - - type_definition.check(&new_value_type, context)?; - } - context.variables_mut()?.insert(key.clone(), new_value); Ok(Value::Empty) diff --git a/src/abstract_tree/function_call.rs b/src/abstract_tree/function_call.rs index e9246e7..51d14f0 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -59,7 +59,7 @@ impl AbstractTree for FunctionCall { arguments.push(value); } - return built_in_function.run(&arguments); + return built_in_function.run(&arguments, context); } } diff --git a/src/abstract_tree/type_defintion.rs b/src/abstract_tree/type_defintion.rs index 5b8619f..7278f5e 100644 --- a/src/abstract_tree/type_defintion.rs +++ b/src/abstract_tree/type_defintion.rs @@ -15,10 +15,14 @@ impl TypeDefinition { Self { r#type } } - pub fn r#type(&self) -> &Type { + pub fn inner(&self) -> &Type { &self.r#type } + pub fn take_inner(self) -> Type { + self.r#type + } + pub fn check(&self, other: &TypeDefinition, context: &Map) -> Result<()> { match (&self.r#type, &other.r#type) { (Type::Any, _) diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index 25ab586..bbb0fe6 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -203,9 +203,9 @@ impl AbstractTree for ValueNode { } if let Some(previous) = previous_type { - previous + TypeDefinition::new(Type::List(Box::new(previous.take_inner()))) } else { - TypeDefinition::new(Type::Any) + TypeDefinition::new(Type::List(Box::new(Type::Any))) } } ValueNode::Empty => TypeDefinition::new(Type::Any), diff --git a/src/built_in_functions/fs.rs b/src/built_in_functions/fs.rs index d5f393f..34b2d9a 100644 --- a/src/built_in_functions/fs.rs +++ b/src/built_in_functions/fs.rs @@ -13,7 +13,7 @@ impl BuiltInFunction for Read { "read" } - fn run(&self, arguments: &[Value]) -> Result { + fn run(&self, arguments: &[Value], _context: &Map) -> Result { let path_string = arguments.first().unwrap_or_default().as_string()?; let path = PathBuf::from(path_string); @@ -55,7 +55,7 @@ impl BuiltInFunction for Write { "write" } - fn run(&self, arguments: &[Value]) -> Result { + fn run(&self, arguments: &[Value], _context: &Map) -> Result { let file_content = arguments.first().unwrap_or_default().as_string()?; let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?; @@ -72,7 +72,7 @@ impl BuiltInFunction for Append { "append" } - fn run(&self, arguments: &[Value]) -> Result { + fn run(&self, arguments: &[Value], _context: &Map) -> Result { let file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?; let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?; diff --git a/src/built_in_functions/mod.rs b/src/built_in_functions/mod.rs index f3112d1..28b2a31 100644 --- a/src/built_in_functions/mod.rs +++ b/src/built_in_functions/mod.rs @@ -1,12 +1,18 @@ -use crate::{Result, Value}; +use crate::{Map, Result, Value}; mod fs; mod output; +mod r#type; -pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 4] = - [&output::Output, &fs::Read, &fs::Write, &fs::Append]; +pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 5] = [ + &fs::Read, + &fs::Write, + &fs::Append, + &output::Output, + &r#type::Type, +]; pub trait BuiltInFunction { fn name(&self) -> &'static str; - fn run(&self, arguments: &[Value]) -> Result; + fn run(&self, arguments: &[Value], context: &Map) -> Result; } diff --git a/src/built_in_functions/output.rs b/src/built_in_functions/output.rs index c0083cd..859edb2 100644 --- a/src/built_in_functions/output.rs +++ b/src/built_in_functions/output.rs @@ -1,4 +1,4 @@ -use crate::{BuiltInFunction, Result, Value}; +use crate::{BuiltInFunction, Map, Result, Value}; pub struct Output; @@ -7,7 +7,7 @@ impl BuiltInFunction for Output { "output" } - fn run(&self, arguments: &[Value]) -> Result { + fn run(&self, arguments: &[Value], _context: &Map) -> Result { for argument in arguments { println!("{argument}"); } diff --git a/src/built_in_functions/type.rs b/src/built_in_functions/type.rs new file mode 100644 index 0000000..f9b3f80 --- /dev/null +++ b/src/built_in_functions/type.rs @@ -0,0 +1,34 @@ +use crate::{BuiltInFunction, Error, List, Map, Result, Value}; + +pub struct Type; + +impl BuiltInFunction for Type { + fn name(&self) -> &'static str { + "type" + } + + fn run(&self, arguments: &[Value], context: &Map) -> Result { + Error::expect_function_argument_amount("type", 1, arguments.len())?; + + if arguments.len() == 1 { + let type_definition = arguments.first().unwrap().r#type(context)?; + let type_text = type_definition.to_string(); + let text_without_brackets = &type_text[1..type_text.len() - 1]; + + Ok(Value::String(text_without_brackets.to_string())) + } else { + let mut answers = Vec::new(); + + for value in arguments { + let type_definition = value.r#type(context)?; + 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()); + + answers.push(text_as_value); + } + + Ok(Value::List(List::with_items(answers))) + } + } +} diff --git a/src/error.rs b/src/error.rs index 7e379f9..4dc7b77 100644 --- a/src/error.rs +++ b/src/error.rs @@ -166,7 +166,7 @@ impl Error { } } - pub fn expect_tool_argument_amount( + pub fn expect_function_argument_amount( tool_name: &'static str, expected: usize, actual: usize, @@ -374,7 +374,7 @@ impl fmt::Display for Error { ), RuntimeTypeCheck { expected, actual } => write!( f, - "Type check error. Expected type {expected} but got value {actual}." + "Type check error. Expected type {expected} but got value with type {actual}." ), } } diff --git a/src/value/mod.rs b/src/value/mod.rs index 99eba30..9880de6 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -53,14 +53,18 @@ impl Value { if let Some(previous) = &previous_type { if &value_type != previous { - break; + return Ok(TypeDefinition::new(Type::List(Box::new(Type::Any)))); } } previous_type = Some(value_type); } - Type::List(Box::new(Type::Any)) + if let Some(previous) = previous_type { + Type::List(Box::new(previous.take_inner())) + } else { + Type::List(Box::new(Type::Any)) + } } Value::Map(_) => Type::Map, Value::Table(_) => Type::Table,