Implement basic enum instances
This commit is contained in:
parent
c2d8bd299f
commit
0de25215b8
@ -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) => {
|
||||
|
@ -160,6 +160,7 @@ pub enum ValidationError {
|
||||
identifier: Identifier,
|
||||
position: SourcePosition,
|
||||
},
|
||||
EnumDefinitionNotFound(Identifier),
|
||||
}
|
||||
|
||||
impl From<RwLockPoisonError> for ValidationError {
|
||||
|
@ -425,8 +425,10 @@ impl InterpreterError {
|
||||
)
|
||||
}
|
||||
ValidationError::ExpectedString { .. } => todo!(),
|
||||
ValidationError::EnumDefinitionNotFound(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
let report = builder.finish();
|
||||
|
||||
reports.push(report);
|
||||
|
@ -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
|
||||
|
@ -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
23
dust-lang/tests/enums.rs
Normal 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
|
||||
)))
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user