diff --git a/dust-lang/src/abstract_tree/assignment.rs b/dust-lang/src/abstract_tree/assignment.rs index ea15f3e..91a812a 100644 --- a/dust-lang/src/abstract_tree/assignment.rs +++ b/dust-lang/src/abstract_tree/assignment.rs @@ -161,7 +161,7 @@ impl AbstractNode for Assignment { #[cfg(test)] mod tests { use crate::{ - abstract_tree::{Expression, ValueNode}, + abstract_tree::{Expression, ValueNode, WithPos}, error::TypeConflict, }; diff --git a/dust-lang/src/abstract_tree/block.rs b/dust-lang/src/abstract_tree/block.rs index 43331f0..8ef0b10 100644 --- a/dust-lang/src/abstract_tree/block.rs +++ b/dust-lang/src/abstract_tree/block.rs @@ -51,7 +51,7 @@ impl AbstractNode for Block { #[cfg(test)] mod tests { use crate::{ - abstract_tree::{Expression, ValueNode}, + abstract_tree::{Expression, ValueNode, WithPos}, Value, }; diff --git a/dust-lang/src/abstract_tree/function_call.rs b/dust-lang/src/abstract_tree/function_call.rs index 1e0b1cb..9dd3f60 100644 --- a/dust-lang/src/abstract_tree/function_call.rs +++ b/dust-lang/src/abstract_tree/function_call.rs @@ -1,7 +1,7 @@ use crate::{ context::Context, error::{RuntimeError, ValidationError}, - value::ValueInner, + value::{Function, ParsedFunction, ValueInner}, }; use super::{AbstractNode, Action, Expression, Type, WithPosition}; @@ -32,7 +32,7 @@ impl AbstractNode for FunctionCall { let function_node_type = self.function.node.expected_type(_context)?; if let Type::Function { return_type, .. } = function_node_type { - Ok(*return_type) + Ok(return_type.node) } else { Err(ValidationError::ExpectedFunction { actual: function_node_type, @@ -41,14 +41,31 @@ impl AbstractNode for FunctionCall { } } - fn validate(&self, _context: &Context) -> Result<(), ValidationError> { + fn validate(&self, context: &Context) -> Result<(), ValidationError> { for expression in &self.arguments { - expression.node.validate(_context)?; + expression.node.validate(context)?; } - let function_node_type = self.function.node.expected_type(_context)?; + let function_node_type = self.function.node.expected_type(context)?; + + if let Type::Function { + parameter_types, + return_type: _, + } = function_node_type + { + for (type_parameter, type_argument) in + parameter_types.iter().zip(self.type_arguments.iter()) + { + type_parameter + .node + .check(&type_argument.node) + .map_err(|conflict| ValidationError::TypeCheck { + conflict, + actual_position: type_argument.position, + expected_position: type_parameter.position, + })?; + } - if let Type::Function { .. } = function_node_type { Ok(()) } else { Err(ValidationError::ExpectedFunction { @@ -94,6 +111,21 @@ impl AbstractNode for FunctionCall { let function_context = Context::new(); + if let Function::Parsed(ParsedFunction { + type_parameters, .. + }) = function + { + for (type_parameter, type_argument) in type_parameters + .iter() + .map(|r#type| r#type.node.clone()) + .zip(self.type_arguments.into_iter().map(|r#type| r#type.node)) + { + if let Type::Argument(identifier) = type_parameter { + function_context.set_type(identifier, type_argument)?; + } + } + }; + function_context.inherit_data_from(&context)?; function.clone().call(arguments, function_context) } diff --git a/dust-lang/src/abstract_tree/if_else.rs b/dust-lang/src/abstract_tree/if_else.rs index 901ce79..279104b 100644 --- a/dust-lang/src/abstract_tree/if_else.rs +++ b/dust-lang/src/abstract_tree/if_else.rs @@ -95,7 +95,7 @@ impl AbstractNode for IfElse { #[cfg(test)] mod tests { use crate::{ - abstract_tree::{Statement, ValueNode}, + abstract_tree::{Statement, ValueNode, WithPos}, Value, }; diff --git a/dust-lang/src/abstract_tree/list_index.rs b/dust-lang/src/abstract_tree/list_index.rs index baf9a33..f99bc7b 100644 --- a/dust-lang/src/abstract_tree/list_index.rs +++ b/dust-lang/src/abstract_tree/list_index.rs @@ -90,7 +90,7 @@ impl AbstractNode for ListIndex { let found_item = list.get(index as usize); if let Some(item) = found_item { - Ok(Action::Return(item.clone())) + Ok(Action::Return(item.node.clone())) } else { Ok(Action::None) } diff --git a/dust-lang/src/abstract_tree/logic.rs b/dust-lang/src/abstract_tree/logic.rs index 58a4063..78995e2 100644 --- a/dust-lang/src/abstract_tree/logic.rs +++ b/dust-lang/src/abstract_tree/logic.rs @@ -187,7 +187,7 @@ impl AbstractNode for Logic { #[cfg(test)] mod tests { - use crate::abstract_tree::ValueNode; + use crate::abstract_tree::{ValueNode, WithPos}; use super::*; diff --git a/dust-lang/src/abstract_tree/loop.rs b/dust-lang/src/abstract_tree/loop.rs index 7065dea..a0e36fa 100644 --- a/dust-lang/src/abstract_tree/loop.rs +++ b/dust-lang/src/abstract_tree/loop.rs @@ -70,7 +70,8 @@ impl Ord for Loop { mod tests { use crate::{ abstract_tree::{ - Assignment, AssignmentOperator, Block, Expression, Identifier, IfElse, Logic, ValueNode, + Assignment, AssignmentOperator, Block, Expression, Identifier, IfElse, Logic, + ValueNode, WithPos, }, Value, }; @@ -99,19 +100,19 @@ mod tests { .with_position((0, 0)), Statement::Loop(Loop::new(vec![Statement::IfElse(IfElse::new( Expression::Logic(Box::new(Logic::Greater( - Expression::Identifier(Identifier::new("i")).with_position((10, 11)), - Expression::Value(ValueNode::Integer(2)).with_position((14, 15)), + Expression::Identifier(Identifier::new("i")).with_position((0, 0)), + Expression::Value(ValueNode::Integer(2)).with_position((0, 0)), ))) - .with_position((10, 15)), - Block::new(vec![Statement::Break.with_position((18, 24))]), + .with_position((0, 0)), + Block::new(vec![Statement::Break.with_position((0, 0))]), Some(Block::new(vec![Statement::Assignment(Assignment::new( Identifier::new("i").with_position((0, 0)), None, AssignmentOperator::AddAssign, Statement::Expression(Expression::Value(ValueNode::Integer(1))) - .with_position((38, 39)), + .with_position((0, 0)), )) - .with_position((33, 39))])), + .with_position((0, 0))])), )) .with_position((0, 0))])) .with_position((0, 0)), diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index 95c62fc..3e6c2a7 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -52,6 +52,17 @@ pub struct WithPosition { pub position: SourcePosition, } +pub trait WithPos: Sized { + fn with_position>(self, span: T) -> WithPosition { + WithPosition { + node: self, + position: span.into(), + } + } +} + +impl WithPos for T {} + #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct SourcePosition(pub usize, pub usize); @@ -164,11 +175,4 @@ pub trait AbstractNode: Sized { fn expected_type(&self, context: &Context) -> Result; fn validate(&self, context: &Context) -> Result<(), ValidationError>; fn run(self, context: &Context) -> Result; - - fn with_position>(self, span: T) -> WithPosition { - WithPosition { - node: self, - position: span.into(), - } - } } diff --git a/dust-lang/src/abstract_tree/type.rs b/dust-lang/src/abstract_tree/type.rs index 3dceec9..22f2b02 100644 --- a/dust-lang/src/abstract_tree/type.rs +++ b/dust-lang/src/abstract_tree/type.rs @@ -17,13 +17,13 @@ pub enum Type { Boolean, Float, Function { - parameter_types: Vec, - return_type: Box, + parameter_types: Vec>, + return_type: Box>, }, Integer, List, - ListOf(Box), - ListExact(Vec), + ListOf(Box>), + ListExact(Vec>), Map, None, Range, @@ -52,27 +52,27 @@ impl Type { | (Type::Range, Type::Range) | (Type::String, Type::String) => return Ok(()), (Type::ListOf(left), Type::ListOf(right)) => { - if let Ok(()) = left.check(right) { + if let Ok(()) = left.node.check(&right.node) { return Ok(()); } } (Type::ListOf(list_of), Type::ListExact(list_exact)) => { for r#type in list_exact { - list_of.check(r#type)?; + list_of.node.check(&r#type.node)?; } return Ok(()); } (Type::ListExact(list_exact), Type::ListOf(list_of)) => { for r#type in list_exact { - r#type.check(&list_of)?; + r#type.node.check(&list_of.node)?; } return Ok(()); } (Type::ListExact(left), Type::ListExact(right)) => { for (left, right) in left.iter().zip(right.iter()) { - left.check(right)?; + left.node.check(&right.node)?; } return Ok(()); @@ -149,15 +149,15 @@ impl Display for Type { Type::Float => write!(f, "float"), Type::Integer => write!(f, "int"), Type::List => write!(f, "list"), - Type::ListOf(item_type) => write!(f, "list({item_type})"), + Type::ListOf(item_type) => write!(f, "list({})", item_type.node), Type::ListExact(item_types) => { write!(f, "[")?; for (index, item_type) in item_types.into_iter().enumerate() { if index == item_types.len() - 1 { - write!(f, "{item_type}")?; + write!(f, "{}", item_type.node)?; } else { - write!(f, "{item_type}, ")?; + write!(f, "{}, ", item_type.node)?; } } @@ -174,10 +174,10 @@ impl Display for Type { write!(f, "(")?; for r#type in parameter_types { - write!(f, "{} ", r#type)?; + write!(f, "{} ", r#type.node)?; } - write!(f, ") : {return_type}") + write!(f, ") : {}", return_type.node) } Type::Structure { name, .. } => write!(f, "{name}"), Type::Argument(_) => todo!(), @@ -187,6 +187,8 @@ impl Display for Type { #[cfg(test)] mod tests { + use crate::abstract_tree::WithPos; + use super::*; #[test] @@ -197,12 +199,14 @@ mod tests { assert_eq!(Type::Integer.check(&Type::Integer), Ok(())); assert_eq!(Type::List.check(&Type::List), Ok(())); assert_eq!( - Type::ListOf(Box::new(Type::Integer)).check(&Type::ListOf(Box::new(Type::Integer))), + Type::ListOf(Box::new(Type::Integer.with_position((0, 0)))) + .check(&Type::ListOf(Box::new(Type::Integer.with_position((0, 0))))), Ok(()) ); assert_eq!( - Type::ListExact(vec![Type::Float]).check(&Type::ListExact(vec![Type::Float])), + Type::ListExact(vec![Type::Float.with_position((0, 0))]) + .check(&Type::ListExact(vec![Type::Float.with_position((0, 0))])), Ok(()) ); assert_eq!(Type::Map.check(&Type::Map), Ok(())); @@ -237,8 +241,8 @@ mod tests { Type::Float, Type::Integer, Type::List, - Type::ListOf(Box::new(Type::Boolean)), - Type::ListExact(vec![Type::Integer]), + Type::ListOf(Box::new(Type::Boolean.with_position((0, 0)))), + Type::ListExact(vec![Type::Integer.with_position((0, 0))]), Type::Map, Type::None, Type::Range, @@ -263,8 +267,11 @@ mod tests { #[test] fn check_list_types() { let list = Type::List; - let list_exact = Type::ListExact(vec![Type::Integer, Type::Integer]); - let list_of = Type::ListOf(Box::new(Type::Integer)); + let list_exact = Type::ListExact(vec![ + Type::Integer.with_position((0, 0)), + Type::Integer.with_position((0, 0)), + ]); + let list_of = Type::ListOf(Box::new(Type::Integer.with_position((0, 0)))); assert_eq!(list.check(&list_exact), Ok(())); assert_eq!(list.check(&list_of), Ok(())); diff --git a/dust-lang/src/abstract_tree/value_node.rs b/dust-lang/src/abstract_tree/value_node.rs index c14bf70..985b1fb 100644 --- a/dust-lang/src/abstract_tree/value_node.rs +++ b/dust-lang/src/abstract_tree/value_node.rs @@ -7,7 +7,7 @@ use crate::{ Value, }; -use super::{AbstractNode, Action, Block, Expression, Identifier, Type, WithPosition}; +use super::{AbstractNode, Action, Block, Expression, Identifier, Type, WithPos, WithPosition}; #[derive(Clone, Debug, PartialEq)] pub enum ValueNode { @@ -47,7 +47,12 @@ impl AbstractNode for ValueNode { let mut item_types = Vec::with_capacity(items.len()); for expression in items { - item_types.push(expression.node.expected_type(context)?); + item_types.push( + expression + .node + .expected_type(context)? + .with_position(expression.position), + ); } Type::ListExact(item_types) @@ -61,10 +66,10 @@ impl AbstractNode for ValueNode { .. } => Type::Function { parameter_types: parameters - .into_iter() - .map(|(_, r#type)| r#type.node.clone()) + .iter() + .map(|(_, r#type)| r#type.clone()) .collect(), - return_type: Box::new(return_type.node.clone()), + return_type: Box::new(return_type.clone()), }, ValueNode::Structure { name, @@ -185,7 +190,10 @@ impl AbstractNode for ValueNode { for expression in expression_list { let action = expression.node.run(_context)?; let value = if let Action::Return(value) = action { - value + WithPosition { + node: value, + position: expression.position, + } } else { return Err(RuntimeError::ValidationFailure( ValidationError::InterpreterExpectedReturn(expression.position), diff --git a/dust-lang/src/abstract_tree/while.rs b/dust-lang/src/abstract_tree/while.rs index 2e68e68..057750a 100644 --- a/dust-lang/src/abstract_tree/while.rs +++ b/dust-lang/src/abstract_tree/while.rs @@ -74,7 +74,7 @@ impl AbstractNode for While { #[cfg(test)] mod tests { use crate::abstract_tree::{ - Assignment, AssignmentOperator, Block, Identifier, Logic, ValueNode, + Assignment, AssignmentOperator, Block, Identifier, Logic, ValueNode, WithPos, }; use super::*; diff --git a/dust-lang/src/built_in_functions.rs b/dust-lang/src/built_in_functions.rs index 1d227d2..e7455cc 100644 --- a/dust-lang/src/built_in_functions.rs +++ b/dust-lang/src/built_in_functions.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - abstract_tree::{Action, Type}, + abstract_tree::{Action, Type, WithPos}, context::Context, error::RuntimeError, value::ValueInner, @@ -36,8 +36,8 @@ impl BuiltInFunction { pub fn r#type(&self) -> Type { match self { BuiltInFunction::WriteLine => Type::Function { - parameter_types: vec![Type::String], - return_type: Box::new(Type::None), + parameter_types: vec![Type::String.with_position((0, 0))], + return_type: Box::new(Type::None.with_position((0, 0))), }, _ => { todo!() diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 0500ced..635c814 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -121,8 +121,8 @@ pub fn parser<'src>() -> impl Parser< } }), )) - }) - .map_with(|r#type, state| r#type.with_position(state.span())); + .map_with(|r#type, state| r#type.with_position(state.span())) + }); let type_argument = identifier .clone() @@ -759,7 +759,10 @@ mod tests { parse(&lex("foobar : list(bool) = [true]").unwrap()).unwrap()[0].node, Statement::Assignment(Assignment::new( Identifier::new("foobar").with_position((0, 6)), - Some(Type::ListOf(Box::new(Type::Boolean)).with_position((9, 19))), + Some( + Type::ListOf(Box::new(Type::Boolean.with_position((14, 18)))) + .with_position((9, 19)) + ), AssignmentOperator::Assign, Statement::Expression(Expression::Value(ValueNode::List(vec![Expression::Value( ValueNode::Boolean(true) @@ -776,7 +779,13 @@ mod tests { parse(&lex("foobar : [bool, str] = [true, '42']").unwrap()).unwrap()[0], Statement::Assignment(Assignment::new( Identifier::new("foobar").with_position((0, 6)), - Some(Type::ListExact(vec![Type::Boolean, Type::String]).with_position((9, 20))), + Some( + Type::ListExact(vec![ + Type::Boolean.with_position((10, 14)), + Type::String.with_position((16, 19)) + ]) + .with_position((9, 20)) + ), AssignmentOperator::Assign, Statement::Expression(Expression::Value(ValueNode::List(vec![ Expression::Value(ValueNode::Boolean(true)).with_position((24, 28)), @@ -797,7 +806,7 @@ mod tests { Some( Type::Function { parameter_types: vec![], - return_type: Box::new(Type::Any) + return_type: Box::new(Type::Any.with_position((17, 20))) } .with_position((9, 20)) ), diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index f6f35a7..a6d248a 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -13,7 +13,7 @@ use stanza::{ }; use crate::{ - abstract_tree::{AbstractNode, Action, Block, Identifier, Type, WithPosition}, + abstract_tree::{AbstractNode, Action, Block, Identifier, Type, WithPos, WithPosition}, built_in_functions::BuiltInFunction, context::Context, error::{RuntimeError, ValidationError}, @@ -39,7 +39,7 @@ impl Value { Value(Arc::new(ValueInner::Integer(integer))) } - pub fn list(list: Vec) -> Self { + pub fn list(list: Vec>) -> Self { Value(Arc::new(ValueInner::List(list))) } @@ -63,7 +63,7 @@ impl Value { ) -> Self { Value(Arc::new(ValueInner::Function(Function::Parsed( ParsedFunction { - type_arguments, + type_parameters: type_arguments, parameters, return_type, body, @@ -91,7 +91,7 @@ impl Value { } } - pub fn as_list(&self) -> Option<&Vec> { + pub fn as_list(&self) -> Option<&Vec>> { if let ValueInner::List(list) = self.inner().as_ref() { Some(list) } else { @@ -122,7 +122,7 @@ impl Display for Value { let mut table = create_table(); for value in list { - table = table.with_row([value.to_string()]); + table = table.with_row([value.node.to_string()]); } write!(f, "{}", Console::default().render(&table)) @@ -139,7 +139,7 @@ impl Display for Value { ValueInner::Range(_) => todo!(), ValueInner::String(string) => write!(f, "{string}"), ValueInner::Function(Function::Parsed(ParsedFunction { - type_arguments, + type_parameters: type_arguments, parameters, return_type, body, @@ -202,7 +202,7 @@ pub enum ValueInner { Float(f64), Function(Function), Integer(i64), - List(Vec), + List(Vec>), Map(BTreeMap), Range(Range), String(String), @@ -222,7 +222,7 @@ impl ValueInner { let mut types = Vec::with_capacity(values.len()); for value in values { - types.push(value.r#type(context)?); + types.push(value.node.r#type(context)?.with_position(value.position)); } Type::ListExact(types) @@ -235,9 +235,9 @@ impl ValueInner { parameter_types: parsed_function .parameters .iter() - .map(|(_, r#type)| r#type.node.clone()) + .map(|(_, r#type)| r#type.clone()) .collect(), - return_type: Box::new(parsed_function.return_type.node.clone()), + return_type: Box::new(parsed_function.return_type.clone()), }, Function::BuiltIn(built_in_function) => { built_in_function.clone().as_value().r#type(context)? @@ -343,8 +343,8 @@ impl Function { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct ParsedFunction { - type_arguments: Vec>, - parameters: Vec<(Identifier, WithPosition)>, - return_type: WithPosition, - body: WithPosition, + pub type_parameters: Vec>, + pub parameters: Vec<(Identifier, WithPosition)>, + pub return_type: WithPosition, + pub body: WithPosition, } diff --git a/dust-lang/tests/values.rs b/dust-lang/tests/values.rs index 6f0563d..a13302f 100644 --- a/dust-lang/tests/values.rs +++ b/dust-lang/tests/values.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeMap, rc::Rc}; use dust_lang::{ - abstract_tree::{Identifier, Type}, + abstract_tree::{Identifier, Type, WithPos}, error::{Error, TypeConflict, ValidationError}, *, }; @@ -100,9 +100,9 @@ fn list() { assert_eq!( interpret(Rc::new("test".to_string()), "[1, 2, 'foobar']"), Ok(Some(Value::list(vec![ - Value::integer(1), - Value::integer(2), - Value::string("foobar".to_string()), + Value::integer(1).with_position((1, 2)), + Value::integer(2).with_position((4, 5)), + Value::string("foobar".to_string()).with_position((7, 15)), ]))) ); } diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs index 4ef1547..46def12 100644 --- a/dust-lang/tests/variables.rs +++ b/dust-lang/tests/variables.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use dust_lang::{ - abstract_tree::{AbstractNode, Block, Expression, Identifier, Statement, Type}, + abstract_tree::{Block, Expression, Identifier, Statement, Type, WithPos}, error::{Error, TypeConflict, ValidationError}, *, };