From cddf199156afdf2350c9c246d8318b758c0867f1 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 17 Jun 2024 17:38:24 -0400 Subject: [PATCH] Fix tests; Implement type generics --- .../{value_expression.rs => expression.rs} | 6 +- dust-lang/src/abstract_tree/mod.rs | 4 +- dust-lang/src/abstract_tree/type.rs | 21 ++- dust-lang/src/abstract_tree/value_node.rs | 30 ++-- dust-lang/src/context.rs | 4 + dust-lang/src/error.rs | 10 +- dust-lang/src/lib.rs | 10 +- dust-lang/src/parser.rs | 153 +++++++++--------- std/json.ds | 2 +- 9 files changed, 131 insertions(+), 109 deletions(-) rename dust-lang/src/abstract_tree/{value_expression.rs => expression.rs} (100%) diff --git a/dust-lang/src/abstract_tree/value_expression.rs b/dust-lang/src/abstract_tree/expression.rs similarity index 100% rename from dust-lang/src/abstract_tree/value_expression.rs rename to dust-lang/src/abstract_tree/expression.rs index a43e030..ce714b1 100644 --- a/dust-lang/src/abstract_tree/value_expression.rs +++ b/dust-lang/src/abstract_tree/expression.rs @@ -44,6 +44,9 @@ impl AbstractNode for Expression { fn validate(&self, context: &mut Context, manage_memory: bool) -> Result<(), ValidationError> { match self { Expression::As(r#as) => r#as.node.validate(context, manage_memory), + Expression::BuiltInFunctionCall(built_in_function_call) => { + built_in_function_call.node.validate(context, manage_memory) + } Expression::FunctionCall(function_call) => { function_call.node.validate(context, manage_memory) } @@ -68,9 +71,6 @@ impl AbstractNode for Expression { Expression::Logic(logic) => logic.node.validate(context, manage_memory), Expression::Math(math) => math.node.validate(context, manage_memory), Expression::Value(value_node) => value_node.node.validate(context, manage_memory), - Expression::BuiltInFunctionCall(built_in_function_call) => { - built_in_function_call.node.validate(context, manage_memory) - } } } diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index 8351d06..8d48637 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -3,6 +3,7 @@ pub mod assignment; pub mod async_block; pub mod block; pub mod built_in_function_call; +pub mod expression; pub mod function_call; pub mod if_else; pub mod list_index; @@ -15,7 +16,6 @@ pub mod structure_definition; pub mod r#type; pub mod type_alias; pub mod type_constructor; -pub mod value_expression; pub mod value_node; pub mod r#while; @@ -29,6 +29,7 @@ pub use self::{ async_block::AsyncBlock, block::Block, built_in_function_call::BuiltInFunctionCall, + expression::Expression, function_call::FunctionCall, if_else::IfElse, list_index::ListIndex, @@ -43,7 +44,6 @@ pub use self::{ structure_definition::StructureDefinition, type_alias::TypeAssignment, type_constructor::{FunctionTypeConstructor, ListTypeConstructor, TypeConstructor}, - value_expression::Expression, value_node::ValueNode, }; diff --git a/dust-lang/src/abstract_tree/type.rs b/dust-lang/src/abstract_tree/type.rs index e9dd18c..7301b6d 100644 --- a/dust-lang/src/abstract_tree/type.rs +++ b/dust-lang/src/abstract_tree/type.rs @@ -21,6 +21,7 @@ pub enum Type { value_parameters: Vec<(Identifier, Type)>, return_type: Box, }, + Generic(Option>), Integer, List { length: usize, @@ -49,8 +50,19 @@ impl Type { | (Type::None, Type::None) | (Type::Range, Type::Range) | (Type::String, Type::String) => return Ok(()), + (Type::Generic(left), Type::Generic(right)) => match (left, right) { + (Some(left), Some(right)) => { + if left.check(&right).is_ok() { + return Ok(()); + } + } + (None, None) => { + return Ok(()); + } + _ => {} + }, (Type::ListOf(left), Type::ListOf(right)) => { - if let Ok(()) = left.check(&right) { + if left.check(&right).is_ok() { return Ok(()); } } @@ -195,6 +207,13 @@ impl Display for Type { Type::Any => write!(f, "any"), Type::Boolean => write!(f, "bool"), Type::Float => write!(f, "float"), + Type::Generic(type_option) => { + if let Some(concrete_type) = type_option { + write!(f, "implied to be {concrete_type}") + } else { + todo!() + } + } Type::Integer => write!(f, "int"), Type::List { length, item_type } => write!(f, "[{length}; {}]", item_type), Type::ListOf(item_type) => write!(f, "list({})", item_type), diff --git a/dust-lang/src/abstract_tree/value_node.rs b/dust-lang/src/abstract_tree/value_node.rs index 328af35..b872595 100644 --- a/dust-lang/src/abstract_tree/value_node.rs +++ b/dust-lang/src/abstract_tree/value_node.rs @@ -26,7 +26,7 @@ pub enum ValueNode { name: WithPosition, fields: Vec<(WithPosition, Expression)>, }, - Parsed { + Function { type_parameters: Option>, value_parameters: Vec<(Identifier, TypeConstructor)>, return_type: TypeConstructor, @@ -57,8 +57,8 @@ impl AbstractNode for ValueNode { return Ok(()); } - if let ValueNode::Parsed { - type_parameters: _, + if let ValueNode::Function { + type_parameters, value_parameters, return_type, body, @@ -66,6 +66,12 @@ impl AbstractNode for ValueNode { { let mut function_context = Context::new(Some(&context)); + if let Some(type_parameters) = type_parameters { + for identifier in type_parameters { + function_context.set_type(identifier.clone(), Type::Generic(None))?; + } + } + for (identifier, type_constructor) in value_parameters { let r#type = type_constructor.clone().construct(&function_context)?; @@ -175,18 +181,14 @@ impl AbstractNode for ValueNode { } ValueNode::Range(range) => Value::range(range), ValueNode::String(string) => Value::string(string), - ValueNode::Parsed { + ValueNode::Function { type_parameters, value_parameters: constructors, return_type, body, } => { - let type_parameters = type_parameters.map(|parameter_list| { - parameter_list - .into_iter() - .map(|parameter| parameter) - .collect() - }); + let type_parameters = + type_parameters.map(|parameter_list| parameter_list.into_iter().collect()); let mut value_parameters = Vec::with_capacity(constructors.len()); for (identifier, constructor) in constructors { @@ -263,13 +265,13 @@ impl Ord for ValueNode { (String(left), String(right)) => left.cmp(right), (String(_), _) => Ordering::Greater, ( - Parsed { + Function { type_parameters: left_type_arguments, value_parameters: left_parameters, return_type: left_return, body: left_body, }, - Parsed { + Function { type_parameters: right_type_arguments, value_parameters: right_parameters, return_type: right_return, @@ -296,7 +298,7 @@ impl Ord for ValueNode { parameter_cmp } } - (Parsed { .. }, _) => Ordering::Greater, + (Function { .. }, _) => Ordering::Greater, ( Structure { name: left_name, @@ -337,7 +339,7 @@ impl ExpectedType for ValueNode { ValueNode::Map(_) => Type::Map, ValueNode::Range(_) => Type::Range, ValueNode::String(_) => Type::String, - ValueNode::Parsed { + ValueNode::Function { type_parameters, value_parameters, return_type, diff --git a/dust-lang/src/context.rs b/dust-lang/src/context.rs index c1d1b50..c829a65 100644 --- a/dust-lang/src/context.rs +++ b/dust-lang/src/context.rs @@ -26,6 +26,10 @@ impl<'a> Context<'a> { } } + pub fn create_child<'b>(&'b self) -> Context<'b> { + Context::new(Some(self)) + } + pub fn inner( &self, ) -> Result>, RwLockPoisonError> diff --git a/dust-lang/src/error.rs b/dust-lang/src/error.rs index b392a08..d4a1926 100644 --- a/dust-lang/src/error.rs +++ b/dust-lang/src/error.rs @@ -10,16 +10,16 @@ use crate::{ #[derive(Debug, PartialEq)] pub enum Error { - Parse { - expected: String, - span: (usize, usize), - found: Option, - }, Lex { expected: String, span: (usize, usize), reason: String, }, + Parse { + expected: String, + span: (usize, usize), + found: Option, + }, Runtime { error: RuntimeError, position: SourcePosition, diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index e57b9cc..dd94a2a 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -234,14 +234,10 @@ impl InterpreterError { } => { let description = if expected.is_empty() { "Invalid token.".to_string() - } else { + } else { format!("Expected {expected}.") }; - - let label_message = format!( - "{} is not valid in this position.", - found.unwrap_or_else(|| String::with_capacity(0)) - ); + let found = found.unwrap_or_else(|| "End of input".to_string()); ( Report::build( @@ -252,7 +248,7 @@ impl InterpreterError { .with_message(description) .with_label( Label::new((self.source_id.clone(), span.0..span.1)) - .with_message(label_message) + .with_message(format!("{found} is not valid in this position.")) .with_color(Color::Red), ), None, diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index aa2ef55..ad44b93 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -246,7 +246,7 @@ pub fn parser<'src>( .map_with( |(((type_parameters, value_parameters), return_type), body), state| { Expression::Value( - ValueNode::Parsed { + ValueNode::Function { type_parameters, value_parameters, return_type, @@ -640,6 +640,73 @@ mod tests { use super::*; + // Reuse these tests when structures are reimplemented + // #[test] + // fn structure_instance() { + // assert_eq!( + // parse( + // &lex(" + // Foo { + // bar = 42, + // baz = 'hiya', + // } + // ") + // .unwrap() + // ) + // .unwrap()[0], + // Statement::Expression(Expression::Value( + // ValueNode::Structure { + // name: Identifier::new("Foo").with_position((21, 24)), + // fields: vec![ + // ( + // Identifier::new("bar").with_position((0, 0)), + // Expression::Value(ValueNode::Integer(42).with_position((57, 59))) + // ), + // ( + // Identifier::new("baz").with_position((0, 0)), + // Expression::Value( + // ValueNode::String("hiya".to_string()).with_position((91, 97)) + // ) + // ), + // ] + // } + // .with_position((21, 120)) + // )) + // ) + // } + + // #[test] + // fn structure_definition() { + // assert_eq!( + // parse( + // &lex(" + // struct Foo { + // bar : int, + // baz : str, + // } + // ") + // .unwrap() + // ) + // .unwrap()[0], + // Statement::StructureDefinition( + // StructureDefinition::new( + // Identifier::new("Foo"), + // vec![ + // ( + // Identifier::new("bar"), + // TypeConstructor::Type(Type::Integer.with_position((64, 67))) + // ), + // ( + // Identifier::new("baz"), + // TypeConstructor::Type(Type::String.with_position((99, 102))) + // ), + // ] + // ) + // .with_position((21, 125)) + // ) + // ) + // } + #[test] fn type_alias() { assert_eq!( @@ -743,72 +810,6 @@ mod tests { ) } - #[test] - fn structure_instance() { - assert_eq!( - parse( - &lex(" - Foo { - bar = 42, - baz = 'hiya', - } - ") - .unwrap() - ) - .unwrap()[0], - Statement::Expression(Expression::Value( - ValueNode::Structure { - name: Identifier::new("Foo").with_position((21, 24)), - fields: vec![ - ( - Identifier::new("bar").with_position((0, 0)), - Expression::Value(ValueNode::Integer(42).with_position((57, 59))) - ), - ( - Identifier::new("baz").with_position((0, 0)), - Expression::Value( - ValueNode::String("hiya".to_string()).with_position((91, 97)) - ) - ), - ] - } - .with_position((21, 120)) - )) - ) - } - - #[test] - fn structure_definition() { - assert_eq!( - parse( - &lex(" - struct Foo { - bar : int, - baz : str, - } - ") - .unwrap() - ) - .unwrap()[0], - Statement::StructureDefinition( - StructureDefinition::new( - Identifier::new("Foo"), - vec![ - ( - Identifier::new("bar"), - TypeConstructor::Type(Type::Integer.with_position((64, 67))) - ), - ( - Identifier::new("baz"), - TypeConstructor::Type(Type::String.with_position((99, 102))) - ), - ] - ) - .with_position((21, 125)) - ) - ) - } - #[test] fn map_index() { assert_eq!( @@ -912,23 +913,23 @@ mod tests { #[test] fn list_of_type() { assert_eq!( - parse(&lex("foobar : list(bool) = [true]").unwrap()).unwrap()[0], + parse(&lex("foobar : [bool] = [true]").unwrap()).unwrap()[0], Statement::Assignment( Assignment::new( Identifier::new("foobar").with_position((0, 6)), Some(TypeConstructor::ListOf( - Box::new(TypeConstructor::Type(Type::Boolean.with_position((9, 19)))) - .with_position((0, 0)) + Box::new(TypeConstructor::Type(Type::Boolean.with_position((10, 14)))) + .with_position((9, 15)) )), AssignmentOperator::Assign, Statement::Expression(Expression::Value( ValueNode::List(vec![Expression::Value( - ValueNode::Boolean(true).with_position((23, 27)) + ValueNode::Boolean(true).with_position((19, 23)) )]) - .with_position((22, 28)) + .with_position((18, 24)) )) ) - .with_position((0, 28)) + .with_position((0, 24)) ) ); } @@ -1009,7 +1010,7 @@ mod tests { assert_eq!( parse(&lex("fn () -> int { 0 }").unwrap()).unwrap()[0], Statement::Expression(Expression::Value( - ValueNode::Parsed { + ValueNode::Function { type_parameters: None, value_parameters: vec![], return_type: TypeConstructor::Type(Type::Integer.with_position((9, 12))), @@ -1025,7 +1026,7 @@ mod tests { assert_eq!( parse(&lex("fn (x: int) -> int { x }").unwrap()).unwrap()[0], Statement::Expression(Expression::Value( - ValueNode::Parsed { + ValueNode::Function { type_parameters: None, value_parameters: vec![( Identifier::new("x"), @@ -1047,7 +1048,7 @@ mod tests { assert_eq!( parse(&lex("fn T, U (x: T, y: U) -> T { x }").unwrap()).unwrap()[0], Statement::Expression(Expression::Value( - ValueNode::Parsed { + ValueNode::Function { type_parameters: Some(vec![Identifier::new("T"), Identifier::new("U"),]), value_parameters: vec![ ( diff --git a/std/json.ds b/std/json.ds index 477bfba..0557c7d 100644 --- a/std/json.ds +++ b/std/json.ds @@ -1,5 +1,5 @@ json = { - parse = fn (T)(input: str) -> T { + parse = fn T (input: str) -> T { JSON_PARSE T input } }