From 790438d1e317c9e5162a338a8ce3f837612d1fba Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 12 Jul 2024 11:08:53 -0400 Subject: [PATCH] Add type arguments to enum instances --- dust-lang/src/abstract_tree/value_node.rs | 72 ++++++++++++++--------- dust-lang/src/parser/mod.rs | 42 ++++++------- dust-lang/src/parser/tests.rs | 10 ++-- dust-lang/src/value.rs | 48 +++++++++++---- dust-lang/tests/enums.rs | 6 +- 5 files changed, 106 insertions(+), 72 deletions(-) diff --git a/dust-lang/src/abstract_tree/value_node.rs b/dust-lang/src/abstract_tree/value_node.rs index e573323..6a1bde7 100644 --- a/dust-lang/src/abstract_tree/value_node.rs +++ b/dust-lang/src/abstract_tree/value_node.rs @@ -25,8 +25,9 @@ pub enum ValueNode { BuiltInFunction(BuiltInFunction), EnumInstance { type_name: WithPosition, + type_arguments: Option>, variant: WithPosition, - content: Option>, + content: Option>, }, Float(f64), Integer(i64), @@ -101,14 +102,13 @@ impl AbstractNode for ValueNode { if let ValueNode::EnumInstance { type_name, + type_arguments, variant, content, } = self { - if let Some(expressions) = content { - for expression in expressions { - expression.define_and_validate(context, _manage_memory, scope)?; - } + if let Some(expression) = content { + expression.define_and_validate(context, _manage_memory, scope)?; } if let Some(Type::Enum { name, variants, .. }) = context.get_type(&type_name.node)? { @@ -296,30 +296,39 @@ impl AbstractNode for ValueNode { ValueNode::Boolean(boolean) => Value::boolean(boolean), ValueNode::EnumInstance { type_name, + type_arguments, variant, content: expressions, } => { - let content = if let Some(expressions) = expressions { - let mut values = Vec::with_capacity(expressions.len()); + let content = if let Some(expression) = expressions { + let position = expression.position(); + let evaluation = expression.evaluate(context, manage_memory, scope)?; - for expression in expressions { - let position = expression.position(); - let evaluation = expression.evaluate(context, manage_memory, scope)?; - - if let Some(Evaluation::Return(value)) = evaluation { - values.push(value); - } else { - return Err(RuntimeError::ValidationFailure( - ValidationError::ExpectedValueStatement(position), - )); - } + if let Some(Evaluation::Return(value)) = evaluation { + Some(value) + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedValueStatement(position), + )); } - Some(values) + } else { + None + }; + let type_arguments = if let Some(arguments) = type_arguments { + let mut types = Vec::with_capacity(arguments.len()); + + for constructor in arguments { + let r#type = constructor.construct(context)?; + + types.push(r#type); + } + + Some(types) } else { None }; - Value::enum_instance(type_name.node, variant.node, content) + Value::enum_instance(type_name.node, type_arguments, variant.node, content) } ValueNode::Float(float) => Value::float(float), ValueNode::Integer(integer) => Value::integer(integer), @@ -577,11 +586,13 @@ impl Ord for ValueNode { ( EnumInstance { type_name: left_name, + type_arguments: left_type_args, variant: left_variant, content: left_content, }, EnumInstance { type_name: right_name, + type_arguments: right_type_args, variant: right_variant, content: right_content, }, @@ -589,12 +600,18 @@ impl Ord for ValueNode { let name_cmp = left_name.cmp(right_name); if name_cmp.is_eq() { - let variant_cmp = left_variant.cmp(right_variant); + let type_arg_cmp = left_type_args.cmp(right_type_args); - if variant_cmp.is_eq() { - left_content.cmp(right_content) + if type_arg_cmp.is_eq() { + let variant_cmp = left_variant.cmp(right_variant); + + if variant_cmp.is_eq() { + left_content.cmp(right_content) + } else { + variant_cmp + } } else { - variant_cmp + type_arg_cmp } } else { name_cmp @@ -670,15 +687,14 @@ impl Display for ValueNode { ValueNode::BuiltInFunction(built_in_function) => write!(f, "{built_in_function}"), ValueNode::EnumInstance { type_name, + type_arguments, variant, content, } => { write!(f, "{}::{}", type_name.node, variant.node)?; - if let Some(content) = content { - for expression in content { - write!(f, "{expression}")?; - } + if let Some(expression) = content { + write!(f, "{expression}")?; } Ok(()) diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index 6f9b7bc..b8cd75f 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -267,6 +267,18 @@ pub fn parser<'src>( type ValueParameters = Vec<(Identifier, TypeConstructor)>; type FunctionParameters = (Option, ValueParameters); + let turbofish = just(Token::Symbol(Symbol::DoubleColon)).ignore_then( + type_constructor + .clone() + .separated_by(just(Token::Symbol(Symbol::Comma))) + .at_least(1) + .collect() + .delimited_by( + just(Token::Symbol(Symbol::Less)), + just(Token::Symbol(Symbol::Greater)), + ), + ); + let function = just(Token::Keyword(Keyword::Fn)) .ignore_then( identifier @@ -323,25 +335,17 @@ pub fn parser<'src>( ); let enum_instance = positioned_identifier + .then(turbofish.clone().or_not()) .then_ignore(just(Token::Symbol(Symbol::DoubleColon))) .then(positioned_identifier) - .then( - expression - .clone() - .separated_by(just(Token::Symbol(Symbol::Comma))) - .collect() - .delimited_by( - just(Token::Symbol(Symbol::ParenOpen)), - just(Token::Symbol(Symbol::ParenClose)), - ) - .or_not(), - ) - .map_with(|((type_name, variant), content), state| { + .then(expression.clone().or_not()) + .map_with(|(((type_name, type_arguments), variant), content), state| { Expression::Value( ValueNode::EnumInstance { type_name, + type_arguments, variant, - content, + content: content.map(|expression| Box::new(expression)), } .with_position(state.span()), ) @@ -403,18 +407,6 @@ pub fn parser<'src>( expression }); - let turbofish = just(Token::Symbol(Symbol::DoubleColon)).ignore_then( - type_constructor - .clone() - .separated_by(just(Token::Symbol(Symbol::Comma))) - .at_least(1) - .collect() - .delimited_by( - just(Token::Symbol(Symbol::Less)), - just(Token::Symbol(Symbol::Greater)), - ), - ); - let atom = choice(( built_in_function, enum_instance.clone(), diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index aaba9cc..9267c62 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -49,10 +49,11 @@ fn type_invokation() { Statement::Expression(Expression::Value( ValueNode::EnumInstance { type_name: Identifier::new("Foo").with_position((14, 17)), + type_arguments: None, variant: Identifier::new("Bar").with_position((19, 22)), - content: Some(vec![Expression::Value( + content: Some(Box::new(Expression::Value( ValueNode::Integer(42).with_position((23, 25)) - )]) + ))) } .with_position((14, 26)) )) @@ -69,10 +70,11 @@ fn enum_instance() { Statement::Expression(Expression::Value( ValueNode::EnumInstance { type_name: Identifier::new("Foo").with_position((0, 3)), + type_arguments: None, variant: Identifier::new("Bar").with_position((5, 8)), - content: Some(vec![Expression::Value( + content: Some(Box::new(Expression::Value( ValueNode::Integer(42).with_position((9, 11)) - )]) + ))) } .with_position((0, 12)) )) diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index c90d28a..093b7d0 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -41,11 +41,13 @@ impl Value { pub fn enum_instance( type_name: Identifier, + type_arguments: Option>, variant: Identifier, - content: Option>, + content: Option, ) -> Self { Value(Arc::new(ValueInner::EnumInstance { type_name, + type_arguments, variant, content, })) @@ -128,20 +130,29 @@ impl Display for Value { ValueInner::Boolean(boolean) => write!(f, "{boolean}"), ValueInner::EnumInstance { type_name, + type_arguments, variant, content, } => { - if let Some(values) = content { - write!(f, "{type_name}::{variant}(")?; + write!(f, "{type_name}::")?; - for value in values { - write!(f, "{value}")?; + if let Some(types) = type_arguments { + write!(f, "::<")?; + + for r#type in types { + write!(f, "{type}, ")?; } - write!(f, ")") - } else { - write!(f, "{type_name}::{variant}") + write!(f, ">")?; } + + write!(f, "::{variant}(")?; + + if let Some(value) = content { + write!(f, "{value}")?; + } + + write!(f, ")") } ValueInner::Float(float) => { write!(f, "{float}")?; @@ -257,12 +268,14 @@ impl Serialize for Value { ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean), ValueInner::EnumInstance { type_name, + type_arguments, variant, content, } => { let mut struct_ser = serializer.serialize_struct("EnumInstance", 3)?; struct_ser.serialize_field("type_name", type_name)?; + struct_ser.serialize_field("type_arguments", type_arguments)?; struct_ser.serialize_field("variant", variant)?; struct_ser.serialize_field("content", content)?; @@ -574,8 +587,9 @@ pub enum ValueInner { BuiltInFunction(BuiltInFunction), EnumInstance { type_name: Identifier, + type_arguments: Option>, variant: Identifier, - content: Option>, + content: Option, }, Float(f64), Function(Function), @@ -691,11 +705,13 @@ impl Ord for ValueInner { ( EnumInstance { type_name: left_name, + type_arguments: left_arguments, variant: left_variant, content: left_content, }, EnumInstance { type_name: right_name, + type_arguments: right_arguments, variant: right_variant, content: right_content, }, @@ -703,12 +719,18 @@ impl Ord for ValueInner { let name_cmp = left_name.cmp(right_name); if name_cmp.is_eq() { - let variant_cmp = left_variant.cmp(right_variant); + let argument_cmp = left_arguments.cmp(right_arguments); - if variant_cmp.is_eq() { - left_content.cmp(right_content) + if argument_cmp.is_eq() { + let variant_cmp = left_variant.cmp(right_variant); + + if variant_cmp.is_eq() { + left_content.cmp(right_content) + } else { + variant_cmp + } } else { - variant_cmp + argument_cmp } } else { name_cmp diff --git a/dust-lang/tests/enums.rs b/dust-lang/tests/enums.rs index 8bdf664..a1f4fdb 100644 --- a/dust-lang/tests/enums.rs +++ b/dust-lang/tests/enums.rs @@ -16,6 +16,7 @@ fn simple_enum() { ), Ok(Some(Value::enum_instance( Identifier::new("FooBar"), + None, Identifier::new("Foo"), None ))) @@ -34,13 +35,14 @@ fn big_enum() { Baz(V), } - FooBarBaz::Baz(42.0) + FooBarBaz::::Baz(42.0) " ), Ok(Some(Value::enum_instance( Identifier::new("FooBarBaz"), + None, Identifier::new("Baz"), - Some(vec![Value::float(42.0)]), + Some(Value::float(42.0)), ))) ); }