Compare commits
No commits in common. "9c54dee454bf03c44c2f33b5e9af37adc0d5c23f" and "58998b424629f1e134e103b8b6f6431368b68e4f" have entirely different histories.
9c54dee454
...
58998b4246
|
@ -176,8 +176,9 @@ where
|
||||||
|
|
||||||
tracing::info!("Transpiling the source code.");
|
tracing::info!("Transpiling the source code.");
|
||||||
|
|
||||||
let datapack =
|
let mut transpiler = Transpiler::new(main_namespace_name, pack_format);
|
||||||
Transpiler::new(main_namespace_name, pack_format).transpile(&programs, handler)?;
|
transpiler.transpile(&programs, handler)?;
|
||||||
|
let datapack = transpiler.into_datapack();
|
||||||
|
|
||||||
if handler.has_received() {
|
if handler.has_received() {
|
||||||
return Err(Error::other(
|
return Err(Error::other(
|
||||||
|
|
|
@ -13,8 +13,11 @@ use crate::{
|
||||||
base::{self, source_file::SourceElement as _, Handler},
|
base::{self, source_file::SourceElement as _, Handler},
|
||||||
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
|
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
|
||||||
syntax::syntax_tree::{
|
syntax::syntax_tree::{
|
||||||
|
condition::{
|
||||||
|
BinaryCondition, Condition, ParenthesizedCondition, PrimaryCondition, UnaryCondition,
|
||||||
|
},
|
||||||
declaration::{Declaration, Function, ImportItems},
|
declaration::{Declaration, Function, ImportItems},
|
||||||
expression::{Expression, FunctionCall, Parenthesized, Primary},
|
expression::{Expression, FunctionCall, Primary},
|
||||||
program::{Namespace, ProgramFile},
|
program::{Namespace, ProgramFile},
|
||||||
statement::{
|
statement::{
|
||||||
execute_block::{
|
execute_block::{
|
||||||
|
@ -357,7 +360,7 @@ impl Conditional {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parenthesized {
|
impl ParenthesizedCondition {
|
||||||
/// Analyzes the semantics of the parenthesized condition.
|
/// Analyzes the semantics of the parenthesized condition.
|
||||||
pub fn analyze_semantics(
|
pub fn analyze_semantics(
|
||||||
&self,
|
&self,
|
||||||
|
@ -365,11 +368,26 @@ impl Parenthesized {
|
||||||
macro_names: &HashSet<String>,
|
macro_names: &HashSet<String>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<(), error::Error> {
|
) -> Result<(), error::Error> {
|
||||||
self.expression()
|
self.condition
|
||||||
.analyze_semantics(function_names, macro_names, handler)
|
.analyze_semantics(function_names, macro_names, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Condition {
|
||||||
|
/// Analyzes the semantics of the condition.
|
||||||
|
pub fn analyze_semantics(
|
||||||
|
&self,
|
||||||
|
function_names: &HashSet<String>,
|
||||||
|
macro_names: &HashSet<String>,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Result<(), error::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Primary(prim) => prim.analyze_semantics(function_names, macro_names, handler),
|
||||||
|
Self::Binary(bin) => bin.analyze_semantics(function_names, macro_names, handler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Else {
|
impl Else {
|
||||||
/// Analyzes the semantics of the else block.
|
/// Analyzes the semantics of the else block.
|
||||||
pub fn analyze_semantics(
|
pub fn analyze_semantics(
|
||||||
|
@ -600,3 +618,62 @@ impl VariableDeclaration {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PrimaryCondition {
|
||||||
|
/// Analyzes the semantics of a primary condition.
|
||||||
|
pub fn analyze_semantics(
|
||||||
|
&self,
|
||||||
|
function_names: &HashSet<String>,
|
||||||
|
macro_names: &HashSet<String>,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Result<(), error::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Parenthesized(paren) => {
|
||||||
|
paren.analyze_semantics(function_names, macro_names, handler)
|
||||||
|
}
|
||||||
|
Self::StringLiteral(_) => Ok(()),
|
||||||
|
Self::Unary(unary) => unary.analyze_semantics(function_names, macro_names, handler),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnaryCondition {
|
||||||
|
/// Analyzes the semantics of an unary condition.
|
||||||
|
pub fn analyze_semantics(
|
||||||
|
&self,
|
||||||
|
function_names: &HashSet<String>,
|
||||||
|
macro_names: &HashSet<String>,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Result<(), error::Error> {
|
||||||
|
self.operand()
|
||||||
|
.analyze_semantics(function_names, macro_names, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinaryCondition {
|
||||||
|
/// Analyzes the semantics of a binary condition.
|
||||||
|
pub fn analyze_semantics(
|
||||||
|
&self,
|
||||||
|
function_names: &HashSet<String>,
|
||||||
|
macro_names: &HashSet<String>,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Result<(), error::Error> {
|
||||||
|
let a = self
|
||||||
|
.left_operand()
|
||||||
|
.analyze_semantics(function_names, macro_names, handler)
|
||||||
|
.inspect_err(|err| {
|
||||||
|
handler.receive(err.clone());
|
||||||
|
});
|
||||||
|
let b = self
|
||||||
|
.right_operand()
|
||||||
|
.analyze_semantics(function_names, macro_names, handler)
|
||||||
|
.inspect_err(|err| {
|
||||||
|
handler.receive(err.clone());
|
||||||
|
});
|
||||||
|
if a.is_err() {
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,451 @@
|
||||||
|
//! Syntax tree nodes for conditions.
|
||||||
|
|
||||||
|
#![allow(clippy::missing_errors_doc)]
|
||||||
|
|
||||||
|
use std::{cmp::Ordering, collections::VecDeque};
|
||||||
|
|
||||||
|
use enum_as_inner::EnumAsInner;
|
||||||
|
use getset::Getters;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
base::{
|
||||||
|
self,
|
||||||
|
source_file::{SourceElement, Span},
|
||||||
|
Handler, VoidHandler,
|
||||||
|
},
|
||||||
|
lexical::{
|
||||||
|
token::{Punctuation, Token},
|
||||||
|
token_stream::Delimiter,
|
||||||
|
},
|
||||||
|
syntax::{
|
||||||
|
error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||||
|
parser::{Parser, Reading},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::AnyStringLiteral;
|
||||||
|
|
||||||
|
/// Condition that is viewed as a single entity during precedence parsing.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// PrimaryCondition:
|
||||||
|
/// UnaryCondition
|
||||||
|
/// | ParenthesizedCondition
|
||||||
|
/// | AnyStringLiteral
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
pub enum PrimaryCondition {
|
||||||
|
Unary(UnaryCondition),
|
||||||
|
Parenthesized(ParenthesizedCondition),
|
||||||
|
StringLiteral(AnyStringLiteral),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for PrimaryCondition {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::Unary(unary) => unary.span(),
|
||||||
|
Self::Parenthesized(parenthesized) => parenthesized.span(),
|
||||||
|
Self::StringLiteral(literal) => literal.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Condition that is composed of two conditions and a binary operator.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// BinaryCondition:
|
||||||
|
/// Condition ConditionalBinaryOperator Condition
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Operator that is used to combine two conditions.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// ConditionalBinaryOperator:
|
||||||
|
/// '&&'
|
||||||
|
/// | '||'
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Condition that is enclosed in parentheses.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// ParenthesizedCondition:
|
||||||
|
/// '(' Condition ')';
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct ParenthesizedCondition {
|
||||||
|
/// The opening parenthesis.
|
||||||
|
#[get = "pub"]
|
||||||
|
pub open_paren: Punctuation,
|
||||||
|
/// The condition within the parenthesis.
|
||||||
|
#[get = "pub"]
|
||||||
|
pub condition: Box<Condition>,
|
||||||
|
/// The closing parenthesis.
|
||||||
|
#[get = "pub"]
|
||||||
|
pub close_paren: Punctuation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParenthesizedCondition {
|
||||||
|
/// Dissolves the parenthesized condition into its components
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Punctuation, Condition, Punctuation) {
|
||||||
|
(self.open_paren, *self.condition, self.close_paren)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ParenthesizedCondition {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.open_paren
|
||||||
|
.span()
|
||||||
|
.join(&self.close_paren.span())
|
||||||
|
.expect("The span of the parenthesis is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Operator that is used to prefix a condition.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// ConditionalPrefixOperator: '!';
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Condition that is prefixed by an operator.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// UnaryCondition:
|
||||||
|
/// ConditionalPrefixOperator PrimaryCondition
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct UnaryCondition {
|
||||||
|
/// The operator of the prefix.
|
||||||
|
#[get = "pub"]
|
||||||
|
operator: ConditionalPrefixOperator,
|
||||||
|
/// The operand of the prefix.
|
||||||
|
#[get = "pub"]
|
||||||
|
operand: Box<PrimaryCondition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for UnaryCondition {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.operator.span().join(&self.operand.span()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl UnaryCondition {
|
||||||
|
/// Dissolves the conditional prefix into its components
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (ConditionalPrefixOperator, PrimaryCondition) {
|
||||||
|
(self.operator, *self.operand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a condition in the syntax tree.
|
||||||
|
///
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Condition:
|
||||||
|
/// PrimaryCondition
|
||||||
|
/// | BinaryCondition
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
pub enum Condition {
|
||||||
|
Primary(PrimaryCondition),
|
||||||
|
Binary(BinaryCondition),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for Condition {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::Primary(primary) => primary.span(),
|
||||||
|
Self::Binary(binary) => binary.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Parses a [`Condition`].
|
||||||
|
///
|
||||||
|
/// # Precedence of the operators
|
||||||
|
/// 1. `!`
|
||||||
|
/// 2. `&&`
|
||||||
|
/// 3. `||`
|
||||||
|
pub fn parse_condition(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> ParseResult<Condition> {
|
||||||
|
let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?);
|
||||||
|
let mut expressions = VecDeque::new();
|
||||||
|
|
||||||
|
// Parses a list of binary operators and expressions
|
||||||
|
while let Ok(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()),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(lhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a [`PrimaryCondition`].
|
||||||
|
pub fn parse_primary_condition(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> ParseResult<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)?);
|
||||||
|
|
||||||
|
Ok(PrimaryCondition::Unary(UnaryCondition {
|
||||||
|
operator,
|
||||||
|
operand,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// string literal
|
||||||
|
Reading::Atomic(Token::StringLiteral(literal)) => {
|
||||||
|
self.forward();
|
||||||
|
Ok(PrimaryCondition::StringLiteral(literal.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// macro string literal
|
||||||
|
Reading::Atomic(Token::MacroStringLiteral(literal)) => {
|
||||||
|
self.forward();
|
||||||
|
Ok(PrimaryCondition::StringLiteral(literal.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parenthesized condition
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '(' => self
|
||||||
|
.parse_parenthesized_condition(handler)
|
||||||
|
.map(PrimaryCondition::Parenthesized),
|
||||||
|
|
||||||
|
unexpected => {
|
||||||
|
// make progress
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::Either(&[
|
||||||
|
SyntaxKind::Punctuation('!'),
|
||||||
|
SyntaxKind::StringLiteral,
|
||||||
|
SyntaxKind::Punctuation('('),
|
||||||
|
]),
|
||||||
|
found: unexpected.into_token(),
|
||||||
|
});
|
||||||
|
handler.receive(err.clone());
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a [`ParenthesizedCondition`].
|
||||||
|
pub fn parse_parenthesized_condition(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> ParseResult<ParenthesizedCondition> {
|
||||||
|
let token_tree = self.step_into(
|
||||||
|
Delimiter::Parenthesis,
|
||||||
|
|parser| {
|
||||||
|
let cond = parser.parse_condition(handler)?;
|
||||||
|
parser.stop_at_significant();
|
||||||
|
Ok(cond)
|
||||||
|
},
|
||||||
|
handler,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(ParenthesizedCondition {
|
||||||
|
open_paren: token_tree.open,
|
||||||
|
condition: Box::new(token_tree.tree?),
|
||||||
|
close_paren: token_tree.close,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_parse_conditional_binary_operator(&mut self) -> ParseResult<ConditionalBinaryOperator> {
|
||||||
|
self.try_parse(|parser| match parser.next_significant_token() {
|
||||||
|
Reading::Atomic(token) => match token.clone() {
|
||||||
|
Token::Punctuation(punc) => match punc.punctuation {
|
||||||
|
'&' => {
|
||||||
|
let b = parser.parse_punctuation('&', false, &VoidHandler)?;
|
||||||
|
Ok(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
let b = parser.parse_punctuation('|', false, &VoidHandler)?;
|
||||||
|
Ok(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||||
|
}
|
||||||
|
_ => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::Either(&[
|
||||||
|
SyntaxKind::Punctuation('&'),
|
||||||
|
SyntaxKind::Punctuation('|'),
|
||||||
|
]),
|
||||||
|
found: Some(token),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
unexpected => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::Either(&[
|
||||||
|
SyntaxKind::Punctuation('&'),
|
||||||
|
SyntaxKind::Punctuation('|'),
|
||||||
|
]),
|
||||||
|
found: Some(unexpected),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
unexpected => Err(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::Either(&[
|
||||||
|
SyntaxKind::Punctuation('&'),
|
||||||
|
SyntaxKind::Punctuation('|'),
|
||||||
|
]),
|
||||||
|
found: unexpected.into_token(),
|
||||||
|
})),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -620,7 +620,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parse_parenthesized(
|
fn parse_parenthesized(
|
||||||
&mut self,
|
&mut self,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> ParseResult<Parenthesized> {
|
) -> ParseResult<Parenthesized> {
|
||||||
|
|
|
@ -23,6 +23,7 @@ use super::{
|
||||||
parser::Parser,
|
parser::Parser,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod condition;
|
||||||
pub mod declaration;
|
pub mod declaration;
|
||||||
pub mod expression;
|
pub mod expression;
|
||||||
pub mod program;
|
pub mod program;
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
syntax::{
|
syntax::{
|
||||||
error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
|
error::{Error, ParseResult, SyntaxKind, UnexpectedSyntax},
|
||||||
parser::{DelimitedTree, Parser, Reading},
|
parser::{DelimitedTree, Parser, Reading},
|
||||||
syntax_tree::{expression::Parenthesized, AnyStringLiteral},
|
syntax_tree::{condition::ParenthesizedCondition, AnyStringLiteral},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ impl SourceElement for ExecuteBlockTail {
|
||||||
///
|
///
|
||||||
/// ``` ebnf
|
/// ``` ebnf
|
||||||
/// Conditional:
|
/// Conditional:
|
||||||
/// 'if' Parenthized
|
/// 'if' ParenthizedCondition
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -159,13 +159,13 @@ pub struct Conditional {
|
||||||
if_keyword: Keyword,
|
if_keyword: Keyword,
|
||||||
/// The condition of the conditional.
|
/// The condition of the conditional.
|
||||||
#[get = "pub"]
|
#[get = "pub"]
|
||||||
condition: Parenthesized,
|
condition: ParenthesizedCondition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Conditional {
|
impl Conditional {
|
||||||
/// Dissolves the [`Conditional`] into its components.
|
/// Dissolves the [`Conditional`] into its components.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn dissolve(self) -> (Keyword, Parenthesized) {
|
pub fn dissolve(self) -> (Keyword, ParenthesizedCondition) {
|
||||||
(self.if_keyword, self.condition)
|
(self.if_keyword, self.condition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -773,7 +773,7 @@ impl<'a> Parser<'a> {
|
||||||
// eat the if keyword
|
// eat the if keyword
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
||||||
let condition = self.parse_parenthesized(handler)?;
|
let condition = self.parse_parenthesized_condition(handler)?;
|
||||||
|
|
||||||
let conditional = Conditional {
|
let conditional = Conditional {
|
||||||
if_keyword,
|
if_keyword,
|
||||||
|
|
|
@ -1,12 +1,44 @@
|
||||||
//! Conversion functions for converting between tokens/ast-nodes and [`shulkerbox`] types
|
//! Conversion functions for converting between tokens/ast-nodes and [`shulkerbox`] types
|
||||||
|
|
||||||
use shulkerbox::util::{MacroString, MacroStringPart};
|
use shulkerbox::{
|
||||||
|
datapack::Condition as DpCondition,
|
||||||
|
util::{MacroString, MacroStringPart},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
|
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
|
||||||
syntax::syntax_tree::AnyStringLiteral,
|
syntax::syntax_tree::{
|
||||||
|
condition::{
|
||||||
|
BinaryCondition, Condition, ConditionalBinaryOperator, ConditionalPrefixOperator,
|
||||||
|
PrimaryCondition,
|
||||||
|
},
|
||||||
|
AnyStringLiteral,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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.into()),
|
||||||
|
PrimaryCondition::Parenthesized(cond) => cond.dissolve().1.into(),
|
||||||
|
PrimaryCondition::Unary(prefix) => match prefix.operator() {
|
||||||
|
ConditionalPrefixOperator::LogicalNot(_) => {
|
||||||
|
Self::Not(Box::new(prefix.dissolve().1.into()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&AnyStringLiteral> for MacroString {
|
impl From<&AnyStringLiteral> for MacroString {
|
||||||
fn from(value: &AnyStringLiteral) -> Self {
|
fn from(value: &AnyStringLiteral) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
@ -56,3 +88,17 @@ impl From<MacroStringLiteral> for MacroString {
|
||||||
Self::from(&value)
|
Self::from(&value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ use std::{fmt::Display, sync::Arc};
|
||||||
use super::{Scope, VariableData};
|
use super::{Scope, VariableData};
|
||||||
use crate::{
|
use crate::{
|
||||||
base::VoidHandler,
|
base::VoidHandler,
|
||||||
lexical::token::MacroStringLiteralPart,
|
|
||||||
syntax::syntax_tree::expression::{
|
syntax::syntax_tree::expression::{
|
||||||
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use derive_more::From;
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use shulkerbox::prelude::{Command, Condition, Execute};
|
use shulkerbox::prelude::{Command, Condition, Execute};
|
||||||
|
|
||||||
|
@ -28,12 +29,11 @@ use crate::{
|
||||||
|
|
||||||
/// Compile-time evaluated value
|
/// Compile-time evaluated value
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, From)]
|
||||||
pub enum ComptimeValue {
|
pub enum ComptimeValue {
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
String(String),
|
String(String),
|
||||||
MacroString(String),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ComptimeValue {
|
impl Display for ComptimeValue {
|
||||||
|
@ -42,20 +42,6 @@ impl Display for ComptimeValue {
|
||||||
Self::Boolean(boolean) => write!(f, "{boolean}"),
|
Self::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
Self::Integer(int) => write!(f, "{int}"),
|
Self::Integer(int) => write!(f, "{int}"),
|
||||||
Self::String(string) => write!(f, "{string}"),
|
Self::String(string) => write!(f, "{string}"),
|
||||||
Self::MacroString(macro_string) => write!(f, "{macro_string}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ComptimeValue {
|
|
||||||
/// Returns the value as a string not containing a macro.
|
|
||||||
#[must_use]
|
|
||||||
pub fn to_string_no_macro(&self) -> Option<String> {
|
|
||||||
match self {
|
|
||||||
Self::Boolean(boolean) => Some(boolean.to_string()),
|
|
||||||
Self::Integer(int) => Some(int.to_string()),
|
|
||||||
Self::String(string) => Some(string.clone()),
|
|
||||||
Self::MacroString(_) => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,17 +50,19 @@ impl ComptimeValue {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
pub enum ValueType {
|
pub enum ValueType {
|
||||||
Boolean,
|
ScoreboardValue,
|
||||||
Integer,
|
Tag,
|
||||||
String,
|
NumberStorage,
|
||||||
|
BooleanStorage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ValueType {
|
impl Display for ValueType {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Boolean => write!(f, "boolean"),
|
Self::ScoreboardValue => write!(f, "scoreboard value"),
|
||||||
Self::Integer => write!(f, "integer"),
|
Self::Tag => write!(f, "tag"),
|
||||||
Self::String => write!(f, "string"),
|
Self::BooleanStorage => write!(f, "boolean storage"),
|
||||||
|
Self::NumberStorage => write!(f, "number storage"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,22 +86,6 @@ pub enum DataLocation {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataLocation {
|
|
||||||
/// Returns the type of the data location.
|
|
||||||
#[must_use]
|
|
||||||
pub fn value_type(&self) -> ValueType {
|
|
||||||
match self {
|
|
||||||
Self::ScoreboardValue { .. } => ValueType::Integer,
|
|
||||||
Self::Tag { .. } => ValueType::Boolean,
|
|
||||||
Self::Storage { r#type, .. } => match r#type {
|
|
||||||
StorageType::Boolean => ValueType::Boolean,
|
|
||||||
StorageType::Byte | StorageType::Int | StorageType::Long => ValueType::Integer,
|
|
||||||
StorageType::Double => todo!("Double storage type"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of a storage.
|
/// The type of a storage.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
|
||||||
|
@ -161,10 +133,10 @@ impl Expression {
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
|
pub fn comptime_eval(&self) -> Option<ComptimeValue> {
|
||||||
match self {
|
match self {
|
||||||
Self::Primary(primary) => primary.comptime_eval(scope),
|
Self::Primary(primary) => primary.comptime_eval(),
|
||||||
Self::Binary(binary) => binary.comptime_eval(scope),
|
Self::Binary(binary) => binary.comptime_eval(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,38 +146,30 @@ impl Primary {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn can_yield_type(&self, r#type: ValueType, scope: &Arc<Scope>) -> bool {
|
pub fn can_yield_type(&self, r#type: ValueType, scope: &Arc<Scope>) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Boolean(_) => matches!(r#type, ValueType::Boolean),
|
Self::Boolean(_) => matches!(r#type, ValueType::Tag | ValueType::BooleanStorage),
|
||||||
Self::Integer(_) => matches!(r#type, ValueType::Integer),
|
Self::Integer(_) => matches!(r#type, ValueType::ScoreboardValue),
|
||||||
Self::FunctionCall(_) => matches!(r#type, ValueType::Integer | ValueType::Boolean),
|
Self::FunctionCall(_) => matches!(
|
||||||
|
r#type,
|
||||||
|
ValueType::ScoreboardValue | ValueType::Tag | ValueType::BooleanStorage
|
||||||
|
),
|
||||||
Self::Identifier(ident) => {
|
Self::Identifier(ident) => {
|
||||||
scope
|
scope
|
||||||
.get_variable(ident.span.str())
|
.get_variable(ident.span.str())
|
||||||
.map_or(false, |variable| match r#type {
|
.map_or(false, |variable| match r#type {
|
||||||
ValueType::Boolean => {
|
ValueType::BooleanStorage => {
|
||||||
matches!(
|
matches!(variable.as_ref(), VariableData::BooleanStorage { .. })
|
||||||
variable.as_ref(),
|
|
||||||
VariableData::Tag { .. } | VariableData::BooleanStorage { .. }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ValueType::Integer => {
|
ValueType::NumberStorage => false,
|
||||||
|
ValueType::ScoreboardValue => {
|
||||||
matches!(variable.as_ref(), VariableData::ScoreboardValue { .. })
|
matches!(variable.as_ref(), VariableData::ScoreboardValue { .. })
|
||||||
}
|
}
|
||||||
ValueType::String => false,
|
ValueType::Tag => matches!(variable.as_ref(), VariableData::Tag { .. }),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Self::Parenthesized(parenthesized) => {
|
Self::Parenthesized(parenthesized) => {
|
||||||
parenthesized.expression().can_yield_type(r#type, scope)
|
parenthesized.expression().can_yield_type(r#type, scope)
|
||||||
}
|
}
|
||||||
Self::Prefix(prefix) => match prefix.operator() {
|
Self::Prefix(_) => todo!(),
|
||||||
PrefixOperator::LogicalNot(_) => {
|
|
||||||
matches!(r#type, ValueType::Boolean)
|
|
||||||
&& prefix.operand().can_yield_type(r#type, scope)
|
|
||||||
}
|
|
||||||
PrefixOperator::Negate(_) => {
|
|
||||||
matches!(r#type, ValueType::Integer)
|
|
||||||
&& prefix.operand().can_yield_type(r#type, scope)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// TODO: Add support for Lua.
|
// TODO: Add support for Lua.
|
||||||
#[expect(clippy::match_same_arms)]
|
#[expect(clippy::match_same_arms)]
|
||||||
Self::Lua(_) => false,
|
Self::Lua(_) => false,
|
||||||
|
@ -215,26 +179,30 @@ impl Primary {
|
||||||
|
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
|
pub fn comptime_eval(&self) -> Option<ComptimeValue> {
|
||||||
|
#[expect(clippy::match_same_arms)]
|
||||||
match self {
|
match self {
|
||||||
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
|
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
|
||||||
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
|
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
|
||||||
Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
|
Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
|
||||||
string_literal.str_content().to_string(),
|
string_literal.str_content().to_string(),
|
||||||
)),
|
)),
|
||||||
Self::Identifier(_) | Self::FunctionCall(_) => None,
|
Self::Identifier(_) => None,
|
||||||
Self::Parenthesized(parenthesized) => parenthesized.expression().comptime_eval(scope),
|
Self::Parenthesized(parenthesized) => parenthesized.expression().comptime_eval(),
|
||||||
Self::Prefix(prefix) => prefix.operand().comptime_eval(scope).and_then(|val| {
|
Self::Prefix(prefix) => {
|
||||||
match (prefix.operator(), val) {
|
prefix
|
||||||
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
.operand()
|
||||||
Some(ComptimeValue::Boolean(!boolean))
|
.comptime_eval()
|
||||||
}
|
.and_then(|val| match (prefix.operator(), val) {
|
||||||
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
||||||
Some(ComptimeValue::Integer(-int))
|
Some(ComptimeValue::Boolean(!boolean))
|
||||||
}
|
}
|
||||||
_ => None,
|
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
||||||
}
|
Some(ComptimeValue::Integer(-int))
|
||||||
}),
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
// TODO: correctly evaluate lua code
|
// TODO: correctly evaluate lua code
|
||||||
Self::Lua(lua) => lua
|
Self::Lua(lua) => lua
|
||||||
.eval_string(&VoidHandler)
|
.eval_string(&VoidHandler)
|
||||||
|
@ -242,18 +210,11 @@ impl Primary {
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(ComptimeValue::String),
|
.map(ComptimeValue::String),
|
||||||
Self::MacroStringLiteral(macro_string_literal) => {
|
Self::MacroStringLiteral(macro_string_literal) => {
|
||||||
if macro_string_literal
|
// TODO: mark as containing macros
|
||||||
.parts()
|
Some(ComptimeValue::String(macro_string_literal.str_content()))
|
||||||
.iter()
|
|
||||||
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
|
||||||
{
|
|
||||||
Some(ComptimeValue::MacroString(
|
|
||||||
macro_string_literal.str_content(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Some(ComptimeValue::String(macro_string_literal.str_content()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// TODO: correctly evaluate function calls
|
||||||
|
Self::FunctionCall(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,31 +222,11 @@ impl Primary {
|
||||||
impl Binary {
|
impl Binary {
|
||||||
/// Evaluate at compile-time.
|
/// Evaluate at compile-time.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
|
pub fn comptime_eval(&self) -> Option<ComptimeValue> {
|
||||||
let left = self.left_operand().comptime_eval(scope)?;
|
let left = self.left_operand().comptime_eval()?;
|
||||||
let right = self.right_operand().comptime_eval(scope)?;
|
let right = self.right_operand().comptime_eval()?;
|
||||||
|
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(ComptimeValue::Boolean(true), _) | (_, ComptimeValue::Boolean(true)) if matches!(self.operator(), BinaryOperator::LogicalOr(..))
|
|
||||||
// TODO: re-enable if can_yield_type works properly
|
|
||||||
/*&& self
|
|
||||||
.left_operand()
|
|
||||||
.can_yield_type(ValueType::Boolean, scope)
|
|
||||||
&& self
|
|
||||||
.right_operand()
|
|
||||||
.can_yield_type(ValueType::Boolean, scope)*/ => {
|
|
||||||
Some(ComptimeValue::Boolean(true))
|
|
||||||
}
|
|
||||||
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false)) if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
|
|
||||||
// TODO: re-enable if can_yield_type works properly
|
|
||||||
/*&& self
|
|
||||||
.left_operand()
|
|
||||||
.can_yield_type(ValueType::Boolean, scope)
|
|
||||||
&& self
|
|
||||||
.right_operand()
|
|
||||||
.can_yield_type(ValueType::Boolean, scope)*/ => {
|
|
||||||
Some(ComptimeValue::Boolean(false))
|
|
||||||
}
|
|
||||||
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
(ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
|
||||||
match self.operator() {
|
match self.operator() {
|
||||||
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
|
||||||
|
@ -295,6 +236,21 @@ impl Binary {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO: check that the other value will be boolean (even if not comptime)
|
||||||
|
(ComptimeValue::Boolean(true), _) | (_, ComptimeValue::Boolean(true)) => {
|
||||||
|
if matches!(self.operator(), BinaryOperator::LogicalOr(..)) {
|
||||||
|
Some(ComptimeValue::Boolean(true))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false)) => {
|
||||||
|
if matches!(self.operator(), BinaryOperator::LogicalAnd(..)) {
|
||||||
|
Some(ComptimeValue::Boolean(false))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
(ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
|
||||||
match self.operator() {
|
match self.operator() {
|
||||||
BinaryOperator::Add(..) => left.checked_add(right).map(ComptimeValue::Integer),
|
BinaryOperator::Add(..) => left.checked_add(right).map(ComptimeValue::Integer),
|
||||||
|
@ -481,7 +437,7 @@ impl Transpiler {
|
||||||
))])
|
))])
|
||||||
}
|
}
|
||||||
DataLocation::Tag { .. } => Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
DataLocation::Tag { .. } => Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: ValueType::Boolean,
|
expected_type: ValueType::Tag,
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
})),
|
})),
|
||||||
DataLocation::Storage {
|
DataLocation::Storage {
|
||||||
|
@ -506,7 +462,7 @@ impl Transpiler {
|
||||||
} else {
|
} else {
|
||||||
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
expected_type: ValueType::Integer,
|
expected_type: ValueType::NumberStorage,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,7 +478,11 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
Primary::StringLiteral(_) | Primary::MacroStringLiteral(_) => {
|
Primary::StringLiteral(_) | Primary::MacroStringLiteral(_) => {
|
||||||
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: target.value_type(),
|
expected_type: match target {
|
||||||
|
DataLocation::ScoreboardValue { .. } => ValueType::ScoreboardValue,
|
||||||
|
DataLocation::Tag { .. } => ValueType::Tag,
|
||||||
|
DataLocation::Storage { .. } => ValueType::NumberStorage,
|
||||||
|
},
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -592,7 +552,7 @@ impl Transpiler {
|
||||||
} else {
|
} else {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
expected_type: target.value_type(),
|
expected_type: ValueType::BooleanStorage,
|
||||||
});
|
});
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -633,7 +593,7 @@ impl Transpiler {
|
||||||
} else {
|
} else {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
expected_type: target.value_type(),
|
expected_type: ValueType::NumberStorage,
|
||||||
});
|
});
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
Err(err)
|
Err(err)
|
||||||
|
@ -641,7 +601,7 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
DataLocation::Tag { .. } => {
|
DataLocation::Tag { .. } => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: ValueType::Boolean,
|
expected_type: ValueType::Tag,
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
});
|
});
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
|
@ -650,7 +610,13 @@ impl Transpiler {
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: target.value_type(),
|
expected_type: match target {
|
||||||
|
DataLocation::ScoreboardValue { .. } => {
|
||||||
|
ValueType::ScoreboardValue
|
||||||
|
}
|
||||||
|
DataLocation::Tag { .. } => ValueType::Tag,
|
||||||
|
DataLocation::Storage { .. } => ValueType::NumberStorage,
|
||||||
|
},
|
||||||
expression: primary.span(),
|
expression: primary.span(),
|
||||||
});
|
});
|
||||||
handler.receive(err.clone());
|
handler.receive(err.clone());
|
||||||
|
@ -668,450 +634,23 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::needless_pass_by_ref_mut)]
|
||||||
fn transpile_binary_expression(
|
fn transpile_binary_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
binary: &Binary,
|
binary: &Binary,
|
||||||
target: &DataLocation,
|
target: &DataLocation,
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<Vec<Command>> {
|
|
||||||
if let Some(value) = binary.comptime_eval(scope) {
|
|
||||||
self.transpile_comptime_value(&value, binary, target, scope, handler)
|
|
||||||
} else {
|
|
||||||
match binary.operator() {
|
|
||||||
BinaryOperator::Add(_)
|
|
||||||
| BinaryOperator::Subtract(_)
|
|
||||||
| BinaryOperator::Multiply(_)
|
|
||||||
| BinaryOperator::Divide(_)
|
|
||||||
| BinaryOperator::Modulo(_) => {
|
|
||||||
self.transpile_scoreboard_operation(binary, target, scope, handler)
|
|
||||||
}
|
|
||||||
BinaryOperator::Equal(..)
|
|
||||||
| BinaryOperator::GreaterThan(_)
|
|
||||||
| BinaryOperator::GreaterThanOrEqual(..)
|
|
||||||
| BinaryOperator::LessThan(_)
|
|
||||||
| BinaryOperator::LessThanOrEqual(..)
|
|
||||||
| BinaryOperator::NotEqual(..)
|
|
||||||
| BinaryOperator::LogicalAnd(..)
|
|
||||||
| BinaryOperator::LogicalOr(..) => {
|
|
||||||
let (mut cmds, cond) =
|
|
||||||
self.transpile_binary_expression_as_condition(binary, scope, handler)?;
|
|
||||||
|
|
||||||
let (success_cmd, else_cmd) = match target {
|
|
||||||
DataLocation::ScoreboardValue { objective, target } => (
|
|
||||||
format!("scoreboard players set {target} {objective} 1"),
|
|
||||||
format!("scoreboard players set {target} {objective} 0"),
|
|
||||||
),
|
|
||||||
DataLocation::Storage {
|
|
||||||
storage_name,
|
|
||||||
path,
|
|
||||||
r#type,
|
|
||||||
} => {
|
|
||||||
if matches!(r#type, StorageType::Boolean) {
|
|
||||||
(
|
|
||||||
format!(
|
|
||||||
"data modify storage {storage_name} {path} set value 1b"
|
|
||||||
),
|
|
||||||
format!(
|
|
||||||
"data modify storage {storage_name} {path} set value 0b"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: binary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataLocation::Tag { tag_name, entity } => (
|
|
||||||
format!("tag {entity} add {tag_name}"),
|
|
||||||
format!("tag {entity} remove {tag_name}"),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
cmds.push(Command::Execute(Execute::If(
|
|
||||||
cond,
|
|
||||||
Box::new(Execute::Run(Box::new(Command::Raw(success_cmd)))),
|
|
||||||
Some(Box::new(Execute::Run(Box::new(Command::Raw(else_cmd))))),
|
|
||||||
)));
|
|
||||||
|
|
||||||
Ok(cmds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn transpile_expression_as_condition(
|
|
||||||
&mut self,
|
|
||||||
expression: &Expression,
|
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<(Vec<Command>, Condition)> {
|
|
||||||
match expression {
|
|
||||||
Expression::Primary(primary) => {
|
|
||||||
self.transpile_primary_expression_as_condition(primary, scope, handler)
|
|
||||||
}
|
|
||||||
Expression::Binary(binary) => {
|
|
||||||
self.transpile_binary_expression_as_condition(binary, scope, handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile_primary_expression_as_condition(
|
|
||||||
&mut self,
|
|
||||||
primary: &Primary,
|
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<(Vec<Command>, Condition)> {
|
|
||||||
match primary {
|
|
||||||
Primary::Boolean(_) => todo!("handle boolean literal if not catched by comptime eval"),
|
|
||||||
Primary::Integer(_) => {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: primary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
Primary::StringLiteral(s) => Ok((
|
|
||||||
Vec::new(),
|
|
||||||
Condition::Atom(s.str_content().to_string().into()),
|
|
||||||
)),
|
|
||||||
Primary::MacroStringLiteral(macro_string) => {
|
|
||||||
Ok((Vec::new(), Condition::Atom(macro_string.into())))
|
|
||||||
}
|
|
||||||
Primary::FunctionCall(func) => {
|
|
||||||
if func
|
|
||||||
.arguments()
|
|
||||||
.as_ref()
|
|
||||||
.is_some_and(|args| !args.is_empty())
|
|
||||||
{
|
|
||||||
let err =
|
|
||||||
TranspileError::FunctionArgumentsNotAllowed(FunctionArgumentsNotAllowed {
|
|
||||||
arguments: func.arguments().as_ref().unwrap().span(),
|
|
||||||
message: "Function calls as conditions do not support arguments."
|
|
||||||
.into(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
} else {
|
|
||||||
let (func_location, _) = self.get_or_transpile_function(
|
|
||||||
&func.identifier().span,
|
|
||||||
None,
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
Vec::new(),
|
|
||||||
Condition::Atom(format!("function {func_location}").into()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Primary::Identifier(ident) => {
|
|
||||||
#[expect(clippy::option_if_let_else)]
|
|
||||||
if let Some(variable) = scope.get_variable(ident.span.str()).as_deref() {
|
|
||||||
match variable {
|
|
||||||
VariableData::BooleanStorage { storage_name, path } => Ok((
|
|
||||||
Vec::new(),
|
|
||||||
Condition::Atom(format!("data storage {storage_name} {{{path}: 1b}}").into()),
|
|
||||||
)),
|
|
||||||
VariableData::FunctionArgument { .. } => {
|
|
||||||
Ok((
|
|
||||||
Vec::new(),
|
|
||||||
Condition::Atom(shulkerbox::util::MacroString::MacroString(vec![
|
|
||||||
shulkerbox::util::MacroStringPart::MacroUsage(ident.span.str().to_string()),
|
|
||||||
]))
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: primary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
|
|
||||||
identifier: ident.span.clone(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
Primary::Parenthesized(parenthesized) => {
|
|
||||||
self.transpile_expression_as_condition(parenthesized.expression(), scope, handler)
|
|
||||||
}
|
|
||||||
Primary::Prefix(prefix) => match prefix.operator() {
|
|
||||||
PrefixOperator::LogicalNot(_) => {
|
|
||||||
let (cmds, cond) = self.transpile_primary_expression_as_condition(
|
|
||||||
prefix.operand(),
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
)?;
|
|
||||||
Ok((cmds, Condition::Not(Box::new(cond))))
|
|
||||||
}
|
|
||||||
PrefixOperator::Negate(_) => {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: primary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Primary::Lua(_) => todo!("Lua code as condition"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile_binary_expression_as_condition(
|
|
||||||
&mut self,
|
|
||||||
binary: &Binary,
|
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<(Vec<Command>, Condition)> {
|
|
||||||
match binary.operator() {
|
|
||||||
BinaryOperator::Equal(..)
|
|
||||||
| BinaryOperator::NotEqual(..)
|
|
||||||
| BinaryOperator::GreaterThan(_)
|
|
||||||
| BinaryOperator::GreaterThanOrEqual(..)
|
|
||||||
| BinaryOperator::LessThan(_)
|
|
||||||
| BinaryOperator::LessThanOrEqual(..) => {
|
|
||||||
self.transpile_comparison_operator(binary, scope, handler)
|
|
||||||
}
|
|
||||||
BinaryOperator::LogicalAnd(..) | BinaryOperator::LogicalOr(..) => {
|
|
||||||
self.transpile_logic_operator(binary, scope, handler)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: binary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile_scoreboard_operation(
|
|
||||||
&mut self,
|
|
||||||
binary: &Binary,
|
|
||||||
target: &DataLocation,
|
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<Vec<Command>> {
|
|
||||||
let left = binary.left_operand();
|
|
||||||
let right = binary.right_operand();
|
|
||||||
let operator = binary.operator();
|
|
||||||
|
|
||||||
let (temp_objective, temp_locations) = self.get_temp_scoreboard_locations(2);
|
|
||||||
|
|
||||||
let score_target_location = match target {
|
|
||||||
DataLocation::ScoreboardValue { objective, target } => (objective, target),
|
|
||||||
_ => (&temp_objective, &temp_locations[0]),
|
|
||||||
};
|
|
||||||
|
|
||||||
let left_cmds = self.transpile_expression(
|
|
||||||
left,
|
|
||||||
&DataLocation::ScoreboardValue {
|
|
||||||
objective: score_target_location.0.clone(),
|
|
||||||
target: score_target_location.1.clone(),
|
|
||||||
},
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
)?;
|
|
||||||
let right_cmds = self.transpile_expression(
|
|
||||||
right,
|
|
||||||
&DataLocation::ScoreboardValue {
|
|
||||||
objective: temp_objective.clone(),
|
|
||||||
target: temp_locations[1].clone(),
|
|
||||||
},
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let calc_cmds = {
|
|
||||||
let (target_objective, target) = score_target_location;
|
|
||||||
let source = &temp_locations[1];
|
|
||||||
let source_objective = &temp_objective;
|
|
||||||
|
|
||||||
let operation = match operator {
|
|
||||||
BinaryOperator::Add(_) => "+=",
|
|
||||||
BinaryOperator::Subtract(_) => "-=",
|
|
||||||
BinaryOperator::Multiply(_) => "*=",
|
|
||||||
BinaryOperator::Divide(_) => "/=",
|
|
||||||
BinaryOperator::Modulo(_) => "%=",
|
|
||||||
_ => unreachable!("This operator should not be handled here."),
|
|
||||||
};
|
|
||||||
|
|
||||||
vec![Command::Raw(format!(
|
|
||||||
"scoreboard players operation {target} {target_objective} {operation} {source} {source_objective}"
|
|
||||||
))]
|
|
||||||
};
|
|
||||||
|
|
||||||
let transfer_cmd = match target {
|
|
||||||
DataLocation::ScoreboardValue { .. } => None,
|
|
||||||
DataLocation::Tag { .. } => {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: binary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
DataLocation::Storage {
|
|
||||||
storage_name,
|
|
||||||
path,
|
|
||||||
r#type,
|
|
||||||
} => match r#type {
|
|
||||||
StorageType::Byte | StorageType::Double | StorageType::Int | StorageType::Long => {
|
|
||||||
Some(Command::Execute(Execute::Store(
|
|
||||||
format!(
|
|
||||||
"result storage {storage_name} {path} {t} 1",
|
|
||||||
t = r#type.as_str()
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
|
||||||
"scoreboard players get {target} {objective}",
|
|
||||||
objective = score_target_location.0,
|
|
||||||
target = score_target_location.1
|
|
||||||
))))),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
StorageType::Boolean => {
|
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
|
||||||
expected_type: ValueType::Boolean,
|
|
||||||
expression: binary.span(),
|
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(left_cmds
|
|
||||||
.into_iter()
|
|
||||||
.chain(right_cmds)
|
|
||||||
.chain(calc_cmds)
|
|
||||||
.chain(transfer_cmd)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile_comparison_operator(
|
|
||||||
&mut self,
|
|
||||||
binary: &Binary,
|
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<(Vec<Command>, Condition)> {
|
|
||||||
let invert = matches!(binary.operator(), BinaryOperator::NotEqual(..));
|
|
||||||
|
|
||||||
// TODO: evaluate comptime values and compare using `matches` and integer ranges
|
|
||||||
|
|
||||||
let operator = match binary.operator() {
|
|
||||||
BinaryOperator::Equal(..) | BinaryOperator::NotEqual(..) => "=",
|
|
||||||
BinaryOperator::GreaterThan(_) => ">",
|
|
||||||
BinaryOperator::GreaterThanOrEqual(..) => ">=",
|
|
||||||
BinaryOperator::LessThan(_) => "<",
|
|
||||||
BinaryOperator::LessThanOrEqual(..) => "<=",
|
|
||||||
_ => unreachable!("This function should only be called for comparison operators."),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (temp_objective, mut temp_locations) = self.get_temp_scoreboard_locations(2);
|
|
||||||
|
|
||||||
let condition = Condition::Atom(
|
|
||||||
format!(
|
|
||||||
"score {target} {temp_objective} {operator} {source} {temp_objective}",
|
|
||||||
target = temp_locations[0],
|
|
||||||
source = temp_locations[1]
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let left_cmds = self.transpile_expression(
|
|
||||||
binary.left_operand(),
|
|
||||||
&DataLocation::ScoreboardValue {
|
|
||||||
objective: temp_objective.clone(),
|
|
||||||
target: std::mem::take(&mut temp_locations[0]),
|
|
||||||
},
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
)?;
|
|
||||||
let right_cmds = self.transpile_expression(
|
|
||||||
binary.right_operand(),
|
|
||||||
&DataLocation::ScoreboardValue {
|
|
||||||
objective: temp_objective,
|
|
||||||
target: std::mem::take(&mut temp_locations[1]),
|
|
||||||
},
|
|
||||||
scope,
|
|
||||||
handler,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
left_cmds.into_iter().chain(right_cmds).collect(),
|
|
||||||
if invert {
|
|
||||||
Condition::Not(Box::new(condition))
|
|
||||||
} else {
|
|
||||||
condition
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn transpile_logic_operator(
|
|
||||||
&mut self,
|
|
||||||
binary: &Binary,
|
|
||||||
scope: &Arc<super::Scope>,
|
|
||||||
handler: &impl Handler<base::Error>,
|
|
||||||
) -> TranspileResult<(Vec<Command>, Condition)> {
|
|
||||||
let left = binary.left_operand().as_ref();
|
|
||||||
let right = binary.right_operand().as_ref();
|
|
||||||
|
|
||||||
let (left_cmds, left_cond) =
|
|
||||||
self.transpile_expression_as_condition(left, scope, handler)?;
|
|
||||||
let (right_cmds, right_cond) =
|
|
||||||
self.transpile_expression_as_condition(right, scope, handler)?;
|
|
||||||
|
|
||||||
let combined_cmds = left_cmds.into_iter().chain(right_cmds).collect();
|
|
||||||
|
|
||||||
match binary.operator() {
|
|
||||||
BinaryOperator::LogicalAnd(..) => Ok((
|
|
||||||
combined_cmds,
|
|
||||||
Condition::And(Box::new(left_cond), Box::new(right_cond)),
|
|
||||||
)),
|
|
||||||
BinaryOperator::LogicalOr(..) => Ok((
|
|
||||||
combined_cmds,
|
|
||||||
Condition::Or(Box::new(left_cond), Box::new(right_cond)),
|
|
||||||
)),
|
|
||||||
_ => unreachable!("This function should only be called for logical operators."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[expect(clippy::unused_self)]
|
|
||||||
fn transpile_comptime_value(
|
|
||||||
&self,
|
|
||||||
value: &ComptimeValue,
|
|
||||||
original: &impl SourceElement,
|
|
||||||
target: &DataLocation,
|
|
||||||
_scope: &Arc<super::Scope>,
|
_scope: &Arc<super::Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
_handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Vec<Command>> {
|
||||||
match value {
|
match binary.comptime_eval() {
|
||||||
ComptimeValue::Integer(value) => match target {
|
Some(ComptimeValue::Integer(value)) => match target {
|
||||||
DataLocation::ScoreboardValue { objective, target } => Ok(vec![Command::Raw(
|
DataLocation::ScoreboardValue { objective, target } => Ok(vec![Command::Raw(
|
||||||
format!("scoreboard players set {target} {objective} {value}"),
|
format!("scoreboard players set {target} {objective} {value}"),
|
||||||
)]),
|
)]),
|
||||||
DataLocation::Tag { .. } => {
|
DataLocation::Tag { .. } => Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
expected_type: ValueType::Tag,
|
||||||
expected_type: ValueType::Boolean,
|
expression: binary.span(),
|
||||||
expression: original.span(),
|
})),
|
||||||
});
|
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
DataLocation::Storage {
|
DataLocation::Storage {
|
||||||
storage_name,
|
storage_name,
|
||||||
path,
|
path,
|
||||||
|
@ -1129,16 +668,14 @@ impl Transpiler {
|
||||||
suffix = r#type.suffix(),
|
suffix = r#type.suffix(),
|
||||||
))])
|
))])
|
||||||
} else {
|
} else {
|
||||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: original.span(),
|
expression: binary.span(),
|
||||||
expected_type: target.value_type(),
|
expected_type: ValueType::NumberStorage,
|
||||||
});
|
}))
|
||||||
handler.receive(err.clone());
|
|
||||||
Err(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
&ComptimeValue::Boolean(value) => match target {
|
Some(ComptimeValue::Boolean(value)) => match target {
|
||||||
DataLocation::ScoreboardValue { objective, target } => {
|
DataLocation::ScoreboardValue { objective, target } => {
|
||||||
Ok(vec![Command::Raw(format!(
|
Ok(vec![Command::Raw(format!(
|
||||||
"scoreboard players set {target} {objective} {value}",
|
"scoreboard players set {target} {objective} {value}",
|
||||||
|
@ -1162,39 +699,29 @@ impl Transpiler {
|
||||||
))])
|
))])
|
||||||
} else {
|
} else {
|
||||||
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expression: original.span(),
|
expression: binary.span(),
|
||||||
expected_type: target.value_type(),
|
expected_type: ValueType::NumberStorage,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ComptimeValue::String(_) | ComptimeValue::MacroString(_) => {
|
Some(ComptimeValue::String(_)) => {
|
||||||
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
Err(TranspileError::MismatchedTypes(MismatchedTypes {
|
||||||
expected_type: target.value_type(),
|
expected_type: match target {
|
||||||
expression: original.span(),
|
DataLocation::ScoreboardValue { .. } => ValueType::ScoreboardValue,
|
||||||
|
DataLocation::Tag { .. } => ValueType::Tag,
|
||||||
|
DataLocation::Storage { .. } => ValueType::NumberStorage,
|
||||||
|
},
|
||||||
|
expression: binary.span(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
let _left = binary.left_operand();
|
||||||
|
let _right = binary.right_operand();
|
||||||
|
let _operator = binary.operator();
|
||||||
|
|
||||||
|
todo!("Transpile binary expression")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get temporary scoreboard locations.
|
|
||||||
fn get_temp_scoreboard_locations(&mut self, amount: usize) -> (String, Vec<String>) {
|
|
||||||
let objective = "shu_temp_".to_string()
|
|
||||||
+ &chksum_md5::hash(&self.main_namespace_name).to_hex_lowercase();
|
|
||||||
|
|
||||||
self.datapack
|
|
||||||
.register_scoreboard(&objective, None::<&str>, None::<&str>);
|
|
||||||
|
|
||||||
let targets = (0..amount)
|
|
||||||
.map(|i| {
|
|
||||||
chksum_md5::hash(format!("{namespace}\0{j}", namespace = self.main_namespace_name, j = i + self.temp_counter))
|
|
||||||
.to_hex_lowercase()
|
|
||||||
.split_off(16)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
self.temp_counter = self.temp_counter.wrapping_add(amount);
|
|
||||||
|
|
||||||
(objective, targets)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,6 @@ use crate::{
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
error::{TranspileError, TranspileResult},
|
error::{TranspileError, TranspileResult},
|
||||||
expression::ComptimeValue,
|
|
||||||
variables::{Scope, VariableData},
|
variables::{Scope, VariableData},
|
||||||
FunctionData, TranspileAnnotationValue,
|
FunctionData, TranspileAnnotationValue,
|
||||||
};
|
};
|
||||||
|
@ -43,7 +42,6 @@ pub struct Transpiler {
|
||||||
pub(super) datapack: shulkerbox::datapack::Datapack,
|
pub(super) datapack: shulkerbox::datapack::Datapack,
|
||||||
pub(super) setup_cmds: Vec<Command>,
|
pub(super) setup_cmds: Vec<Command>,
|
||||||
pub(super) initialized_constant_scores: HashSet<i64>,
|
pub(super) initialized_constant_scores: HashSet<i64>,
|
||||||
pub(super) temp_counter: usize,
|
|
||||||
/// Top-level [`Scope`] for each program identifier
|
/// Top-level [`Scope`] for each program identifier
|
||||||
scopes: BTreeMap<String, Arc<Scope<'static>>>,
|
scopes: BTreeMap<String, Arc<Scope<'static>>>,
|
||||||
/// Key: (program identifier, function name)
|
/// Key: (program identifier, function name)
|
||||||
|
@ -62,23 +60,28 @@ impl Transpiler {
|
||||||
datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
|
datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
|
||||||
setup_cmds: Vec::new(),
|
setup_cmds: Vec::new(),
|
||||||
initialized_constant_scores: HashSet::new(),
|
initialized_constant_scores: HashSet::new(),
|
||||||
temp_counter: 0,
|
|
||||||
scopes: BTreeMap::new(),
|
scopes: BTreeMap::new(),
|
||||||
functions: BTreeMap::new(),
|
functions: BTreeMap::new(),
|
||||||
aliases: HashMap::new(),
|
aliases: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transpiles the given programs and returns the resulting datapack.
|
/// Consumes the transpiler and returns the resulting datapack.
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_datapack(self) -> Datapack {
|
||||||
|
self.datapack
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transpiles the given programs.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing
|
/// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub fn transpile(
|
pub fn transpile(
|
||||||
mut self,
|
&mut self,
|
||||||
programs: &[ProgramFile],
|
programs: &[ProgramFile],
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<Datapack, TranspileError> {
|
) -> Result<(), TranspileError> {
|
||||||
tracing::trace!("Transpiling program declarations");
|
tracing::trace!("Transpiling program declarations");
|
||||||
for program in programs {
|
for program in programs {
|
||||||
let program_identifier = program
|
let program_identifier = program
|
||||||
|
@ -139,7 +142,7 @@ impl Transpiler {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.datapack)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transpiles the given program.
|
/// Transpiles the given program.
|
||||||
|
@ -331,8 +334,8 @@ impl Transpiler {
|
||||||
|val| match val {
|
|val| match val {
|
||||||
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
||||||
TranspileAnnotationValue::Expression(expr) => expr
|
TranspileAnnotationValue::Expression(expr) => expr
|
||||||
.comptime_eval(scope)
|
.comptime_eval()
|
||||||
.and_then(|val| val.to_string_no_macro())
|
.map(|val| val.to_string())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
let err = TranspileError::IllegalAnnotationContent(
|
let err = TranspileError::IllegalAnnotationContent(
|
||||||
IllegalAnnotationContent {
|
IllegalAnnotationContent {
|
||||||
|
@ -503,12 +506,9 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
Statement::ExecuteBlock(execute) => {
|
Statement::ExecuteBlock(execute) => {
|
||||||
let child_scope = Scope::with_parent(scope);
|
let child_scope = Scope::with_parent(scope);
|
||||||
Ok(self.transpile_execute_block(
|
Ok(self
|
||||||
execute,
|
.transpile_execute_block(execute, program_identifier, &child_scope, handler)?
|
||||||
program_identifier,
|
.map_or_else(Vec::new, |cmd| vec![cmd]))
|
||||||
&child_scope,
|
|
||||||
handler,
|
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
Statement::DocComment(doccomment) => {
|
Statement::DocComment(doccomment) => {
|
||||||
let content = doccomment.content();
|
let content = doccomment.content();
|
||||||
|
@ -652,15 +652,9 @@ impl Transpiler {
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Option<Command>> {
|
||||||
self.transpile_execute_block_internal(execute, program_identifier, scope, handler)
|
self.transpile_execute_block_internal(execute, program_identifier, scope, handler)
|
||||||
.map(|ex| {
|
.map(|ex| ex.map(Command::Execute))
|
||||||
ex.map(|(mut pre_cmds, exec)| {
|
|
||||||
pre_cmds.push(exec.into());
|
|
||||||
pre_cmds
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transpile_execute_block_internal(
|
fn transpile_execute_block_internal(
|
||||||
|
@ -669,7 +663,7 @@ impl Transpiler {
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<(Vec<Command>, Execute)>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
match execute {
|
match execute {
|
||||||
ExecuteBlock::HeadTail(head, tail) => {
|
ExecuteBlock::HeadTail(head, tail) => {
|
||||||
let tail = match tail {
|
let tail = match tail {
|
||||||
|
@ -693,7 +687,7 @@ impl Transpiler {
|
||||||
if commands.is_empty() {
|
if commands.is_empty() {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Ok(Some((Vec::new(), Execute::Runs(commands))))
|
Ok(Some(Execute::Runs(commands)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExecuteBlockTail::ExecuteBlock(_, execute_block) => self
|
ExecuteBlockTail::ExecuteBlock(_, execute_block) => self
|
||||||
|
@ -768,66 +762,58 @@ impl Transpiler {
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<(Vec<Command>, Execute)>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
let cond_expression = cond.condition().expression().as_ref();
|
let (_, cond) = cond.clone().dissolve();
|
||||||
|
let (_, cond, _) = cond.dissolve();
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
let el = el.and_then(|el| {
|
let el = el
|
||||||
let (_, block) = el.clone().dissolve();
|
.and_then(|el| {
|
||||||
let statements = block.statements();
|
let (_, block) = el.clone().dissolve();
|
||||||
let cmds = statements
|
let statements = block.statements();
|
||||||
.iter()
|
let cmds = statements
|
||||||
.flat_map(|statement| {
|
.iter()
|
||||||
self.transpile_statement(statement, program_identifier, scope, handler)
|
.flat_map(|statement| {
|
||||||
.unwrap_or_else(|err| {
|
self.transpile_statement(statement, program_identifier, scope, handler)
|
||||||
errors.push(err);
|
.unwrap_or_else(|err| {
|
||||||
Vec::new()
|
errors.push(err);
|
||||||
})
|
Vec::new()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
match cmds.len() {
|
match cmds.len() {
|
||||||
0 => None,
|
0 => None,
|
||||||
1 => Some(Execute::Run(Box::new(
|
1 => Some(Execute::Run(Box::new(
|
||||||
cmds.into_iter().next().expect("length is 1"),
|
cmds.into_iter().next().expect("length is 1"),
|
||||||
))),
|
))),
|
||||||
_ => Some(Execute::Runs(cmds)),
|
_ => Some(Execute::Runs(cmds)),
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.map(Box::new);
|
||||||
|
|
||||||
if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope) {
|
if !errors.is_empty() {
|
||||||
if value {
|
return Err(errors.remove(0));
|
||||||
Ok(Some((Vec::new(), then)))
|
|
||||||
} else {
|
|
||||||
Ok(el.map(|el| (Vec::new(), el)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !errors.is_empty() {
|
|
||||||
return Err(errors.remove(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (pre_cond_cmds, cond) =
|
|
||||||
self.transpile_expression_as_condition(cond_expression, scope, handler)?;
|
|
||||||
|
|
||||||
Ok(Some((
|
|
||||||
pre_cond_cmds,
|
|
||||||
Execute::If(cond, Box::new(then), el.map(Box::new)),
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Some(Execute::If(
|
||||||
|
datapack::Condition::from(cond),
|
||||||
|
Box::new(then),
|
||||||
|
el,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn combine_execute_head_tail(
|
fn combine_execute_head_tail(
|
||||||
&mut self,
|
&mut self,
|
||||||
head: &ExecuteBlockHead,
|
head: &ExecuteBlockHead,
|
||||||
tail: Option<(Vec<Command>, Execute)>,
|
tail: Option<Execute>,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
scope: &Arc<Scope>,
|
scope: &Arc<Scope>,
|
||||||
handler: &impl Handler<base::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<(Vec<Command>, Execute)>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
Ok(match head {
|
Ok(match head {
|
||||||
ExecuteBlockHead::Conditional(cond) => {
|
ExecuteBlockHead::Conditional(cond) => {
|
||||||
if let Some((mut pre_cmds, tail)) = tail {
|
if let Some(tail) = tail {
|
||||||
self.transpile_conditional(
|
self.transpile_conditional(
|
||||||
cond,
|
cond,
|
||||||
tail,
|
tail,
|
||||||
|
@ -836,88 +822,57 @@ impl Transpiler {
|
||||||
scope,
|
scope,
|
||||||
handler,
|
handler,
|
||||||
)?
|
)?
|
||||||
.map(|(pre_cond_cmds, cond)| {
|
|
||||||
pre_cmds.extend(pre_cond_cmds);
|
|
||||||
(pre_cmds, cond)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::As(r#as) => {
|
ExecuteBlockHead::As(r#as) => {
|
||||||
let selector = r#as.as_selector();
|
let selector = r#as.as_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::As(selector.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::As(selector.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::At(at) => {
|
ExecuteBlockHead::At(at) => {
|
||||||
let selector = at.at_selector();
|
let selector = at.at_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::At(selector.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::At(selector.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Align(align) => {
|
ExecuteBlockHead::Align(align) => {
|
||||||
let align = align.align_selector();
|
let align = align.align_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Align(align.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::Align(align.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Anchored(anchored) => {
|
ExecuteBlockHead::Anchored(anchored) => {
|
||||||
let anchor = anchored.anchored_selector();
|
let anchor = anchored.anchored_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Anchored(anchor.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::Anchored(anchor.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::In(r#in) => {
|
ExecuteBlockHead::In(r#in) => {
|
||||||
let dimension = r#in.in_selector();
|
let dimension = r#in.in_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::In(dimension.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::In(dimension.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Positioned(positioned) => {
|
ExecuteBlockHead::Positioned(positioned) => {
|
||||||
let position = positioned.positioned_selector();
|
let position = positioned.positioned_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Positioned(position.into(), Box::new(tail)))
|
||||||
(
|
|
||||||
pre_cmds,
|
|
||||||
Execute::Positioned(position.into(), Box::new(tail)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Rotated(rotated) => {
|
ExecuteBlockHead::Rotated(rotated) => {
|
||||||
let rotation = rotated.rotated_selector();
|
let rotation = rotated.rotated_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Rotated(rotation.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::Rotated(rotation.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Facing(facing) => {
|
ExecuteBlockHead::Facing(facing) => {
|
||||||
let facing = facing.facing_selector();
|
let facing = facing.facing_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Facing(facing.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::Facing(facing.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::AsAt(as_at) => {
|
ExecuteBlockHead::AsAt(as_at) => {
|
||||||
let selector = as_at.asat_selector();
|
let selector = as_at.asat_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::AsAt(selector.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::AsAt(selector.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::On(on) => {
|
ExecuteBlockHead::On(on) => {
|
||||||
let dimension = on.on_selector();
|
let dimension = on.on_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::On(dimension.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::On(dimension.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Store(store) => {
|
ExecuteBlockHead::Store(store) => {
|
||||||
let store = store.store_selector();
|
let store = store.store_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Store(store.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::Store(store.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
ExecuteBlockHead::Summon(summon) => {
|
ExecuteBlockHead::Summon(summon) => {
|
||||||
let entity = summon.summon_selector();
|
let entity = summon.summon_selector();
|
||||||
tail.map(|(pre_cmds, tail)| {
|
tail.map(|tail| Execute::Summon(entity.into(), Box::new(tail)))
|
||||||
(pre_cmds, Execute::Summon(entity.into(), Box::new(tail)))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use chksum_md5 as md5;
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use shulkerbox::prelude::{Command, Condition, Execute};
|
use shulkerbox::prelude::{Command, Condition, Execute};
|
||||||
|
|
||||||
use enum_as_inner::EnumAsInner;
|
use strum::EnumIs;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{self, source_file::SourceElement as _, Handler},
|
base::{self, source_file::SourceElement as _, Handler},
|
||||||
|
@ -33,7 +33,7 @@ use super::{
|
||||||
use super::Transpiler;
|
use super::Transpiler;
|
||||||
|
|
||||||
/// Stores the data required to access a variable.
|
/// Stores the data required to access a variable.
|
||||||
#[derive(Debug, Clone, EnumAsInner)]
|
#[derive(Debug, Clone, EnumIs)]
|
||||||
pub enum VariableData {
|
pub enum VariableData {
|
||||||
/// A function.
|
/// A function.
|
||||||
Function {
|
Function {
|
||||||
|
@ -376,8 +376,8 @@ fn get_single_data_location_identifiers(
|
||||||
) = (name, target)
|
) = (name, target)
|
||||||
{
|
{
|
||||||
if let (Some(name_eval), Some(target_eval)) = (
|
if let (Some(name_eval), Some(target_eval)) = (
|
||||||
objective.comptime_eval(scope).map(|val| val.to_string()),
|
objective.comptime_eval().map(|val| val.to_string()),
|
||||||
target.comptime_eval(scope).map(|val| val.to_string()),
|
target.comptime_eval().map(|val| val.to_string()),
|
||||||
) {
|
) {
|
||||||
// TODO: change invalid criteria if boolean
|
// TODO: change invalid criteria if boolean
|
||||||
if !crate::util::is_valid_scoreboard_objective_name(&name_eval) {
|
if !crate::util::is_valid_scoreboard_objective_name(&name_eval) {
|
||||||
|
|
Loading…
Reference in New Issue