From ce4d366bab29a3010ba04af8232dcaf5509a0602 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 13 Dec 2023 15:47:41 -0500 Subject: [PATCH] Implement type checking for functions and indexes --- examples/async.ds | 2 +- examples/clue_solver.ds | 8 +- examples/yield.ds | 2 +- src/abstract_tree/assignment.rs | 25 +-- src/abstract_tree/for.rs | 8 +- src/abstract_tree/function_call.rs | 37 +++-- src/abstract_tree/identifier.rs | 4 +- src/abstract_tree/index.rs | 7 +- src/abstract_tree/index_assignment.rs | 5 +- src/abstract_tree/match.rs | 6 +- src/abstract_tree/mod.rs | 4 +- src/abstract_tree/type_definition.rs | 18 ++- src/abstract_tree/type_defintion.rs | 217 -------------------------- src/abstract_tree/use.rs | 4 +- src/abstract_tree/value_node.rs | 17 +- src/built_in_functions/fs.rs | 24 +-- src/evaluate.rs | 10 +- src/main.rs | 14 +- src/value/function.rs | 4 +- src/value/map.rs | 6 +- src/value/mod.rs | 5 +- 21 files changed, 94 insertions(+), 333 deletions(-) delete mode 100644 src/abstract_tree/type_defintion.rs diff --git a/examples/async.ds b/examples/async.ds index 9db08d0..e22a352 100644 --- a/examples/async.ds +++ b/examples/async.ds @@ -1,4 +1,4 @@ -create_random_numbers <(int)> = fn |count| { +create_random_numbers = (fn count ) { numbers = [] while (length numbers) < count { diff --git a/examples/clue_solver.ds b/examples/clue_solver.ds index 51b7434..1d18d85 100644 --- a/examples/clue_solver.ds +++ b/examples/clue_solver.ds @@ -4,26 +4,26 @@ all_cards = { weapons = ['Rope' 'Lead_Pipe' 'Knife'] } -is_ready_to_solve <(map) -> bool> = fn |cards| { +is_ready_to_solve = (fn cards ) { ((length cards:suspects) == 1) && ((length cards:rooms) == 1) && ((length cards:weapons) == 1) } -take_turn <(map str str) -> map> = fn |cards opponent_card current_room| { +take_turn = (fn cards , opponent_card , current_room ) { cards = (remove_card opponent_card cards) cards = (make_guess current_room cards) cards } -remove_card <(map str) -> map> = fn |cards opponent_card| { +remove_card = (fn cards , opponent_card ) { cards:rooms -= opponent_card cards:suspects -= opponent_card cards:weapons -= opponent_card cards } -make_guess <(map str)> = fn |cards current_room| { +make_guess = (fn cards current_room ) { if (is_ready_to_solve cards) { (output 'It was ' + cards:suspects:0 diff --git a/examples/yield.ds b/examples/yield.ds index a69fec5..2105216 100644 --- a/examples/yield.ds +++ b/examples/yield.ds @@ -1,6 +1,6 @@ 1 -> (output) -add_one <([int]) -> [int]> = fn |numbers| { +add_one = (fn numbers <[int]>) <[int]> { new_numbers = [] for number in numbers { diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index f9e0c0f..4899933 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -57,9 +57,14 @@ 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 { - let statement_type = statement.expected_type(context)?; + context.set( + identifier.inner().clone(), + Value::Empty, + Some(type_definition.inner().clone()), + )?; match operator { AssignmentOperator::Equal => { @@ -78,22 +83,6 @@ impl AbstractTree for Assignment { } AssignmentOperator::MinusEqual => todo!(), } - } else if let Some((_previous_value, previous_type)) = - context.variables()?.get(identifier.inner()) - { - let statement_type = statement.expected_type(context)?; - let type_check = if let Type::List(item_type) = previous_type { - item_type.check(&statement_type) - } else { - previous_type.check(&statement_type) - }; - - if let Err(error) = type_check { - return Err(error.with_context( - statement_node.start_position(), - source[statement_node.byte_range()].to_string(), - )); - } } Ok(Assignment { @@ -129,6 +118,8 @@ impl AbstractTree for Assignment { }; if let Some(type_defintion) = &self.type_definition { + type_defintion.inner().check(&new_value.r#type())?; + context.set(key.clone(), new_value, Some(type_defintion.inner().clone()))?; } else { context.set(key.clone(), new_value, None)?; diff --git a/src/abstract_tree/for.rs b/src/abstract_tree/for.rs index 6421f0a..ab302d4 100644 --- a/src/abstract_tree/for.rs +++ b/src/abstract_tree/for.rs @@ -57,9 +57,7 @@ impl AbstractTree for For { values.par_iter().try_for_each(|value| { let mut iter_context = Map::clone_from(context)?; - iter_context - .variables_mut()? - .insert(key.clone(), (value.clone(), value.r#type())); + iter_context.set(key.clone(), value.clone(), None)?; self.block.run(source, &mut iter_context).map(|_value| ()) })?; @@ -67,9 +65,7 @@ impl AbstractTree for For { let loop_context = Map::clone_from(context)?; for value in values.iter() { - loop_context - .variables_mut()? - .insert(key.clone(), (value.clone(), value.r#type())); + loop_context.set(key.clone(), value.clone(), None)?; 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 717f7ee..3b7e1f0 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -26,6 +26,7 @@ impl AbstractTree for FunctionCall { let expression_node = node.child(1).unwrap(); let function_expression = Expression::from_syntax_node(source, expression_node, context)?; + let function_type = function_expression.expected_type(context)?; let mut arguments = Vec::new(); @@ -34,27 +35,31 @@ 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, + return_type: _, + } = &function_type + { + let expected_type = parameter_types.get(argument_index).unwrap(); + + println!("{expected_type} {expression_type}"); + + expected_type + .check(&expression_type) + .map_err(|error| Error::WithContext { + error: Box::new(error), + location: child.start_position(), + source: source[child.byte_range()].to_string(), + })?; + } arguments.push(expression); } } - let function_type = function_expression.expected_type(context)?; - - if let Type::Function { - parameter_types, - return_type: _, - } = function_type - { - let argument_type_pairs = arguments.iter().zip(parameter_types.iter()); - - for (argument, r#type) in argument_type_pairs { - let argument_type = argument.expected_type(context)?; - - r#type.check(&argument_type)?; - } - } - Ok(FunctionCall { function_expression, arguments, diff --git a/src/abstract_tree/identifier.rs b/src/abstract_tree/identifier.rs index 6f3d5dd..f09ddc0 100644 --- a/src/abstract_tree/identifier.rs +++ b/src/abstract_tree/identifier.rs @@ -47,7 +47,9 @@ impl AbstractTree for Identifier { } else { for built_in_function in BUILT_IN_FUNCTIONS { if self.0 == built_in_function.name() { - return Ok(built_in_function.r#type()); + if let Type::Function { return_type, .. } = built_in_function.r#type() { + return Ok(*return_type); + } } } diff --git a/src/abstract_tree/index.rs b/src/abstract_tree/index.rs index 6fb1a4e..d4ef954 100644 --- a/src/abstract_tree/index.rs +++ b/src/abstract_tree/index.rs @@ -88,7 +88,12 @@ impl AbstractTree for Index { } fn expected_type(&self, context: &Map) -> Result { - self.collection.expected_type(context) + match self.collection.expected_type(context)? { + Type::List(item_type) => Ok(*item_type.clone()), + Type::Map => Ok(Type::Any), + Type::Empty => Ok(Type::Empty), + _ => todo!(), + } } } diff --git a/src/abstract_tree/index_assignment.rs b/src/abstract_tree/index_assignment.rs index 4830ac3..e1c03ff 100644 --- a/src/abstract_tree/index_assignment.rs +++ b/src/abstract_tree/index_assignment.rs @@ -85,11 +85,8 @@ impl AbstractTree for IndexAssignment { } AssignmentOperator::Equal => value, }; - let new_value_type = new_value.r#type(); - index_context - .variables_mut()? - .insert(index_key.clone(), (new_value, new_value_type)); + index_context.set(index_key.clone(), new_value, None)?; Ok(Value::Empty) } diff --git a/src/abstract_tree/match.rs b/src/abstract_tree/match.rs index 454be4c..a82ae46 100644 --- a/src/abstract_tree/match.rs +++ b/src/abstract_tree/match.rs @@ -76,8 +76,10 @@ impl AbstractTree for Match { } } - fn expected_type(&self, _context: &Map) -> Result { - todo!() + fn expected_type(&self, context: &Map) -> Result { + let (_, first_statement) = self.options.first().unwrap(); + + first_statement.expected_type(context) } } diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index c2154f2..c3dbfd2 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -19,7 +19,7 @@ pub mod logic; pub mod r#match; pub mod math; pub mod statement; -pub mod type_defintion; +pub mod type_definition; pub mod r#use; pub mod value_node; pub mod r#while; @@ -28,7 +28,7 @@ pub mod r#yield; pub use { assignment::*, block::*, expression::*, function_call::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, logic::*, math::*, r#for::*, r#match::*, r#use::*, - r#while::*, r#yield::*, statement::*, type_defintion::*, value_node::*, + r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*, }; use tree_sitter::Node; diff --git a/src/abstract_tree/type_definition.rs b/src/abstract_tree/type_definition.rs index 8957e3a..66bdf25 100644 --- a/src/abstract_tree/type_definition.rs +++ b/src/abstract_tree/type_definition.rs @@ -100,10 +100,24 @@ 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)?; + let check = self_parameter_type.check(&other_parameter_type); + + if let Err(Error::TypeCheck { .. }) = check { + return Err(Error::TypeCheck { + expected: self.clone(), + actual: other.clone(), + }); + } } - self_return_type.check(other_return_type)?; + let check = self_return_type.check(other_return_type); + + if let Err(Error::TypeCheck { .. }) = check { + return Err(Error::TypeCheck { + expected: self.clone(), + actual: other.clone(), + }); + } Ok(()) } diff --git a/src/abstract_tree/type_defintion.rs b/src/abstract_tree/type_defintion.rs deleted file mode 100644 index 8957e3a..0000000 --- a/src/abstract_tree/type_defintion.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use serde::{Deserialize, Serialize}; -use tree_sitter::Node; - -use crate::{AbstractTree, Error, Map, Result, Value}; - -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] -pub struct TypeDefinition { - r#type: Type, -} - -impl TypeDefinition { - pub fn new(r#type: Type) -> Self { - Self { r#type } - } - - pub fn inner(&self) -> &Type { - &self.r#type - } - - pub fn take_inner(self) -> Type { - self.r#type - } -} - -impl AbstractTree for TypeDefinition { - fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result { - Error::expect_syntax_node(source, "type_definition", node)?; - - let type_node = node.child(1).unwrap(); - let r#type = Type::from_syntax_node(source, type_node, context)?; - - Ok(TypeDefinition { r#type }) - } - - fn run(&self, source: &str, context: &Map) -> Result { - self.r#type.run(source, context) - } - - fn expected_type(&self, context: &Map) -> Result { - self.r#type.expected_type(context) - } -} - -impl Display for TypeDefinition { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "<{}>", self.r#type) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] -pub enum Type { - Any, - Boolean, - Empty, - Float, - Function { - parameter_types: Vec, - return_type: Box, - }, - Integer, - List(Box), - Map, - Number, - String, -} - -impl Type { - pub fn check(&self, other: &Type) -> Result<()> { - match (self, other) { - (Type::Any, _) - | (_, Type::Any) - | (Type::Boolean, Type::Boolean) - | (Type::Empty, Type::Empty) - | (Type::Float, Type::Float) - | (Type::Integer, Type::Integer) - | (Type::Map, Type::Map) - | (Type::Number, Type::Number) - | (Type::Number, Type::Integer) - | (Type::Number, Type::Float) - | (Type::Integer, Type::Number) - | (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) - } - ( - Type::Function { - parameter_types: self_parameter_types, - return_type: self_return_type, - }, - Type::Function { - parameter_types: other_parameter_types, - return_type: other_return_type, - }, - ) => { - let parameter_type_pairs = self_parameter_types - .iter() - .zip(other_parameter_types.iter()); - - for (self_parameter_type, other_parameter_type) in parameter_type_pairs { - self_parameter_type.check(&other_parameter_type)?; - } - - self_return_type.check(other_return_type)?; - - Ok(()) - } - _ => Err(Error::TypeCheck { - expected: self.clone(), - actual: other.clone(), - }), - } - } -} - -impl AbstractTree for Type { - fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result { - Error::expect_syntax_node(source, "type", node)?; - - let type_node = node.child(0).unwrap(); - - let r#type = match type_node.kind() { - "[" => { - let item_type_node = node.child(1).unwrap(); - let item_type = Type::from_syntax_node(source, item_type_node, context)?; - - Type::List(Box::new(item_type)) - } - "any" => Type::Any, - "bool" => Type::Boolean, - "float" => Type::Float, - "(" => { - let child_count = node.child_count(); - let mut parameter_types = Vec::new(); - - for index in 1..child_count - 2 { - let child = node.child(index).unwrap(); - - if child.is_named() { - let parameter_type = Type::from_syntax_node(source, child, context)?; - - parameter_types.push(parameter_type); - } - } - - let final_node = node.child(child_count - 1).unwrap(); - let return_type = if final_node.is_named() { - Type::from_syntax_node(source, final_node, context)? - } else { - Type::Empty - }; - - Type::Function { - parameter_types, - return_type: Box::new(return_type), - } - } - "int" => Type::Integer, - "map" => Type::Map, - "num" => Type::Number, - "str" => Type::String, - _ => { - return Err(Error::UnexpectedSyntaxNode { - expected: "any, bool, float, fn, int, list, map, num or str", - actual: type_node.kind(), - location: type_node.start_position(), - relevant_source: source[type_node.byte_range()].to_string(), - }) - } - }; - - Ok(r#type) - } - - fn run(&self, _source: &str, _context: &Map) -> Result { - Ok(Value::Empty) - } - - fn expected_type(&self, _context: &Map) -> Result { - Ok(Type::Empty) - } -} - -impl Display for Type { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Type::Any => write!(f, "any"), - Type::Boolean => write!(f, "bool"), - Type::Empty => write!(f, "empty"), - Type::Float => write!(f, "float"), - Type::Function { - parameter_types, - return_type, - } => { - write!(f, "(")?; - - for parameter_type in parameter_types { - write!(f, "{parameter_type}")?; - - if parameter_type != parameter_types.last().unwrap() { - write!(f, " ")?; - } - } - - write!(f, ")")?; - write!(f, " -> {return_type}") - } - Type::Integer => write!(f, "int"), - Type::List(item_type) => write!(f, "[{item_type}]"), - Type::Map => write!(f, "map"), - Type::Number => write!(f, "num"), - Type::String => write!(f, "str"), - } - } -} diff --git a/src/abstract_tree/use.rs b/src/abstract_tree/use.rs index a022174..a84b935 100644 --- a/src/abstract_tree/use.rs +++ b/src/abstract_tree/use.rs @@ -31,8 +31,8 @@ impl AbstractTree for Use { evaluate_with_context(&file_contents, &mut file_context)?; - for (key, value) in file_context.variables()?.iter() { - context.variables_mut()?.insert(key.clone(), value.clone()); + for (key, (value, r#type)) in file_context.variables()?.iter() { + context.set(key.clone(), value.clone(), Some(r#type.clone()))?; } Ok(Value::Map(file_context)) diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index 615cb40..a3c9141 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -145,13 +145,10 @@ impl AbstractTree for ValueNode { let map = Map::new(); { - let mut variables = map.variables_mut()?; - for (key, statement) in key_statement_pairs { let value = statement.run(source, context)?; - let value_type = value.r#type(); - variables.insert(key.clone(), (value, value_type)); + map.set(key.clone(), value, None)?; } } @@ -252,15 +249,9 @@ mod tests { fn evaluate_map() { let map = Map::new(); - { - let mut variables = map.variables_mut().unwrap(); - - variables.insert("x".to_string(), (Value::Integer(1), Type::Integer)); - variables.insert( - "foo".to_string(), - (Value::String("bar".to_string()), Type::String), - ); - } + map.set("x".to_string(), Value::Integer(1), None).unwrap(); + map.set("foo".to_string(), Value::String("bar".to_string()), None) + .unwrap(); assert_eq!(evaluate("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map))); } diff --git a/src/built_in_functions/fs.rs b/src/built_in_functions/fs.rs index 35b95ba..9aad9e5 100644 --- a/src/built_in_functions/fs.rs +++ b/src/built_in_functions/fs.rs @@ -24,24 +24,14 @@ impl BuiltInFunction for Read { let entry = entry?; let file_data = Map::new(); - { - let mut file_data_variables = file_data.variables_mut()?; - let name = entry.file_name().to_string_lossy().to_string(); - let metadata = entry.metadata()?; - let created = metadata.created()?.elapsed()?.as_secs() as i64; - let modified = metadata.modified()?.elapsed()?.as_secs() as i64; + let name = entry.file_name().to_string_lossy().to_string(); + let metadata = entry.metadata()?; + let created = metadata.created()?.elapsed()?.as_secs() as i64; + let modified = metadata.modified()?.elapsed()?.as_secs() as i64; - file_data_variables - .insert("name".to_string(), (Value::String(name), Type::String)); - file_data_variables.insert( - "created".to_string(), - (Value::Integer(created), Type::Integer), - ); - file_data_variables.insert( - "modified".to_string(), - (Value::Integer(modified), Type::Integer), - ); - } + file_data.set("name".to_string(), Value::String(name), None)?; + file_data.set("created".to_string(), Value::Integer(created), None)?; + file_data.set("modified".to_string(), Value::Integer(modified), None)?; files.items_mut().push(Value::Map(file_data)); } diff --git a/src/evaluate.rs b/src/evaluate.rs index d3280e6..9c9c0a5 100644 --- a/src/evaluate.rs +++ b/src/evaluate.rs @@ -31,13 +31,9 @@ pub fn evaluate(source: &str) -> Result { /// # use dust_lang::*; /// let mut context = Map::new(); /// -/// { -/// let mut variables = context.variables_mut().unwrap(); -/// -/// variables.insert("one".into(), (1.into(), Type::Integer)); -/// variables.insert("two".into(), (2.into(), Type::Integer)); -/// variables.insert("three".into(), (3.into(), Type::Integer)); -/// } +/// context.set("one".into(), 1.into(), None); +/// context.set("two".into(), 2.into(), None); +/// context.set("three".into(), 3.into(), None); /// /// let dust_code = "four = 4 one + two + three + four"; /// diff --git a/src/main.rs b/src/main.rs index 75fb151..42d4dc1 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, Type, Value}; +use dust_lang::{evaluate_with_context, language, Interpreter, Map, Value}; /// Command-line arguments to be parsed. #[derive(Parser, Debug)] @@ -61,18 +61,16 @@ fn main() { if let Some(input) = args.input { context - .variables_mut() - .unwrap() - .insert("input".to_string(), (Value::String(input), Type::String)); + .set("input".to_string(), Value::String(input), None) + .unwrap(); } 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), Type::String), - ); + context + .set("input".to_string(), Value::String(file_contents), None) + .unwrap(); } let mut parser = TSParser::new(); diff --git a/src/value/function.rs b/src/value/function.rs index ee6b759..74b77d4 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -82,9 +82,7 @@ impl Function { let key = identifier.inner().clone(); - function_context - .variables_mut()? - .insert(key, (value, value_type)); + function_context.set(key, value, Some(value_type))?; } let return_value = self.body.run(source, &function_context)?; diff --git a/src/value/map.rs b/src/value/map.rs index f6e1de8..9aa7519 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -3,7 +3,7 @@ use std::{ cmp::Ordering, collections::BTreeMap, fmt::{self, Display, Formatter}, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{Arc, RwLock, RwLockReadGuard}, }; use crate::{value::Value, Result, Type}; @@ -59,10 +59,6 @@ impl Map { Ok(previous) } - - pub fn variables_mut(&self) -> Result>> { - Ok(self.variables.write()?) - } } impl Default for Map { diff --git a/src/value/mod.rs b/src/value/mod.rs index f37c747..fdfc435 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -781,11 +781,8 @@ impl<'de> Visitor<'de> for ValueVisitor { let map = Map::new(); { - let mut variables = map.variables_mut().unwrap(); - while let Some((key, value)) = access.next_entry::()? { - let r#type = value.r#type(); - variables.insert(key, (value, r#type)); + map.set(key, value, None).unwrap(); } }