Implement matching for enums
This commit is contained in:
parent
85cb641af8
commit
a6e52e4ee6
70
src/abstract_tree/enum_pattern.rs
Normal file
70
src/abstract_tree/enum_pattern.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node as SyntaxNode;
|
||||
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, Context, Format, Identifier, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct EnumPattern {
|
||||
name: Identifier,
|
||||
variant: Identifier,
|
||||
inner_identifier: Option<Identifier>,
|
||||
}
|
||||
|
||||
impl EnumPattern {
|
||||
pub fn name(&self) -> &Identifier {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn variant(&self) -> &Identifier {
|
||||
&self.variant
|
||||
}
|
||||
|
||||
pub fn inner_identifier(&self) -> &Option<Identifier> {
|
||||
&self.inner_identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for EnumPattern {
|
||||
fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> {
|
||||
SyntaxError::expect_syntax_node(source, "enum_pattern", node)?;
|
||||
|
||||
let enum_name_node = node.child(0).unwrap();
|
||||
let name = Identifier::from_syntax(enum_name_node, source, context)?;
|
||||
|
||||
let enum_variant_node = node.child(2).unwrap();
|
||||
let variant = Identifier::from_syntax(enum_variant_node, source, context)?;
|
||||
|
||||
let inner_identifier = if let Some(child) = node.child(4) {
|
||||
Some(Identifier::from_syntax(child, source, context)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(EnumPattern {
|
||||
name,
|
||||
variant,
|
||||
inner_identifier,
|
||||
})
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
Ok(Type::None)
|
||||
}
|
||||
|
||||
fn validate(&self, _source: &str, _context: &Context) -> Result<(), ValidationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::none())
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for EnumPattern {
|
||||
fn format(&self, _output: &mut String, _indent_level: u8) {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -3,17 +3,18 @@
|
||||
//! Note that this module is called "match" but is escaped as "r#match" because
|
||||
//! "match" is a keyword in Rust.
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node as SyntaxNode;
|
||||
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, Context, Expression, Format, Statement, SyntaxNode, Type, Value,
|
||||
AbstractTree, Context, Expression, Format, MatchPattern, Statement, Type, Value,
|
||||
};
|
||||
|
||||
/// Abstract representation of a match statement.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Match {
|
||||
matcher: Expression,
|
||||
options: Vec<(Expression, Statement)>,
|
||||
options: Vec<(MatchPattern, Statement)>,
|
||||
fallback: Option<Box<Statement>>,
|
||||
}
|
||||
|
||||
@ -25,19 +26,15 @@ impl AbstractTree for Match {
|
||||
let matcher = Expression::from_syntax(matcher_node, source, context)?;
|
||||
|
||||
let mut options = Vec::new();
|
||||
let mut previous_expression = None;
|
||||
let mut previous_pattern = None;
|
||||
let mut next_statement_is_fallback = false;
|
||||
let mut fallback = None;
|
||||
|
||||
for index in 2..node.child_count() {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
if child.kind() == "*" {
|
||||
next_statement_is_fallback = true;
|
||||
}
|
||||
|
||||
if child.kind() == "expression" {
|
||||
previous_expression = Some(Expression::from_syntax(child, source, context)?);
|
||||
if child.kind() == "match_pattern" {
|
||||
previous_pattern = Some(MatchPattern::from_syntax(child, source, context)?);
|
||||
}
|
||||
|
||||
if child.kind() == "statement" {
|
||||
@ -46,7 +43,7 @@ impl AbstractTree for Match {
|
||||
if next_statement_is_fallback {
|
||||
fallback = Some(Box::new(statement));
|
||||
next_statement_is_fallback = false;
|
||||
} else if let Some(expression) = &previous_expression {
|
||||
} else if let Some(expression) = &previous_pattern {
|
||||
options.push((expression.clone(), statement));
|
||||
}
|
||||
}
|
||||
@ -83,10 +80,26 @@ impl AbstractTree for Match {
|
||||
fn run(&self, source: &str, context: &Context) -> Result<Value, RuntimeError> {
|
||||
let matcher_value = self.matcher.run(source, context)?;
|
||||
|
||||
for (expression, statement) in &self.options {
|
||||
let option_value = expression.run(source, context)?;
|
||||
for (pattern, statement) in &self.options {
|
||||
if let (Value::Enum(enum_instance), MatchPattern::EnumPattern(enum_pattern)) =
|
||||
(&matcher_value, pattern)
|
||||
{
|
||||
if enum_instance.name() == enum_pattern.name().inner()
|
||||
&& enum_instance.variant_name() == enum_pattern.variant().inner()
|
||||
{
|
||||
let statement_context = Context::with_variables_from(context)?;
|
||||
|
||||
if matcher_value == option_value {
|
||||
if let Some(identifier) = enum_pattern.inner_identifier() {
|
||||
statement_context
|
||||
.set_value(identifier.inner().clone(), enum_instance.value().clone())?;
|
||||
}
|
||||
|
||||
return statement.run(source, &statement_context);
|
||||
}
|
||||
}
|
||||
let pattern_value = pattern.run(source, context)?;
|
||||
|
||||
if matcher_value == pattern_value {
|
||||
return statement.run(source, context);
|
||||
}
|
||||
}
|
||||
|
65
src/abstract_tree/match_pattern.rs
Normal file
65
src/abstract_tree/match_pattern.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node as SyntaxNode;
|
||||
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, Context, EnumPattern, Format, Type, Value, ValueNode,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum MatchPattern {
|
||||
EnumPattern(EnumPattern),
|
||||
Value(ValueNode),
|
||||
Wildcard,
|
||||
}
|
||||
|
||||
impl AbstractTree for MatchPattern {
|
||||
fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> {
|
||||
SyntaxError::expect_syntax_node(source, "match_pattern", node)?;
|
||||
|
||||
let child = node.child(0).unwrap();
|
||||
let pattern = match child.kind() {
|
||||
"enum_pattern" => {
|
||||
MatchPattern::EnumPattern(EnumPattern::from_syntax(child, source, context)?)
|
||||
}
|
||||
"value" => MatchPattern::Value(ValueNode::from_syntax(child, source, context)?),
|
||||
"*" => MatchPattern::Wildcard,
|
||||
_ => {
|
||||
return Err(SyntaxError::UnexpectedSyntaxNode {
|
||||
expected: "enum pattern or value".to_string(),
|
||||
actual: child.kind().to_string(),
|
||||
location: child.start_position(),
|
||||
relevant_source: source[child.byte_range()].to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Ok(pattern)
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
match self {
|
||||
MatchPattern::EnumPattern(enum_pattern) => enum_pattern.expected_type(_context),
|
||||
MatchPattern::Value(value_node) => value_node.expected_type(_context),
|
||||
MatchPattern::Wildcard => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn validate(&self, _source: &str, _context: &Context) -> Result<(), ValidationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
match self {
|
||||
MatchPattern::EnumPattern(enum_pattern) => enum_pattern.run(_source, _context),
|
||||
MatchPattern::Value(value_node) => value_node.run(_source, _context),
|
||||
MatchPattern::Wildcard => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for MatchPattern {
|
||||
fn format(&self, _output: &mut String, _indent_level: u8) {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ pub mod assignment_operator;
|
||||
pub mod block;
|
||||
pub mod command;
|
||||
pub mod enum_defintion;
|
||||
pub mod enum_pattern;
|
||||
pub mod expression;
|
||||
pub mod r#for;
|
||||
pub mod function_call;
|
||||
@ -26,6 +27,7 @@ pub mod logic;
|
||||
pub mod logic_operator;
|
||||
pub mod map_node;
|
||||
pub mod r#match;
|
||||
pub mod match_pattern;
|
||||
pub mod math;
|
||||
pub mod math_operator;
|
||||
pub mod new;
|
||||
@ -38,12 +40,12 @@ pub mod value_node;
|
||||
pub mod r#while;
|
||||
|
||||
pub use {
|
||||
assignment::*, assignment_operator::*, block::*, command::*, enum_defintion::*, expression::*,
|
||||
function_call::*, function_expression::*, function_node::*, identifier::*, if_else::*,
|
||||
index::*, index_assignment::IndexAssignment, index_expression::*, logic::*, logic_operator::*,
|
||||
map_node::*, math::*, math_operator::*, new::*, r#as::*, r#for::*, r#match::*, r#type::*,
|
||||
r#while::*, statement::*, struct_definition::*, type_definition::*, type_specification::*,
|
||||
value_node::*,
|
||||
assignment::*, assignment_operator::*, block::*, command::*, enum_defintion::*,
|
||||
enum_pattern::*, expression::*, function_call::*, function_expression::*, function_node::*,
|
||||
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*,
|
||||
logic::*, logic_operator::*, map_node::*, match_pattern::*, math::*, math_operator::*, new::*,
|
||||
r#as::*, r#for::*, r#match::*, r#type::*, r#while::*, statement::*, struct_definition::*,
|
||||
type_definition::*, type_specification::*, value_node::*,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use crate::Value;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||
@ -15,4 +17,22 @@ impl EnumInstance {
|
||||
value: Box::new(value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn variant_name(&self) -> &String {
|
||||
&self.variant_name
|
||||
}
|
||||
|
||||
pub fn value(&self) -> &Value {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EnumInstance {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
write!(f, "{}::{}({})", self.name, self.variant_name, self.value)
|
||||
}
|
||||
}
|
||||
|
@ -560,7 +560,7 @@ impl Display for Value {
|
||||
Value::Function(function) => write!(f, "{function}"),
|
||||
Value::Struct(structure) => write!(f, "{structure}"),
|
||||
Value::Range(range) => write!(f, "{}..{}", range.start(), range.end()),
|
||||
Value::Enum(_) => todo!(),
|
||||
Value::Enum(enum_instance) => write!(f, "{enum_instance}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn r#match() {
|
||||
fn match_value() {
|
||||
let test = interpret(
|
||||
"
|
||||
match 1 {
|
||||
@ -32,3 +32,19 @@ fn match_assignment() {
|
||||
|
||||
assert_eq!(Value::Boolean(true), test);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_enum() {
|
||||
let result = interpret(
|
||||
"
|
||||
foobar = Option::Some(true)
|
||||
|
||||
match foobar {
|
||||
Option::None => false,
|
||||
Option::Some(content) => content,
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
assert_eq!(result, Ok(Value::Boolean(true)));
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ match x {
|
||||
(match
|
||||
(expression
|
||||
(identifier))
|
||||
(expression
|
||||
(match_pattern
|
||||
(value
|
||||
(integer)))
|
||||
(statement
|
||||
@ -25,10 +25,43 @@ match x {
|
||||
(expression
|
||||
(value
|
||||
(boolean))))))
|
||||
(expression
|
||||
(match_pattern
|
||||
(value
|
||||
(integer)))
|
||||
(statement
|
||||
(expression
|
||||
(value
|
||||
(boolean)))))))
|
||||
|
||||
================================================================================
|
||||
Match Enum
|
||||
================================================================================
|
||||
|
||||
match foobar {
|
||||
FooBar::Foo => true
|
||||
FooBar::Bar => false
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
(root
|
||||
(statement
|
||||
(match
|
||||
(expression
|
||||
(identifier))
|
||||
(match_pattern
|
||||
(enum_pattern
|
||||
(identifier)
|
||||
(identifier)))
|
||||
(statement
|
||||
(expression
|
||||
(value
|
||||
(boolean))))
|
||||
(match_pattern
|
||||
(enum_pattern
|
||||
(identifier)
|
||||
(identifier)))
|
||||
(statement
|
||||
(expression
|
||||
(value
|
||||
(boolean)))))))
|
||||
|
@ -290,7 +290,7 @@ module.exports = grammar({
|
||||
'{',
|
||||
repeat1(
|
||||
seq(
|
||||
choice($.expression, '*'),
|
||||
$.match_pattern,
|
||||
'=>',
|
||||
$.statement,
|
||||
optional(','),
|
||||
@ -300,6 +300,26 @@ module.exports = grammar({
|
||||
),
|
||||
),
|
||||
|
||||
match_pattern: $ =>
|
||||
choice(
|
||||
$.enum_pattern,
|
||||
$.value,
|
||||
'*',
|
||||
),
|
||||
|
||||
enum_pattern: $ =>
|
||||
prec(
|
||||
1,
|
||||
seq(
|
||||
$.identifier,
|
||||
'::',
|
||||
$.identifier,
|
||||
optional(
|
||||
seq('(', $.identifier, ')'),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
while: $ =>
|
||||
seq(
|
||||
'while',
|
||||
|
@ -855,19 +855,10 @@
|
||||
"type": "REPEAT1",
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "expression"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "*"
|
||||
}
|
||||
]
|
||||
"name": "match_pattern"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
@ -899,6 +890,69 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"match_pattern": {
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "enum_pattern"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "value"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"enum_pattern": {
|
||||
"type": "PREC",
|
||||
"value": 1,
|
||||
"content": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "::"
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
{
|
||||
"type": "CHOICE",
|
||||
"members": [
|
||||
{
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": "("
|
||||
},
|
||||
{
|
||||
"type": "SYMBOL",
|
||||
"name": "identifier"
|
||||
},
|
||||
{
|
||||
"type": "STRING",
|
||||
"value": ")"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "BLANK"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"while": {
|
||||
"type": "SEQ",
|
||||
"members": [
|
||||
|
@ -170,6 +170,21 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "enum_pattern",
|
||||
"named": true,
|
||||
"fields": {},
|
||||
"children": {
|
||||
"multiple": true,
|
||||
"required": true,
|
||||
"types": [
|
||||
{
|
||||
"type": "identifier",
|
||||
"named": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "expression",
|
||||
"named": true,
|
||||
@ -495,6 +510,10 @@
|
||||
"type": "expression",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "match_pattern",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "statement",
|
||||
"named": true
|
||||
@ -502,6 +521,25 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "match_pattern",
|
||||
"named": true,
|
||||
"fields": {},
|
||||
"children": {
|
||||
"multiple": false,
|
||||
"required": false,
|
||||
"types": [
|
||||
{
|
||||
"type": "enum_pattern",
|
||||
"named": true
|
||||
},
|
||||
{
|
||||
"type": "value",
|
||||
"named": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "math",
|
||||
"named": true,
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user