1
0

Refine enums

This commit is contained in:
Jeff 2024-03-18 16:00:04 -04:00
parent 40efafb163
commit 9f98617f6a
7 changed files with 214 additions and 78 deletions

View File

@ -8,15 +8,15 @@ use super::{AbstractTree, Action, Identifier, Type, WithPosition};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct EnumDefinition { pub struct EnumDefinition {
name: Identifier, name: Identifier,
type_parameters: Vec<Identifier>, type_parameters: Option<Vec<Identifier>>,
variants: Vec<(Identifier, Option<Vec<WithPosition<Type>>>)>, variants: Vec<(Identifier, Option<WithPosition<Type>>)>,
} }
impl EnumDefinition { impl EnumDefinition {
pub fn new( pub fn new(
name: Identifier, name: Identifier,
type_parameters: Vec<Identifier>, type_parameters: Option<Vec<Identifier>>,
variants: Vec<(Identifier, Option<Vec<WithPosition<Type>>>)>, variants: Vec<(Identifier, Option<WithPosition<Type>>)>,
) -> Self { ) -> Self {
Self { Self {
name, name,
@ -24,6 +24,18 @@ impl EnumDefinition {
variants, variants,
} }
} }
pub fn name(&self) -> &Identifier {
&self.name
}
pub fn type_parameters(&self) -> &Option<Vec<Identifier>> {
&self.type_parameters
}
pub fn variants(&self) -> &Vec<(Identifier, Option<WithPosition<Type>>)> {
&self.variants
}
} }
impl AbstractTree for EnumDefinition { impl AbstractTree for EnumDefinition {

View File

@ -12,7 +12,12 @@ use super::{AbstractTree, Action};
pub enum Type { pub enum Type {
Any, Any,
Boolean, Boolean,
Custom(Identifier), Parameter(Identifier),
Enum {
name: Identifier,
type_arguments: Option<Vec<Type>>,
variants: Vec<(Identifier, Option<Type>)>,
},
Float, Float,
Function { Function {
parameter_types: Vec<Type>, parameter_types: Vec<Type>,
@ -45,7 +50,7 @@ impl Type {
| (Type::None, Type::None) | (Type::None, Type::None)
| (Type::Range, Type::Range) | (Type::Range, Type::Range)
| (Type::String, Type::String) => Ok(()), | (Type::String, Type::String) => Ok(()),
(Type::Custom(left), Type::Custom(right)) => { (Type::Parameter(left), Type::Parameter(right)) => {
if left == right { if left == right {
Ok(()) Ok(())
} else { } else {
@ -113,7 +118,26 @@ impl Display for Type {
match self { match self {
Type::Any => write!(f, "any"), Type::Any => write!(f, "any"),
Type::Boolean => write!(f, "boolean"), Type::Boolean => write!(f, "boolean"),
Type::Custom(name) => write!(f, "{name}"), Type::Parameter(name) => write!(f, "{name}"),
Type::Enum {
name,
type_arguments,
variants: _,
} => {
write!(f, "{name}(")?;
if let Some(type_arguments) = type_arguments {
for (index, r#type) in type_arguments.into_iter().enumerate() {
if index == type_arguments.len() - 1 {
write!(f, "{}", r#type)?;
} else {
write!(f, "{}, ", r#type)?;
}
}
}
write!(f, ")")
}
Type::Float => write!(f, "float"), Type::Float => write!(f, "float"),
Type::Integer => write!(f, "integer"), Type::Integer => write!(f, "integer"),
Type::List => write!(f, "list"), Type::List => write!(f, "list"),
@ -160,7 +184,7 @@ mod tests {
assert_eq!(Type::Any.check(&Type::Any), Ok(())); assert_eq!(Type::Any.check(&Type::Any), Ok(()));
assert_eq!(Type::Boolean.check(&Type::Boolean), Ok(())); assert_eq!(Type::Boolean.check(&Type::Boolean), Ok(()));
assert_eq!( assert_eq!(
Type::Custom(Identifier::new("foo")).check(&Type::Custom(Identifier::new("foo"))), Type::Parameter(Identifier::new("foo")).check(&Type::Parameter(Identifier::new("foo"))),
Ok(()) Ok(())
); );
assert_eq!(Type::Float.check(&Type::Float), Ok(())); assert_eq!(Type::Float.check(&Type::Float), Ok(()));
@ -183,8 +207,8 @@ mod tests {
#[test] #[test]
fn errors() { fn errors() {
let foo = Type::Custom(Identifier::new("foo")); let foo = Type::Parameter(Identifier::new("foo"));
let bar = Type::Custom(Identifier::new("bar")); let bar = Type::Parameter(Identifier::new("bar"));
assert_eq!( assert_eq!(
foo.check(&bar), foo.check(&bar),

View File

@ -15,7 +15,8 @@ pub enum ValueNode {
EnumInstance { EnumInstance {
name: Identifier, name: Identifier,
variant: Identifier, variant: Identifier,
expressions: Vec<WithPosition<Expression>>, type_arguments: Option<Vec<WithPosition<Type>>>,
expression: Box<WithPosition<Expression>>,
}, },
Float(f64), Float(f64),
Integer(i64), Integer(i64),
@ -37,18 +38,29 @@ pub enum ValueNode {
} }
impl AbstractTree for ValueNode { impl AbstractTree for ValueNode {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> { fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
let r#type = match self { let r#type = match self {
ValueNode::Boolean(_) => Type::Boolean, ValueNode::Boolean(_) => Type::Boolean,
ValueNode::EnumInstance { name, .. } => Type::Custom(name.clone()), ValueNode::EnumInstance {
name,
variant: _,
type_arguments,
expression: _,
} => {
if let Some(r#type) = context.get_type(name)? {
r#type
} else {
Type::None
}
}
ValueNode::Float(_) => Type::Float, ValueNode::Float(_) => Type::Float,
ValueNode::Integer(_) => Type::Integer, ValueNode::Integer(_) => Type::Integer,
ValueNode::List(items) => { ValueNode::List(items) => {
let mut item_types = Vec::with_capacity(items.len()); let mut item_types = Vec::with_capacity(items.len());
for expression in items { for expression in items {
item_types.push(expression.node.expected_type(_context)?); item_types.push(expression.node.expected_type(context)?);
} }
Type::ListExact(item_types) Type::ListExact(item_types)
@ -117,6 +129,16 @@ impl AbstractTree for ValueNode {
})?; })?;
} }
if let ValueNode::EnumInstance {
name,
variant,
type_arguments,
expression,
} = self
{
let r#type = self.expected_type(context)?;
}
Ok(()) Ok(())
} }
@ -126,11 +148,9 @@ impl AbstractTree for ValueNode {
ValueNode::EnumInstance { ValueNode::EnumInstance {
name, name,
variant, variant,
expressions, type_arguments: _,
expression,
} => { } => {
let mut values = Vec::with_capacity(expressions.len());
for expression in expressions {
let action = expression.node.run(_context)?; let action = expression.node.run(_context)?;
let value = if let Action::Return(value) = action { let value = if let Action::Return(value) = action {
value value
@ -138,10 +158,7 @@ impl AbstractTree for ValueNode {
todo!() todo!()
}; };
values.push(value); Value::enum_instance(EnumInstance::new(name, variant, value))
}
Value::enum_instance(EnumInstance::new(name, variant, values))
} }
ValueNode::Float(float) => Value::float(float), ValueNode::Float(float) => Value::float(float),
ValueNode::Integer(integer) => Value::integer(integer), ValueNode::Integer(integer) => Value::integer(integer),
@ -213,12 +230,14 @@ impl Ord for ValueNode {
EnumInstance { EnumInstance {
name: left_name, name: left_name,
variant: left_variant, variant: left_variant,
expressions: left_expressions, type_arguments: left_types,
expression: left_expression,
}, },
EnumInstance { EnumInstance {
name: right_name, name: right_name,
variant: right_variant, variant: right_variant,
expressions: right_expressions, type_arguments: right_types,
expression: right_expression,
}, },
) => { ) => {
let name_cmp = left_name.cmp(right_name); let name_cmp = left_name.cmp(right_name);
@ -227,7 +246,13 @@ impl Ord for ValueNode {
let variant_cmp = left_variant.cmp(right_variant); let variant_cmp = left_variant.cmp(right_variant);
if variant_cmp.is_eq() { if variant_cmp.is_eq() {
left_expressions.cmp(right_expressions) let type_cmp = left_types.cmp(right_types);
if type_cmp.is_eq() {
left_expression.cmp(right_expression)
} else {
type_cmp
}
} else { } else {
variant_cmp variant_cmp
} }

View File

@ -77,7 +77,34 @@ impl Context {
let r#type = match value_data { let r#type = match value_data {
ValueData::Type(r#type) => r#type.clone(), ValueData::Type(r#type) => r#type.clone(),
ValueData::Value(value) => value.r#type(), ValueData::Value(value) => value.r#type(),
ValueData::EnumDefinition(_) => return Ok(None), ValueData::EnumDefinition(enum_definition) => {
let type_arguments =
enum_definition
.type_parameters()
.as_ref()
.map(|identifier_list| {
identifier_list
.into_iter()
.map(|identifier| Type::Parameter(identifier.clone()))
.collect()
});
let variants = enum_definition
.variants()
.into_iter()
.map(|(identifier, type_option)| {
(
identifier.clone(),
type_option.clone().map(|r#type| r#type.node),
)
})
.collect();
Type::Enum {
name: enum_definition.name().clone(),
type_arguments,
variants,
}
}
}; };
return Ok(Some(r#type.clone())); return Ok(Some(r#type.clone()));

View File

@ -107,7 +107,7 @@ pub fn parser<'src>() -> DustParser<'src> {
just(Token::Keyword("list")).to(Type::List), just(Token::Keyword("list")).to(Type::List),
identifier identifier
.clone() .clone()
.map(|identifier| Type::Custom(identifier)), .map(|identifier| Type::Parameter(identifier)),
)) ))
}) })
.map_with(|r#type, state| r#type.with_position(state.span())); .map_with(|r#type, state| r#type.with_position(state.span()));
@ -345,10 +345,8 @@ pub fn parser<'src>() -> DustParser<'src> {
let enum_instance = identifier let enum_instance = identifier
.clone() .clone()
.then_ignore(just(Token::Control(Control::DoubleColon)))
.then(identifier.clone())
.then( .then(
positioned_expression r#type
.clone() .clone()
.separated_by(just(Token::Control(Control::Comma))) .separated_by(just(Token::Control(Control::Comma)))
.allow_trailing() .allow_trailing()
@ -356,13 +354,21 @@ pub fn parser<'src>() -> DustParser<'src> {
.delimited_by( .delimited_by(
just(Token::Control(Control::ParenOpen)), just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)), just(Token::Control(Control::ParenClose)),
),
) )
.map_with(|((name, variant), expressions), state| { .or_not(),
)
.then_ignore(just(Token::Control(Control::DoubleColon)))
.then(identifier.clone())
.then(positioned_expression.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
))
.map_with(|(((name, type_arguments), variant), expression), state| {
Expression::Value(ValueNode::EnumInstance { Expression::Value(ValueNode::EnumInstance {
name, name,
type_arguments,
variant, variant,
expressions, expression: Box::new(expression),
}) })
.with_position(state.span()) .with_position(state.span())
}); });
@ -452,9 +458,6 @@ pub fn parser<'src>() -> DustParser<'src> {
let enum_variant = identifier.clone().then( let enum_variant = identifier.clone().then(
r#type r#type
.clone() .clone()
.separated_by(just(Token::Control(Control::Comma)))
.allow_trailing()
.collect()
.delimited_by( .delimited_by(
just(Token::Control(Control::ParenOpen)), just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)), just(Token::Control(Control::ParenClose)),
@ -473,7 +476,8 @@ pub fn parser<'src>() -> DustParser<'src> {
.delimited_by( .delimited_by(
just(Token::Control(Control::ParenOpen)), just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)), just(Token::Control(Control::ParenClose)),
), )
.or_not(),
) )
.then( .then(
enum_variant enum_variant
@ -517,9 +521,9 @@ mod tests {
assert_eq!( assert_eq!(
parse( parse(
&lex(" &lex("
enum FooBar (F, B) { enum FooBar {
Foo(F), Foo,
Bar(B), Bar,
} }
") ")
.unwrap() .unwrap()
@ -528,20 +532,10 @@ mod tests {
.node, .node,
Statement::EnumDefinition(EnumDefinition::new( Statement::EnumDefinition(EnumDefinition::new(
Identifier::new("FooBar"), Identifier::new("FooBar"),
vec![Identifier::new("F"), Identifier::new("B")], None,
vec![ vec![
( (Identifier::new("Foo"), None),
Identifier::new("Foo"), (Identifier::new("Bar"), None)
Some(vec![
Type::Custom(Identifier::new("F")).with_position((62, 63))
]),
),
(
Identifier::new("Bar"),
Some(vec![
Type::Custom(Identifier::new("B")).with_position((90, 91))
])
)
] ]
)) ))
); );

View File

@ -81,7 +81,7 @@ impl Value {
pub fn r#type(&self) -> Type { pub fn r#type(&self) -> Type {
match self.0.as_ref() { match self.0.as_ref() {
ValueInner::Boolean(_) => Type::Boolean, ValueInner::Boolean(_) => Type::Boolean,
ValueInner::EnumInstance(EnumInstance { name, .. }) => Type::Custom(name.clone()), ValueInner::EnumInstance(EnumInstance { name, .. }) => Type::Parameter(name.clone()),
ValueInner::Float(_) => Type::Float, ValueInner::Float(_) => Type::Float,
ValueInner::Integer(_) => Type::Integer, ValueInner::Integer(_) => Type::Integer,
ValueInner::List(values) => { ValueInner::List(values) => {
@ -146,19 +146,9 @@ impl Display for Value {
ValueInner::EnumInstance(EnumInstance { ValueInner::EnumInstance(EnumInstance {
name, name,
variant, variant,
value: content, value,
}) => { }) => {
write!(f, "{name}::{variant}(")?; write!(f, "{name}::{variant}({value})")
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::Float(float) => write!(f, "{float}"),
ValueInner::Integer(integer) => write!(f, "{integer}"), ValueInner::Integer(integer) => write!(f, "{integer}"),
@ -276,11 +266,11 @@ impl Ord for ValueInner {
pub struct EnumInstance { pub struct EnumInstance {
name: Identifier, name: Identifier,
variant: Identifier, variant: Identifier,
value: Vec<Value>, value: Value,
} }
impl EnumInstance { impl EnumInstance {
pub fn new(name: Identifier, variant: Identifier, value: Vec<Value>) -> Self { pub fn new(name: Identifier, variant: Identifier, value: Value) -> Self {
Self { Self {
name, name,
variant, variant,

View File

@ -1,11 +1,43 @@
use dust_lang::*; use dust_lang::{
abstract_tree::Type,
error::{Error, TypeConflict, ValidationError},
*,
};
#[test] #[test]
fn define_enum() { fn simple_enum_type_check() {
assert_eq!(
interpret( interpret(
" "
enum FooBar(F) { enum FooBar {
Foo(F), Foo(int),
Bar,
}
foo = FooBar::Foo('yo')
foo
",
),
Err(vec![Error::Validation {
error: ValidationError::TypeCheck {
conflict: TypeConflict {
actual: Type::String,
expected: Type::Integer,
},
actual_position: (0, 0).into(),
expected_position: (0, 0).into()
},
position: (0, 0).into()
}])
)
}
#[test]
fn simple_enum() {
interpret(
"
enum FooBar {
Foo(int),
Bar, Bar,
} }
@ -15,3 +47,35 @@ fn define_enum() {
) )
.unwrap(); .unwrap();
} }
#[test]
fn simple_enum_with_type_argument() {
interpret(
"
enum FooBar(F) {
Foo(F),
Bar,
}
foo = FooBar(int)::Foo(1)
foo
",
)
.unwrap();
}
#[test]
fn complex_enum_with_type_arguments() {
interpret(
"
enum FooBar(F, B) {
Foo(F),
Bar(B),
}
bar = FooBar(int, str)::Bar('bar')
bar
",
)
.unwrap();
}