Implement conditional operators
This commit is contained in:
parent
9159c8222e
commit
4b52985992
|
@ -196,7 +196,7 @@ pub struct StringLiteral {
|
||||||
impl StringLiteral {
|
impl StringLiteral {
|
||||||
/// Returns the string without the leading and trailing double quotes.
|
/// Returns the string without the leading and trailing double quotes.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn string_content(&self) -> &str {
|
pub fn str_content(&self) -> &str {
|
||||||
let string = self.span.str();
|
let string = self.span.str();
|
||||||
&string[1..string.len() - 1]
|
&string[1..string.len() - 1]
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,6 +247,30 @@ impl<'a> Frame<'a> {
|
||||||
self.get_reading(self.token_provider.token_stream().get(self.current_index))
|
self.get_reading(self.token_provider.token_stream().get(self.current_index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the next significant [`Token`] after the `current_index` of the [`Frame`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn peek_significant(&self) -> Reading {
|
||||||
|
let mut index = self.current_index;
|
||||||
|
let token_stream = self.token_provider.token_stream();
|
||||||
|
while index < self.token_provider.token_stream().len() {
|
||||||
|
let token = self.get_reading(token_stream.get(index));
|
||||||
|
|
||||||
|
if !matches!(
|
||||||
|
token,
|
||||||
|
Reading::Atomic(Token::WhiteSpaces(..) | Token::Comment(..))
|
||||||
|
) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.token_provider {
|
||||||
|
TokenProvider::TokenStream(..) => Reading::Eof,
|
||||||
|
TokenProvider::Delimited(delimited) => Reading::DelimitedEnd(delimited.close.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a [`Token`] pointing by the `current_index` with the given index offset of the
|
/// Returns a [`Token`] pointing by the `current_index` with the given index offset of the
|
||||||
/// [`Frame`].
|
/// [`Frame`].
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,22 +1,127 @@
|
||||||
//! Syntax tree nodes for expressions.
|
//! Syntax tree nodes for expressions.
|
||||||
|
|
||||||
|
use std::{cmp::Ordering, collections::VecDeque};
|
||||||
|
|
||||||
|
use enum_as_inner::EnumAsInner;
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
Handler,
|
Dummy, Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Punctuation, StringLiteral, Token},
|
token::{Punctuation, StringLiteral, Token},
|
||||||
token_stream::Delimiter,
|
token_stream::Delimiter,
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::{
|
||||||
error::{Error, UnexpectedSyntax},
|
error::{Error, SyntaxKind, UnexpectedSyntax},
|
||||||
parser::{Parser, Reading},
|
parser::{Parser, Reading},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Expression:
|
||||||
|
/// Prefix
|
||||||
|
/// | Parenthesized
|
||||||
|
/// | StringLiteral
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
pub enum PrimaryCondition {
|
||||||
|
Prefix(ConditionalPrefix),
|
||||||
|
Parenthesized(ParenthesizedCondition),
|
||||||
|
StringLiteral(StringLiteral),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for PrimaryCondition {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::Prefix(prefix) => prefix.span(),
|
||||||
|
Self::Parenthesized(parenthesized) => parenthesized.span(),
|
||||||
|
Self::StringLiteral(literal) => literal.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// BinaryCondition:
|
||||||
|
/// Condition ConditionalBinaryOperator Condition
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct BinaryCondition {
|
||||||
|
/// The left operand of the binary condition.
|
||||||
|
#[get = "pub"]
|
||||||
|
left_operand: Box<Condition>,
|
||||||
|
/// The operator of the binary condition.
|
||||||
|
#[get = "pub"]
|
||||||
|
operator: ConditionalBinaryOperator,
|
||||||
|
/// The right operand of the binary condition.
|
||||||
|
#[get = "pub"]
|
||||||
|
right_operand: Box<Condition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for BinaryCondition {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.left_operand
|
||||||
|
.span()
|
||||||
|
.join(&self.right_operand.span())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinaryCondition {
|
||||||
|
/// Dissolves the binary condition into its components
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Condition, ConditionalBinaryOperator, Condition) {
|
||||||
|
(*self.left_operand, self.operator, *self.right_operand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// BinaryOperator:
|
||||||
|
/// '&&'
|
||||||
|
/// | '||'
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum ConditionalBinaryOperator {
|
||||||
|
LogicalAnd(Punctuation, Punctuation),
|
||||||
|
LogicalOr(Punctuation, Punctuation),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConditionalBinaryOperator {
|
||||||
|
/// Gets the precedence of the operator (the higher the number, the first it will be evaluated)
|
||||||
|
///
|
||||||
|
/// The least operator has precedence 1.
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_precedence(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Self::LogicalOr(..) => 1,
|
||||||
|
Self::LogicalAnd(..) => 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ConditionalBinaryOperator {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::LogicalAnd(a, b) | Self::LogicalOr(a, b) => a
|
||||||
|
.span
|
||||||
|
.join(&b.span)
|
||||||
|
.expect("Invalid tokens for ConditionalBinaryOperator"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
|
@ -56,32 +161,178 @@ impl SourceElement for ParenthesizedCondition {
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// Condition: StringLiteral;
|
/// PrefixOperator: '!';
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
pub enum ConditionalPrefixOperator {
|
||||||
|
LogicalNot(Punctuation),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ConditionalPrefixOperator {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::LogicalNot(token) => token.span.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// Prefix:
|
||||||
|
/// ConditionalPrefixOperator StringLiteral
|
||||||
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
pub struct Condition {
|
pub struct ConditionalPrefix {
|
||||||
/// The value of the condition.
|
/// The operator of the prefix.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
pub value: StringLiteral,
|
operator: ConditionalPrefixOperator,
|
||||||
|
/// The operand of the prefix.
|
||||||
|
#[get = "pub"]
|
||||||
|
operand: Box<PrimaryCondition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ConditionalPrefix {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.operator.span().join(&self.operand.span()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ConditionalPrefix {
|
||||||
|
/// Dissolves the conditional prefix into its components
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (ConditionalPrefixOperator, PrimaryCondition) {
|
||||||
|
(self.operator, *self.operand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Condition: PrimaryCondition;
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
pub enum Condition {
|
||||||
|
Primary(PrimaryCondition),
|
||||||
|
Binary(BinaryCondition),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for Condition {
|
impl SourceElement for Condition {
|
||||||
fn span(&self) -> Span {
|
fn span(&self) -> Span {
|
||||||
self.value.span()
|
match self {
|
||||||
|
Self::Primary(primary) => primary.span(),
|
||||||
|
Self::Binary(binary) => binary.span(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses a [`Condition`].
|
/// Parses a [`Condition`].
|
||||||
pub fn parse_condition(&mut self, handler: &impl Handler<Error>) -> Option<Condition> {
|
pub fn parse_condition(&mut self, handler: &impl Handler<Error>) -> Option<Condition> {
|
||||||
match self.next_significant_token() {
|
let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?);
|
||||||
Reading::Atomic(Token::StringLiteral(s)) => Some(Condition { value: s }),
|
let mut expressions = VecDeque::new();
|
||||||
|
|
||||||
|
// Parses a list of binary operators and expressions
|
||||||
|
while let Some(binary_operator) = self.try_parse_conditional_binary_operator() {
|
||||||
|
expressions.push_back((
|
||||||
|
binary_operator,
|
||||||
|
Some(Condition::Primary(self.parse_primary_condition(handler)?)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut candidate_index = 0;
|
||||||
|
let mut current_precedence;
|
||||||
|
|
||||||
|
while !expressions.is_empty() {
|
||||||
|
// reset precedence
|
||||||
|
current_precedence = 0;
|
||||||
|
|
||||||
|
for (index, (binary_op, _)) in expressions.iter().enumerate() {
|
||||||
|
let new_precedence = binary_op.get_precedence();
|
||||||
|
match new_precedence.cmp(¤t_precedence) {
|
||||||
|
// Clear the candidate indices and set the current precedence to the
|
||||||
|
// precedence of the current binary operator.
|
||||||
|
Ordering::Greater => {
|
||||||
|
current_precedence = new_precedence;
|
||||||
|
candidate_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ordering::Less | Ordering::Equal => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSUMPTION: The assignments have 1 precedence and are right associative.
|
||||||
|
assert!(current_precedence > 0);
|
||||||
|
|
||||||
|
if candidate_index == 0 {
|
||||||
|
let (binary_op, rhs) = expressions.pop_front().expect("No binary operator found");
|
||||||
|
|
||||||
|
// fold the first expression
|
||||||
|
lhs = Condition::Binary(BinaryCondition {
|
||||||
|
left_operand: Box::new(lhs),
|
||||||
|
operator: binary_op,
|
||||||
|
right_operand: Box::new(rhs.unwrap()),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let (binary_op, rhs) = expressions
|
||||||
|
.remove(candidate_index)
|
||||||
|
.expect("No binary operator found");
|
||||||
|
|
||||||
|
// fold the expression at candidate_index
|
||||||
|
expressions[candidate_index - 1].1 = Some(Condition::Binary(BinaryCondition {
|
||||||
|
left_operand: Box::new(expressions[candidate_index - 1].1.take().unwrap()),
|
||||||
|
operator: binary_op,
|
||||||
|
right_operand: Box::new(rhs.unwrap()),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(lhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a [`PrimaryCondition`].
|
||||||
|
pub fn parse_primary_condition(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<Error>,
|
||||||
|
) -> Option<PrimaryCondition> {
|
||||||
|
match self.stop_at_significant() {
|
||||||
|
// prefixed expression
|
||||||
|
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '!' => {
|
||||||
|
// eat prefix operator
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
let operator = match punc.punctuation {
|
||||||
|
'!' => ConditionalPrefixOperator::LogicalNot(punc),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let operand = Box::new(self.parse_primary_condition(handler)?);
|
||||||
|
|
||||||
|
Some(PrimaryCondition::Prefix(ConditionalPrefix {
|
||||||
|
operator,
|
||||||
|
operand,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// string literal
|
||||||
|
Reading::Atomic(Token::StringLiteral(literal)) => {
|
||||||
|
self.forward();
|
||||||
|
Some(PrimaryCondition::StringLiteral(literal))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parenthesized condition
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '(' => self
|
||||||
|
.parse_parenthesized_condition(handler)
|
||||||
|
.map(PrimaryCondition::Parenthesized),
|
||||||
|
|
||||||
unexpected => {
|
unexpected => {
|
||||||
// make progress
|
// make progress
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
||||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
expected: crate::syntax::error::SyntaxKind::Expression,
|
expected: SyntaxKind::Expression,
|
||||||
found: unexpected.into_token(),
|
found: unexpected.into_token(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -107,4 +358,21 @@ impl<'a> Parser<'a> {
|
||||||
close_paren: token_tree.close,
|
close_paren: token_tree.close,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_parse_conditional_binary_operator(&mut self) -> Option<ConditionalBinaryOperator> {
|
||||||
|
self.try_parse(|parser| match parser.next_significant_token() {
|
||||||
|
Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation {
|
||||||
|
'&' => {
|
||||||
|
let b = parser.parse_punctuation('&', false, &Dummy)?;
|
||||||
|
Some(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
let b = parser.parse_punctuation('|', false, &Dummy)?;
|
||||||
|
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
//! Conversion functions for converting between tokens/ast-nodes and [`shulkerbox`] types
|
||||||
|
|
||||||
|
use shulkerbox::datapack::Condition as DpCondition;
|
||||||
|
|
||||||
|
use crate::syntax::syntax_tree::expression::{
|
||||||
|
BinaryCondition, Condition, ConditionalBinaryOperator, ConditionalPrefixOperator,
|
||||||
|
PrimaryCondition,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl From<Condition> for DpCondition {
|
||||||
|
fn from(value: Condition) -> Self {
|
||||||
|
match value {
|
||||||
|
Condition::Primary(primary) => primary.into(),
|
||||||
|
Condition::Binary(binary) => binary.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PrimaryCondition> for DpCondition {
|
||||||
|
fn from(value: PrimaryCondition) -> Self {
|
||||||
|
match value {
|
||||||
|
PrimaryCondition::StringLiteral(literal) => {
|
||||||
|
Self::Atom(literal.str_content().to_string())
|
||||||
|
}
|
||||||
|
PrimaryCondition::Parenthesized(cond) => cond.dissolve().1.into(),
|
||||||
|
PrimaryCondition::Prefix(prefix) => match prefix.operator() {
|
||||||
|
ConditionalPrefixOperator::LogicalNot(_) => {
|
||||||
|
Self::Not(Box::new(prefix.dissolve().1.into()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BinaryCondition> for DpCondition {
|
||||||
|
fn from(value: BinaryCondition) -> Self {
|
||||||
|
let (lhs, op, rhs) = value.dissolve();
|
||||||
|
match op {
|
||||||
|
ConditionalBinaryOperator::LogicalAnd(_, _) => {
|
||||||
|
Self::And(Box::new(lhs.into()), Box::new(rhs.into()))
|
||||||
|
}
|
||||||
|
ConditionalBinaryOperator::LogicalOr(_, _) => {
|
||||||
|
Self::Or(Box::new(lhs.into()), Box::new(rhs.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
//! The transpile module is responsible for transpiling the abstract syntax tree into a data pack.
|
//! The transpile module is responsible for transpiling the abstract syntax tree into a data pack.
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod conversions;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod transpiler;
|
pub mod transpiler;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use shulkerbox::datapack::{Command, Datapack, Execute};
|
use shulkerbox::datapack::{self, Command, Datapack, Execute};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{source_file::SourceElement, Handler},
|
base::{source_file::SourceElement, Handler},
|
||||||
|
@ -19,7 +19,7 @@ pub struct Transpiler {
|
||||||
type AnnotationMap = HashMap<String, Option<String>>;
|
type AnnotationMap = HashMap<String, Option<String>>;
|
||||||
|
|
||||||
impl Transpiler {
|
impl Transpiler {
|
||||||
/// Creates a new compiler.
|
/// Creates a new transpiler.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -27,7 +27,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compiles the given program.
|
/// Transpiles the given program.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - [`TranspileError::MissingMainFunction`] If the main function is missing.
|
/// - [`TranspileError::MissingMainFunction`] If the main function is missing.
|
||||||
|
@ -49,9 +49,7 @@ impl Transpiler {
|
||||||
let value = annotation.value();
|
let value = annotation.value();
|
||||||
(
|
(
|
||||||
key.span().str().to_string(),
|
key.span().str().to_string(),
|
||||||
value
|
value.as_ref().map(|(_, ref v)| v.str_content().to_string()),
|
||||||
.as_ref()
|
|
||||||
.map(|(_, ref v)| v.string_content().to_string()),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -122,7 +120,7 @@ fn compile_statement(statement: &Statement) -> Option<Command> {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Command::Execute(Execute::If(
|
Some(Command::Execute(Execute::If(
|
||||||
cond.value().string_content().into(),
|
datapack::Condition::from(cond),
|
||||||
Box::new(Execute::Runs(Vec::new())),
|
Box::new(Execute::Runs(Vec::new())),
|
||||||
el,
|
el,
|
||||||
)))
|
)))
|
||||||
|
@ -136,7 +134,7 @@ fn compile_statement(statement: &Statement) -> Option<Command> {
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(Command::Execute(Execute::If(
|
Some(Command::Execute(Execute::If(
|
||||||
cond.value().string_content().into(),
|
datapack::Condition::from(cond),
|
||||||
Box::new(run),
|
Box::new(run),
|
||||||
el,
|
el,
|
||||||
)))
|
)))
|
||||||
|
|
Loading…
Reference in New Issue