Implement basic enum instances

This commit is contained in:
Jeff 2024-06-19 10:48:22 -04:00
parent c2d8bd299f
commit 0de25215b8
6 changed files with 240 additions and 36 deletions

View File

@ -16,6 +16,11 @@ use super::{
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ValueNode {
Boolean(bool),
EnumInstance {
type_name: WithPosition<Identifier>,
variant: WithPosition<Identifier>,
content: Option<Vec<Expression>>,
},
Float(f64),
Integer(i64),
List(Vec<Expression>),
@ -142,6 +147,34 @@ impl Evaluate for ValueNode {
) -> Result<Evaluation, RuntimeError> {
let value = match self {
ValueNode::Boolean(boolean) => Value::boolean(boolean),
ValueNode::EnumInstance {
type_name,
variant,
content: expressions,
} => {
let content = if let Some(expressions) = expressions {
let mut values = Vec::with_capacity(expressions.len());
for expression in expressions {
let position = expression.position();
let evaluation = expression.evaluate(context, _manage_memory)?;
if let Evaluation::Return(value) = evaluation {
values.push(value);
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::InterpreterExpectedReturn(position),
));
}
}
Some(values)
} else {
None
};
Value::enum_instance(type_name.node, variant.node, content)
}
ValueNode::Float(float) => Value::float(float),
ValueNode::Integer(integer) => Value::integer(integer),
ValueNode::List(expression_list) => {
@ -267,6 +300,33 @@ impl Ord for ValueNode {
(Range(_), _) => Ordering::Greater,
(String(left), String(right)) => left.cmp(right),
(String(_), _) => Ordering::Greater,
(
EnumInstance {
type_name: left_name,
variant: left_variant,
content: left_content,
},
EnumInstance {
type_name: right_name,
variant: right_variant,
content: right_content,
},
) => {
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_content.cmp(right_content)
} else {
variant_cmp
}
} else {
name_cmp
}
}
(EnumInstance { .. }, _) => Ordering::Greater,
(
Function {
type_parameters: left_type_arguments,
@ -329,6 +389,15 @@ impl ExpectedType for ValueNode {
fn expected_type(&self, context: &mut Context) -> Result<Type, ValidationError> {
let r#type = match self {
ValueNode::Boolean(_) => Type::Boolean,
ValueNode::EnumInstance { type_name, .. } => {
if let Some(r#type) = context.get_type(&type_name.node)? {
r#type
} else {
return Err(ValidationError::EnumDefinitionNotFound(
type_name.node.clone(),
));
}
}
ValueNode::Float(_) => Type::Float,
ValueNode::Integer(_) => Type::Integer,
ValueNode::List(items) => {

View File

@ -160,6 +160,7 @@ pub enum ValidationError {
identifier: Identifier,
position: SourcePosition,
},
EnumDefinitionNotFound(Identifier),
}
impl From<RwLockPoisonError> for ValidationError {

View File

@ -425,8 +425,10 @@ impl InterpreterError {
)
}
ValidationError::ExpectedString { .. } => todo!(),
ValidationError::EnumDefinitionNotFound(_) => todo!(),
}
}
let report = builder.finish();
reports.push(report);

View File

@ -316,6 +316,32 @@ pub fn parser<'src>(
},
);
let enum_instance = positioned_identifier
.clone()
.then_ignore(just(Token::Control(Control::DoubleColon)))
.then(positioned_identifier.clone())
.then(
expression
.clone()
.separated_by(just(Token::Control(Control::Comma)))
.collect()
.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
)
.or_not(),
)
.map_with(|((type_name, variant), content), state| {
Expression::Value(
ValueNode::EnumInstance {
type_name,
variant,
content,
}
.with_position(state.span()),
)
});
let built_in_function_call = choice((
just(Token::Keyword(Keyword::Length))
.ignore_then(expression.clone())
@ -390,6 +416,7 @@ pub fn parser<'src>(
);
let atom = choice((
enum_instance.clone(),
range.clone(),
function.clone(),
list.clone(),
@ -411,41 +438,6 @@ pub fn parser<'src>(
Expression::Logic(Box::new(Logic::Not(expression)).with_position(span))
},
),
postfix(
3,
expression.clone().delimited_by(
just(Token::Control(Control::SquareOpen)),
just(Token::Control(Control::SquareClose)),
),
|left, right, span| {
Expression::ListIndex(
Box::new(ListIndex::new(left, right)).with_position(span),
)
},
),
postfix(
3,
turbofish.clone().or_not().then(
expression
.clone()
.separated_by(just(Token::Control(Control::Comma)))
.collect()
.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
),
),
|function_expression, (type_parameters, value_parameters), span| {
Expression::FunctionCall(
FunctionCall::new(
function_expression,
type_parameters,
value_parameters,
)
.with_position(span),
)
},
),
infix(
left(1),
just(Token::Operator(Operator::Equal)),
@ -554,6 +546,42 @@ pub fn parser<'src>(
)
},
),
postfix(
3,
expression.clone().delimited_by(
just(Token::Control(Control::SquareOpen)),
just(Token::Control(Control::SquareClose)),
),
|left, right, span| {
Expression::ListIndex(
Box::new(ListIndex::new(left, right)).with_position(span),
)
},
),
// Function call
postfix(
3,
turbofish.clone().or_not().then(
expression
.clone()
.separated_by(just(Token::Control(Control::Comma)))
.collect()
.delimited_by(
just(Token::Control(Control::ParenOpen)),
just(Token::Control(Control::ParenClose)),
),
),
|function_expression, (type_parameters, value_parameters), span| {
Expression::FunctionCall(
FunctionCall::new(
function_expression,
type_parameters,
value_parameters,
)
.with_position(span),
)
},
),
// As
postfix(
2,
@ -568,6 +596,7 @@ pub fn parser<'src>(
choice((
logic_math_indexes_as_and_function_calls,
enum_instance,
built_in_function_call,
range,
function,
@ -576,7 +605,6 @@ pub fn parser<'src>(
basic_value,
identifier_expression,
))
// .delimited_by(comment.clone().or_not(), comment.or_not())
});
let expression_statement = expression

View File

@ -32,6 +32,18 @@ impl Value {
Value(Arc::new(ValueInner::Boolean(boolean)))
}
pub fn enum_instance(
type_name: Identifier,
variant: Identifier,
content: Option<Vec<Value>>,
) -> Self {
Value(Arc::new(ValueInner::EnumInstance {
type_name,
variant,
content,
}))
}
pub fn float(float: f64) -> Self {
Value(Arc::new(ValueInner::Float(float)))
}
@ -107,6 +119,23 @@ impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.inner().as_ref() {
ValueInner::Boolean(boolean) => write!(f, "{boolean}"),
ValueInner::EnumInstance {
type_name,
variant,
content,
} => {
if let Some(values) = content {
write!(f, "{type_name}::{variant}(")?;
for value in values {
write!(f, "{value}")?;
}
write!(f, ")")
} else {
write!(f, "{type_name}::{variant}")
}
}
ValueInner::Float(float) => write!(f, "{float}"),
ValueInner::Integer(integer) => write!(f, "{integer}"),
ValueInner::List(list) => {
@ -195,6 +224,19 @@ impl Serialize for Value {
{
match self.0.as_ref() {
ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean),
ValueInner::EnumInstance {
type_name,
variant,
content,
} => {
let mut struct_ser = serializer.serialize_struct("EnumInstance", 3)?;
struct_ser.serialize_field("type_name", type_name)?;
struct_ser.serialize_field("variant", variant)?;
struct_ser.serialize_field("content", content)?;
struct_ser.end()
}
ValueInner::Float(float) => serializer.serialize_f64(*float),
ValueInner::Function(Function {
type_parameters,
@ -496,6 +538,11 @@ impl<'de> Deserialize<'de> for Value {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ValueInner {
Boolean(bool),
EnumInstance {
type_name: Identifier,
variant: Identifier,
content: Option<Vec<Value>>,
},
Float(f64),
Function(Function),
Integer(i64),
@ -513,6 +560,13 @@ impl ValueInner {
pub fn r#type(&self, context: &Context) -> Result<Type, ValidationError> {
let r#type = match self {
ValueInner::Boolean(_) => Type::Boolean,
ValueInner::EnumInstance { type_name, .. } => {
if let Some(r#type) = context.get_type(type_name)? {
r#type
} else {
return Err(ValidationError::EnumDefinitionNotFound(type_name.clone()));
}
}
ValueInner::Float(_) => Type::Float,
ValueInner::Integer(_) => Type::Integer,
ValueInner::List(values) => {
@ -591,6 +645,33 @@ impl Ord for ValueInner {
(Range(_), _) => Ordering::Greater,
(String(left), String(right)) => left.cmp(right),
(String(_), _) => Ordering::Greater,
(
EnumInstance {
type_name: left_name,
variant: left_variant,
content: left_content,
},
EnumInstance {
type_name: right_name,
variant: right_variant,
content: right_content,
},
) => {
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_content.cmp(right_content)
} else {
variant_cmp
}
} else {
name_cmp
}
}
(EnumInstance { .. }, _) => Ordering::Greater,
(Function(left), Function(right)) => left.cmp(right),
(Function(_), _) => Ordering::Greater,
(

23
dust-lang/tests/enums.rs Normal file
View File

@ -0,0 +1,23 @@
use dust_lang::{identifier::Identifier, *};
#[test]
fn simple_enum() {
assert_eq!(
interpret(
"test",
"
type FooBar = enum {
Foo,
Bar,
}
FooBar::Foo
"
),
Ok(Some(Value::enum_instance(
Identifier::new("FooBar"),
Identifier::new("Foo"),
None
)))
);
}