diff --git a/src/abstract_tree/enum_definition.rs b/src/abstract_tree/enum_definition.rs index 0be3cfc..e1b0640 100644 --- a/src/abstract_tree/enum_definition.rs +++ b/src/abstract_tree/enum_definition.rs @@ -9,14 +9,14 @@ use super::{AbstractTree, Action, Identifier, Type, WithPosition}; pub struct EnumDefinition { name: Identifier, type_parameters: Vec, - variants: Vec<(Identifier, Vec>)>, + variants: Vec<(Identifier, Option>>)>, } impl EnumDefinition { pub fn new( name: Identifier, type_parameters: Vec, - variants: Vec<(Identifier, Vec>)>, + variants: Vec<(Identifier, Option>>)>, ) -> Self { Self { name, diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index 9e5b3b3..ea306d8 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -3,6 +3,7 @@ use std::{cmp::Ordering, collections::BTreeMap, ops::Range}; use crate::{ context::Context, error::{RuntimeError, ValidationError}, + value::EnumInstance, Value, }; @@ -11,6 +12,11 @@ use super::{AbstractTree, Action, Block, Expression, Identifier, Type, WithPosit #[derive(Clone, Debug, PartialEq)] pub enum ValueNode { Boolean(bool), + EnumInstance { + name: Identifier, + variant: Identifier, + expressions: Vec>, + }, Float(f64), Integer(i64), List(Vec>), @@ -34,6 +40,8 @@ impl AbstractTree for ValueNode { fn expected_type(&self, _context: &Context) -> Result { let r#type = match self { ValueNode::Boolean(_) => Type::Boolean, + + ValueNode::EnumInstance { name, .. } => Type::Custom(name.clone()), ValueNode::Float(_) => Type::Float, ValueNode::Integer(_) => Type::Integer, ValueNode::List(items) => { @@ -115,6 +123,26 @@ impl AbstractTree for ValueNode { fn run(self, _context: &Context) -> Result { let value = match self { ValueNode::Boolean(boolean) => Value::boolean(boolean), + ValueNode::EnumInstance { + name, + variant, + expressions, + } => { + let mut values = Vec::with_capacity(expressions.len()); + + for expression in expressions { + let action = expression.node.run(_context)?; + let value = if let Action::Return(value) = action { + value + } else { + todo!() + }; + + values.push(value); + } + + Value::enum_instance(EnumInstance::new(name, variant, values)) + } ValueNode::Float(float) => Value::float(float), ValueNode::Integer(integer) => Value::integer(integer), ValueNode::List(expression_list) => { @@ -181,6 +209,33 @@ impl Ord for ValueNode { match (self, other) { (Boolean(left), Boolean(right)) => left.cmp(right), (Boolean(_), _) => Ordering::Greater, + ( + EnumInstance { + name: left_name, + variant: left_variant, + expressions: left_expressions, + }, + EnumInstance { + name: right_name, + variant: right_variant, + expressions: right_expressions, + }, + ) => { + let name_cmp = left_name.cmp(right_name); + + if name_cmp.is_eq() { + let variant_cmp = left_variant.cmp(right_variant); + + if variant_cmp.is_eq() { + left_expressions.cmp(right_expressions) + } else { + variant_cmp + } + } else { + name_cmp + } + } + (EnumInstance { .. }, _) => Ordering::Greater, (Float(left), Float(right)) => left.total_cmp(right), (Float(_), _) => Ordering::Greater, (Integer(left), Integer(right)) => left.cmp(right), diff --git a/src/parser.rs b/src/parser.rs index 0a4f338..a44ce41 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -343,7 +343,32 @@ pub fn parser<'src>() -> DustParser<'src> { ), )); + let enum_instance = identifier + .clone() + .then_ignore(just(Token::Control(Control::DoubleColon))) + .then(identifier.clone()) + .then( + positioned_expression + .clone() + .separated_by(just(Token::Control(Control::Comma))) + .allow_trailing() + .collect() + .delimited_by( + just(Token::Control(Control::ParenOpen)), + just(Token::Control(Control::ParenClose)), + ), + ) + .map_with(|((name, variant), expressions), state| { + Expression::Value(ValueNode::EnumInstance { + name, + variant, + expressions, + }) + .with_position(state.span()) + }); + choice(( + enum_instance, range, logic_math_indexes_and_function_calls, function, @@ -433,7 +458,8 @@ pub fn parser<'src>() -> DustParser<'src> { .delimited_by( just(Token::Control(Control::ParenOpen)), just(Token::Control(Control::ParenClose)), - ), + ) + .or_not(), ); let enum_definition = just(Token::Keyword("enum")) @@ -506,11 +532,15 @@ mod tests { vec![ ( Identifier::new("Foo"), - vec![Type::Custom(Identifier::new("F")).with_position((62, 63))], + Some(vec![ + Type::Custom(Identifier::new("F")).with_position((62, 63)) + ]), ), ( Identifier::new("Bar"), - vec![Type::Custom(Identifier::new("B")).with_position((90, 91))] + Some(vec![ + Type::Custom(Identifier::new("B")).with_position((90, 91)) + ]) ) ] )) diff --git a/src/value.rs b/src/value.rs index 01bd9e9..8f95187 100644 --- a/src/value.rs +++ b/src/value.rs @@ -32,6 +32,10 @@ impl Value { Value(Arc::new(ValueInner::Boolean(boolean))) } + pub fn enum_instance(enum_instance: EnumInstance) -> Self { + Value(Arc::new(ValueInner::EnumInstance(enum_instance))) + } + pub fn float(float: f64) -> Self { Value(Arc::new(ValueInner::Float(float))) } @@ -77,6 +81,7 @@ impl Value { pub fn r#type(&self) -> Type { match self.0.as_ref() { ValueInner::Boolean(_) => Type::Boolean, + ValueInner::EnumInstance(EnumInstance { name, .. }) => Type::Custom(name.clone()), ValueInner::Float(_) => Type::Float, ValueInner::Integer(_) => Type::Integer, ValueInner::List(values) => { @@ -138,6 +143,23 @@ impl Display for Value { match self.inner().as_ref() { ValueInner::Boolean(boolean) => write!(f, "{boolean}"), + ValueInner::EnumInstance(EnumInstance { + name, + variant, + value: content, + }) => { + write!(f, "{name}::{variant}(")?; + + for (index, value) in content.into_iter().enumerate() { + if index == content.len() - 1 { + write!(f, "{value}")?; + } else { + write!(f, "{value} ")?; + } + } + + write!(f, ")") + } ValueInner::Float(float) => write!(f, "{float}"), ValueInner::Integer(integer) => write!(f, "{integer}"), ValueInner::List(list) => { @@ -197,6 +219,7 @@ impl Ord for Value { #[derive(Clone, Debug, PartialEq)] pub enum ValueInner { Boolean(bool), + EnumInstance(EnumInstance), Float(f64), Function(Function), Integer(i64), @@ -221,6 +244,8 @@ impl Ord for ValueInner { match (self, other) { (Boolean(left), Boolean(right)) => left.cmp(right), (Boolean(_), _) => Ordering::Greater, + (EnumInstance(left), EnumInstance(right)) => left.cmp(right), + (EnumInstance(_), _) => Ordering::Greater, (Float(left), Float(right)) => left.total_cmp(right), (Float(_), _) => Ordering::Greater, (Integer(left), Integer(right)) => left.cmp(right), @@ -247,6 +272,23 @@ impl Ord for ValueInner { } } +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct EnumInstance { + name: Identifier, + variant: Identifier, + value: Vec, +} + +impl EnumInstance { + pub fn new(name: Identifier, variant: Identifier, value: Vec) -> Self { + Self { + name, + variant, + value, + } + } +} + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Function { Parsed(ParsedFunction), diff --git a/tests/enums.rs b/tests/enums.rs new file mode 100644 index 0000000..e2740cd --- /dev/null +++ b/tests/enums.rs @@ -0,0 +1,17 @@ +use dust_lang::*; + +#[test] +fn define_enum() { + interpret( + " + enum FooBar(F) { + Foo(F), + Bar, + } + + foo = FooBar::Foo(1) + foo + ", + ) + .unwrap(); +}