basic transpilation of template string working
This commit is contained in:
		
							parent
							
								
									ef7bf95447
								
							
						
					
					
						commit
						6043a4add5
					
				| 
						 | 
					@ -35,13 +35,14 @@ pathdiff = "0.2.3"
 | 
				
			||||||
serde = { version = "1.0.217", features = ["derive"], optional = true }
 | 
					serde = { version = "1.0.217", features = ["derive"], optional = true }
 | 
				
			||||||
serde_json = { version = "1.0.138", optional = true }
 | 
					serde_json = { version = "1.0.138", optional = true }
 | 
				
			||||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
 | 
					# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
 | 
				
			||||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "6ff544131b2518b8c92bc4d2de7682efd7141ec4", default-features = false, optional = true }
 | 
					shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "89709834da6f39840caa9c6a2eadbdececdc7d44", default-features = false, optional = true }
 | 
				
			||||||
strsim = "0.11.1"
 | 
					strsim = "0.11.1"
 | 
				
			||||||
strum = { version = "0.27.0", features = ["derive"] }
 | 
					strum = { version = "0.27.0", features = ["derive"] }
 | 
				
			||||||
thiserror = "2.0.11"
 | 
					thiserror = "2.0.11"
 | 
				
			||||||
tracing = "0.1.41"
 | 
					tracing = "0.1.41"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
 | 
					assert-struct = "0.2.0"
 | 
				
			||||||
serde_json = "1.0.138"
 | 
					serde_json = "1.0.138"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[[example]]
 | 
					[[example]]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -219,6 +219,12 @@ impl std::hash::Hash for Span {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SourceElement for Span {
 | 
				
			||||||
 | 
					    fn span(&self) -> Span {
 | 
				
			||||||
 | 
					        self.clone()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Span {
 | 
					impl Span {
 | 
				
			||||||
    /// Create a span from the given start and end byte indices in the source file.
 | 
					    /// Create a span from the given start and end byte indices in the source file.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,7 +81,10 @@ impl Display for ConflictingFunctionNames {
 | 
				
			||||||
            "{}",
 | 
					            "{}",
 | 
				
			||||||
            Message::new(
 | 
					            Message::new(
 | 
				
			||||||
                Severity::Error,
 | 
					                Severity::Error,
 | 
				
			||||||
                format!("the following function declaration conflicts with an existing function with name `{}`", self.name)
 | 
					                format!(
 | 
				
			||||||
 | 
					                    "the following function declaration conflicts with an existing function with name `{}`",
 | 
				
			||||||
 | 
					                    self.name
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,13 +173,12 @@ impl Function {
 | 
				
			||||||
            if let Some(incompatible) = self.annotations().iter().find(|a| {
 | 
					            if let Some(incompatible) = self.annotations().iter().find(|a| {
 | 
				
			||||||
                ["tick", "load", "uninstall"].contains(&a.assignment().identifier.span.str())
 | 
					                ["tick", "load", "uninstall"].contains(&a.assignment().identifier.span.str())
 | 
				
			||||||
            }) {
 | 
					            }) {
 | 
				
			||||||
                let err =
 | 
					                let err = error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
 | 
				
			||||||
                    error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
 | 
					                    span: incompatible.assignment().identifier.span.clone(),
 | 
				
			||||||
                        span: incompatible.assignment().identifier.span.clone(),
 | 
					                    reason:
 | 
				
			||||||
                        reason:
 | 
					                        "functions with the `tick`, `load` or `uninstall` annotation cannot have parameters"
 | 
				
			||||||
                            "functions with the `tick`, `load` or `uninstall` annotation cannot have parameters"
 | 
					                            .to_string(),
 | 
				
			||||||
                                .to_string(),
 | 
					                });
 | 
				
			||||||
                    });
 | 
					 | 
				
			||||||
                handler.receive(err.clone());
 | 
					                handler.receive(err.clone());
 | 
				
			||||||
                return Err(err);
 | 
					                return Err(err);
 | 
				
			||||||
            } else if parameters
 | 
					            } else if parameters
 | 
				
			||||||
| 
						 | 
					@ -191,13 +190,12 @@ impl Function {
 | 
				
			||||||
                    .iter()
 | 
					                    .iter()
 | 
				
			||||||
                    .find(|a| a.assignment().identifier.span.str() == "deobfuscate")
 | 
					                    .find(|a| a.assignment().identifier.span.str() == "deobfuscate")
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    let err =
 | 
					                    let err = error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
 | 
				
			||||||
                            error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
 | 
					                        span: incompatible.assignment().identifier.span.clone(),
 | 
				
			||||||
                                span: incompatible.assignment().identifier.span.clone(),
 | 
					                        reason:
 | 
				
			||||||
                                reason:
 | 
					                            "functions with the `deobfuscate` annotation cannot have compile-time parameters"
 | 
				
			||||||
                                    "functions with the `deobfuscate` annotation cannot have compile-time parameters"
 | 
					                                .to_string(),
 | 
				
			||||||
                                        .to_string(),
 | 
					                    });
 | 
				
			||||||
                            });
 | 
					 | 
				
			||||||
                    handler.receive(err.clone());
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
                    return Err(err);
 | 
					                    return Err(err);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -415,6 +415,14 @@ impl TemplateStringLiteral {
 | 
				
			||||||
    pub fn parts(&self) -> &[TemplateStringLiteralPart] {
 | 
					    pub fn parts(&self) -> &[TemplateStringLiteralPart] {
 | 
				
			||||||
        &self.parts
 | 
					        &self.parts
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns whether the template string literal contains any expressions.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn contains_expression(&self) -> bool {
 | 
				
			||||||
 | 
					        self.parts
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .any(|part| matches!(part, TemplateStringLiteralPart::Expression { .. }))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SourceElement for TemplateStringLiteral {
 | 
					impl SourceElement for TemplateStringLiteral {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -828,13 +828,15 @@ impl Parser<'_> {
 | 
				
			||||||
                    parser.parse_statement(handler).map_or_else(
 | 
					                    parser.parse_statement(handler).map_or_else(
 | 
				
			||||||
                        |_| {
 | 
					                        |_| {
 | 
				
			||||||
                            // error recovery
 | 
					                            // error recovery
 | 
				
			||||||
                            parser.stop_at(|reading| matches!(
 | 
					                            parser.stop_at(|reading| {
 | 
				
			||||||
                                reading,
 | 
					                                matches!(
 | 
				
			||||||
                                Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == ';'
 | 
					                                    reading,
 | 
				
			||||||
                            ) || matches!(
 | 
					                                    Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == ';'
 | 
				
			||||||
                                reading,
 | 
					                                ) || matches!(
 | 
				
			||||||
                                Reading::IntoDelimited(punc) if punc.punctuation == '{'
 | 
					                                    reading,
 | 
				
			||||||
                            ));
 | 
					                                    Reading::IntoDelimited(punc) if punc.punctuation == '{'
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            // goes after the semicolon or the open brace
 | 
					                            // goes after the semicolon or the open brace
 | 
				
			||||||
                            parser.forward();
 | 
					                            parser.forward();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,26 +1,37 @@
 | 
				
			||||||
//! Conversion functions for converting between tokens/ast-nodes and [`shulkerbox`] types
 | 
					//! Conversion functions for converting between tokens/ast-nodes and [`shulkerbox`] types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{borrow::Cow, sync::Arc};
 | 
					use std::{borrow::Cow, collections::BTreeMap, sync::Arc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use shulkerbox::util::{MacroString as ExtMacroString, MacroStringPart as ExtMacroStringPart};
 | 
					use shulkerbox::{
 | 
				
			||||||
 | 
					    prelude::Command,
 | 
				
			||||||
 | 
					    util::{MacroString as ExtMacroString, MacroStringPart as ExtMacroStringPart},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::{self, Handler},
 | 
					    base::{self, source_file::Span, Handler},
 | 
				
			||||||
    semantic::error::UnexpectedExpression,
 | 
					    semantic::error::UnexpectedExpression,
 | 
				
			||||||
    syntax::syntax_tree::expression::{TemplateStringLiteral, TemplateStringLiteralPart},
 | 
					    syntax::syntax_tree::expression::{TemplateStringLiteral, TemplateStringLiteralPart},
 | 
				
			||||||
    transpile::{Scope, TranspileError, TranspileResult},
 | 
					    transpile::{expression::DataLocation, Scope, TranspileError, TranspileResult},
 | 
				
			||||||
    util,
 | 
					    util,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::util::{MacroString, MacroStringPart};
 | 
					use super::util::{MacroString, MacroStringPart};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<MacroString> for ExtMacroString {
 | 
					type ShulkerboxMacroStringMap = BTreeMap<String, (DataLocation, Vec<Command>, Span)>;
 | 
				
			||||||
    fn from(value: MacroString) -> Self {
 | 
					
 | 
				
			||||||
        match value {
 | 
					impl MacroString {
 | 
				
			||||||
            MacroString::String(s) => Self::String(s),
 | 
					    pub fn into_sb(self) -> (ExtMacroString, ShulkerboxMacroStringMap) {
 | 
				
			||||||
            MacroString::MacroString(parts) => {
 | 
					        match self {
 | 
				
			||||||
                Self::MacroString(parts.into_iter().map(ExtMacroStringPart::from).collect())
 | 
					            Self::String(s) => (ExtMacroString::String(s), BTreeMap::new()),
 | 
				
			||||||
            }
 | 
					            Self::MacroString {
 | 
				
			||||||
 | 
					                parts,
 | 
				
			||||||
 | 
					                prepare_variables,
 | 
				
			||||||
 | 
					            } => (
 | 
				
			||||||
 | 
					                ExtMacroString::MacroString(
 | 
				
			||||||
 | 
					                    parts.into_iter().map(ExtMacroStringPart::from).collect(),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                prepare_variables,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -465,10 +465,7 @@ impl Display for IllegalIndexingReason {
 | 
				
			||||||
                write!(f, "The expression cannot be indexed.")
 | 
					                write!(f, "The expression cannot be indexed.")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::InvalidComptimeType { expected } => {
 | 
					            Self::InvalidComptimeType { expected } => {
 | 
				
			||||||
                write!(
 | 
					                write!(f, "The expression can only be indexed with type {expected} that can be evaluated at compile time.")
 | 
				
			||||||
                    f,
 | 
					 | 
				
			||||||
                    "The expression can only be indexed with type {expected} that can be evaluated at compile time."
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::IndexOutOfBounds { index, length } => {
 | 
					            Self::IndexOutOfBounds { index, length } => {
 | 
				
			||||||
                write!(
 | 
					                write!(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,6 @@
 | 
				
			||||||
//! The expression transpiler.
 | 
					//! The expression transpiler.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{fmt::Display, string::ToString};
 | 
					use std::fmt::Display;
 | 
				
			||||||
 | 
					 | 
				
			||||||
use super::util::MacroString;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use enum_as_inner::EnumAsInner;
 | 
					use enum_as_inner::EnumAsInner;
 | 
				
			||||||
| 
						 | 
					@ -15,6 +13,7 @@ use super::{
 | 
				
			||||||
    error::{
 | 
					    error::{
 | 
				
			||||||
        IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
 | 
					        IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    util::MacroString,
 | 
				
			||||||
    Scope, TranspileResult, Transpiler, VariableData,
 | 
					    Scope, TranspileResult, Transpiler, VariableData,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
| 
						 | 
					@ -35,6 +34,7 @@ use crate::{
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Compile-time evaluated value
 | 
					/// Compile-time evaluated value
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[allow(missing_docs)]
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
pub enum ComptimeValue {
 | 
					pub enum ComptimeValue {
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,7 @@ pub enum ComptimeValue {
 | 
				
			||||||
    MacroString(MacroString),
 | 
					    MacroString(MacroString),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl ComptimeValue {
 | 
					impl ComptimeValue {
 | 
				
			||||||
    /// Returns the value as a string not containing a macro.
 | 
					    /// Returns the value as a string not containing a macro.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
| 
						 | 
					@ -152,7 +153,8 @@ impl From<ValueType> for ExpectedType {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Location of data
 | 
					/// Location of data
 | 
				
			||||||
#[allow(missing_docs)]
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum DataLocation {
 | 
					pub enum DataLocation {
 | 
				
			||||||
    ScoreboardValue {
 | 
					    ScoreboardValue {
 | 
				
			||||||
        objective: String,
 | 
					        objective: String,
 | 
				
			||||||
| 
						 | 
					@ -179,21 +181,34 @@ impl DataLocation {
 | 
				
			||||||
            Self::Storage { r#type, .. } => match r#type {
 | 
					            Self::Storage { r#type, .. } => match r#type {
 | 
				
			||||||
                StorageType::Boolean => ValueType::Boolean,
 | 
					                StorageType::Boolean => ValueType::Boolean,
 | 
				
			||||||
                StorageType::Byte | StorageType::Int | StorageType::Long => ValueType::Integer,
 | 
					                StorageType::Byte | StorageType::Int | StorageType::Long => ValueType::Integer,
 | 
				
			||||||
 | 
					                StorageType::String => ValueType::String,
 | 
				
			||||||
                StorageType::Double => todo!("Double storage type"),
 | 
					                StorageType::Double => todo!("Double storage type"),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns the storage type of the data location.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn storage_type(&self) -> StorageType {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::ScoreboardValue { .. } => StorageType::Int,
 | 
				
			||||||
 | 
					            Self::Tag { .. } => StorageType::Boolean,
 | 
				
			||||||
 | 
					            Self::Storage { r#type, .. } => *r#type,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The type of a storage.
 | 
					/// The type of a storage.
 | 
				
			||||||
#[allow(missing_docs)]
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum StorageType {
 | 
					pub enum StorageType {
 | 
				
			||||||
    Boolean,
 | 
					    Boolean,
 | 
				
			||||||
    Byte,
 | 
					    Byte,
 | 
				
			||||||
    Int,
 | 
					    Int,
 | 
				
			||||||
    Long,
 | 
					    Long,
 | 
				
			||||||
    Double,
 | 
					    Double,
 | 
				
			||||||
 | 
					    String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl StorageType {
 | 
					impl StorageType {
 | 
				
			||||||
| 
						 | 
					@ -205,6 +220,7 @@ impl StorageType {
 | 
				
			||||||
            Self::Int => "",
 | 
					            Self::Int => "",
 | 
				
			||||||
            Self::Long => "l",
 | 
					            Self::Long => "l",
 | 
				
			||||||
            Self::Double => "d",
 | 
					            Self::Double => "d",
 | 
				
			||||||
 | 
					            Self::String => "",
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,6 +232,7 @@ impl StorageType {
 | 
				
			||||||
            Self::Int => "int",
 | 
					            Self::Int => "int",
 | 
				
			||||||
            Self::Long => "long",
 | 
					            Self::Long => "long",
 | 
				
			||||||
            Self::Double => "double",
 | 
					            Self::Double => "double",
 | 
				
			||||||
 | 
					            Self::String => "string",
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -413,15 +430,9 @@ impl Primary {
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .and_then(|val| val),
 | 
					                .and_then(|val| val),
 | 
				
			||||||
            Self::TemplateStringLiteral(template_string_literal) => {
 | 
					            Self::TemplateStringLiteral(template_string_literal) => {
 | 
				
			||||||
                use crate::syntax::syntax_tree::expression::TemplateStringLiteralPart;
 | 
					                if template_string_literal.contains_expression() {
 | 
				
			||||||
 | 
					 | 
				
			||||||
                if template_string_literal
 | 
					 | 
				
			||||||
                    .parts()
 | 
					 | 
				
			||||||
                    .iter()
 | 
					 | 
				
			||||||
                    .any(|part| matches!(part, TemplateStringLiteralPart::Expression { .. }))
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    template_string_literal
 | 
					                    template_string_literal
 | 
				
			||||||
                        .to_macro_string(scope, handler)
 | 
					                        .to_macro_string(None, scope, handler)
 | 
				
			||||||
                        .map(ComptimeValue::MacroString)
 | 
					                        .map(ComptimeValue::MacroString)
 | 
				
			||||||
                        .map_err(|_| NotComptime {
 | 
					                        .map_err(|_| NotComptime {
 | 
				
			||||||
                            expression: template_string_literal.span(),
 | 
					                            expression: template_string_literal.span(),
 | 
				
			||||||
| 
						 | 
					@ -1310,7 +1321,9 @@ impl Transpiler {
 | 
				
			||||||
                            handler,
 | 
					                            handler,
 | 
				
			||||||
                        )?;
 | 
					                        )?;
 | 
				
			||||||
                        self.initialize_constant_score(-1);
 | 
					                        self.initialize_constant_score(-1);
 | 
				
			||||||
                        let negate_cmd = Command::Raw(format!("scoreboard players operation {score_target} {objective} *= -1 shu_constants"));
 | 
					                        let negate_cmd = Command::Raw(format!(
 | 
				
			||||||
 | 
					                            "scoreboard players operation {score_target} {objective} *= -1 shu_constants"
 | 
				
			||||||
 | 
					                        ));
 | 
				
			||||||
                        expr_cmds.push(negate_cmd);
 | 
					                        expr_cmds.push(negate_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        Ok(expr_cmds)
 | 
					                        Ok(expr_cmds)
 | 
				
			||||||
| 
						 | 
					@ -1377,7 +1390,9 @@ impl Transpiler {
 | 
				
			||||||
                    let run_cmd = if run_cmds.len() == 1 {
 | 
					                    let run_cmd = if run_cmds.len() == 1 {
 | 
				
			||||||
                        run_cmds.into_iter().next().expect("length is 1")
 | 
					                        run_cmds.into_iter().next().expect("length is 1")
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        Command::Group(run_cmds)
 | 
					                        use shulkerbox::datapack::Group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Command::Group(Group::new(run_cmds))
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    match target {
 | 
					                    match target {
 | 
				
			||||||
                        DataLocation::ScoreboardValue { objective, target } => {
 | 
					                        DataLocation::ScoreboardValue { objective, target } => {
 | 
				
			||||||
| 
						 | 
					@ -1416,12 +1431,18 @@ impl Transpiler {
 | 
				
			||||||
                                self.get_temp_storage_locations_array();
 | 
					                                self.get_temp_storage_locations_array();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            let store_cmd = Command::Execute(Execute::Store(
 | 
					                            let store_cmd = Command::Execute(Execute::Store(
 | 
				
			||||||
                                format!("success storage {temp_storage_name} {temp_storage_path} boolean 1.0").into(), 
 | 
					                                format!(
 | 
				
			||||||
                                Box::new(Execute::Run(Box::new(run_cmd)))
 | 
					                                    "success storage {temp_storage_name} {temp_storage_path} boolean 1.0"
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                                .into(),
 | 
				
			||||||
 | 
					                                Box::new(Execute::Run(Box::new(run_cmd))),
 | 
				
			||||||
                            ));
 | 
					                            ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            let if_cmd = Command::Execute(Execute::If(
 | 
					                            let if_cmd = Command::Execute(Execute::If(
 | 
				
			||||||
                                Condition::Atom(format!("data storage {temp_storage_name} {{{temp_storage_name}:1b}}").into()),
 | 
					                                Condition::Atom(
 | 
				
			||||||
 | 
					                                    format!("data storage {temp_storage_name} {{{temp_storage_name}:1b}}")
 | 
				
			||||||
 | 
					                                        .into(),
 | 
				
			||||||
 | 
					                                ),
 | 
				
			||||||
                                Box::new(Execute::Run(Box::new(success_cmd))),
 | 
					                                Box::new(Execute::Run(Box::new(success_cmd))),
 | 
				
			||||||
                                None,
 | 
					                                None,
 | 
				
			||||||
                            ));
 | 
					                            ));
 | 
				
			||||||
| 
						 | 
					@ -1676,12 +1697,15 @@ impl Transpiler {
 | 
				
			||||||
                Vec::new(),
 | 
					                Vec::new(),
 | 
				
			||||||
                ExtendedCondition::Runtime(Condition::Atom(s.str_content().to_string().into())),
 | 
					                ExtendedCondition::Runtime(Condition::Atom(s.str_content().to_string().into())),
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
            Primary::TemplateStringLiteral(template_string) => Ok((
 | 
					            Primary::TemplateStringLiteral(template_string) => {
 | 
				
			||||||
                Vec::new(),
 | 
					                let (macro_string, prepare_variables) = template_string
 | 
				
			||||||
                ExtendedCondition::Runtime(Condition::Atom(
 | 
					                    .to_macro_string(Some(self), scope, handler)?
 | 
				
			||||||
                    template_string.to_macro_string(scope, handler)?.into(),
 | 
					                    .into_sb();
 | 
				
			||||||
                )),
 | 
					                Ok((
 | 
				
			||||||
            )),
 | 
					                    Vec::new(),
 | 
				
			||||||
 | 
					                    ExtendedCondition::Runtime(Condition::Atom(macro_string)),
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            Primary::FunctionCall(func) => {
 | 
					            Primary::FunctionCall(func) => {
 | 
				
			||||||
                if func
 | 
					                if func
 | 
				
			||||||
                    .arguments()
 | 
					                    .arguments()
 | 
				
			||||||
| 
						 | 
					@ -1858,13 +1882,16 @@ impl Transpiler {
 | 
				
			||||||
                        ComptimeValue::String(s) => Ok((
 | 
					                        ComptimeValue::String(s) => Ok((
 | 
				
			||||||
                            Vec::new(),
 | 
					                            Vec::new(),
 | 
				
			||||||
                            ExtendedCondition::Runtime(Condition::Atom(
 | 
					                            ExtendedCondition::Runtime(Condition::Atom(
 | 
				
			||||||
                                MacroString::String(s).into(),
 | 
					                                shulkerbox::util::MacroString::String(s),
 | 
				
			||||||
                            )),
 | 
					                            )),
 | 
				
			||||||
                        )),
 | 
					                        )),
 | 
				
			||||||
                        ComptimeValue::MacroString(s) => Ok((
 | 
					                        ComptimeValue::MacroString(s) => {
 | 
				
			||||||
                            Vec::new(),
 | 
					                            let (macro_string, prepare_variables) = s.into_sb();
 | 
				
			||||||
                            ExtendedCondition::Runtime(Condition::Atom(s.into())),
 | 
					                            Ok((
 | 
				
			||||||
                        )),
 | 
					                                Vec::new(),
 | 
				
			||||||
 | 
					                                ExtendedCondition::Runtime(Condition::Atom(macro_string)),
 | 
				
			||||||
 | 
					                            ))
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
            Primary::Prefix(prefix) => match prefix.operator() {
 | 
					            Primary::Prefix(prefix) => match prefix.operator() {
 | 
				
			||||||
| 
						 | 
					@ -1917,10 +1944,13 @@ impl Transpiler {
 | 
				
			||||||
                    Vec::new(),
 | 
					                    Vec::new(),
 | 
				
			||||||
                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
					                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
				
			||||||
                )),
 | 
					                )),
 | 
				
			||||||
                Ok(ComptimeValue::MacroString(value)) => Ok((
 | 
					                Ok(ComptimeValue::MacroString(value)) => {
 | 
				
			||||||
                    Vec::new(),
 | 
					                    let (macro_string, prepare_variables) = value.into_sb();
 | 
				
			||||||
                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
					                    Ok((
 | 
				
			||||||
                )),
 | 
					                        Vec::new(),
 | 
				
			||||||
 | 
					                        ExtendedCondition::Runtime(Condition::Atom(macro_string)),
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                Ok(ComptimeValue::Boolean(boolean)) => {
 | 
					                Ok(ComptimeValue::Boolean(boolean)) => {
 | 
				
			||||||
                    Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
 | 
					                    Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -2078,6 +2108,14 @@ impl Transpiler {
 | 
				
			||||||
                    handler.receive(Box::new(err.clone()));
 | 
					                    handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                    return Err(err);
 | 
					                    return Err(err);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                StorageType::String => {
 | 
				
			||||||
 | 
					                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                        expected_type: ExpectedType::String,
 | 
				
			||||||
 | 
					                        expression: binary.span(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					                    return Err(err);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2204,6 +2242,7 @@ impl Transpiler {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
    fn store_comptime_value(
 | 
					    fn store_comptime_value(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        value: &ComptimeValue,
 | 
					        value: &ComptimeValue,
 | 
				
			||||||
| 
						 | 
					@ -2294,12 +2333,15 @@ impl Transpiler {
 | 
				
			||||||
                        r#type: StorageType::Boolean,
 | 
					                        r#type: StorageType::Boolean,
 | 
				
			||||||
                        ..
 | 
					                        ..
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    | DataLocation::Tag { .. } => self.store_condition_success(
 | 
					                    | DataLocation::Tag { .. } => {
 | 
				
			||||||
                        ExtendedCondition::Runtime(Condition::Atom(value.clone().into())),
 | 
					                        let (macro_string, prepare_variables) = value.clone().into_sb();
 | 
				
			||||||
                        target,
 | 
					                        self.store_condition_success(
 | 
				
			||||||
                        source,
 | 
					                            ExtendedCondition::Runtime(Condition::Atom(macro_string)),
 | 
				
			||||||
                        handler,
 | 
					                            target,
 | 
				
			||||||
                    ),
 | 
					                            source,
 | 
				
			||||||
 | 
					                            handler,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    // DataLocation::Storage { storage_name, path, r#type: StorageType::String } => todo!("implement storage string")
 | 
					                    // DataLocation::Storage { storage_name, path, r#type: StorageType::String } => todo!("implement storage string")
 | 
				
			||||||
                    _ => {
 | 
					                    _ => {
 | 
				
			||||||
                        let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                        let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
| 
						 | 
					@ -2370,6 +2412,12 @@ impl Transpiler {
 | 
				
			||||||
        Ok(vec![cmd])
 | 
					        Ok(vec![cmd])
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(super) fn get_temp_count(&mut self, amount: usize) -> usize {
 | 
				
			||||||
 | 
					        let current = self.temp_counter;
 | 
				
			||||||
 | 
					        self.temp_counter = self.temp_counter.wrapping_add(amount);
 | 
				
			||||||
 | 
					        current
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Get temporary scoreboard locations.
 | 
					    /// Get temporary scoreboard locations.
 | 
				
			||||||
    pub(super) fn get_temp_scoreboard_locations(&mut self, amount: usize) -> (String, Vec<String>) {
 | 
					    pub(super) fn get_temp_scoreboard_locations(&mut self, amount: usize) -> (String, Vec<String>) {
 | 
				
			||||||
        let objective = "shu_temp_".to_string()
 | 
					        let objective = "shu_temp_".to_string()
 | 
				
			||||||
| 
						 | 
					@ -2378,20 +2426,20 @@ impl Transpiler {
 | 
				
			||||||
        self.datapack
 | 
					        self.datapack
 | 
				
			||||||
            .register_scoreboard(&objective, None::<&str>, None::<&str>);
 | 
					            .register_scoreboard(&objective, None::<&str>, None::<&str>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let temp_count = self.get_temp_count(amount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let targets = (0..amount)
 | 
					        let targets = (0..amount)
 | 
				
			||||||
            .map(|i| {
 | 
					            .map(|i| {
 | 
				
			||||||
                chksum_md5::hash(format!(
 | 
					                chksum_md5::hash(format!(
 | 
				
			||||||
                    "{namespace}\0{j}",
 | 
					                    "{namespace}\0{j}",
 | 
				
			||||||
                    namespace = self.main_namespace_name,
 | 
					                    namespace = self.main_namespace_name,
 | 
				
			||||||
                    j = i + self.temp_counter
 | 
					                    j = i + temp_count
 | 
				
			||||||
                ))
 | 
					                ))
 | 
				
			||||||
                .to_hex_lowercase()
 | 
					                .to_hex_lowercase()
 | 
				
			||||||
                .split_off(16)
 | 
					                .split_off(16)
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .collect();
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.temp_counter = self.temp_counter.wrapping_add(amount);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        (objective, targets)
 | 
					        (objective, targets)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2411,20 +2459,20 @@ impl Transpiler {
 | 
				
			||||||
        let storage_name = "shulkerscript:temp_".to_string()
 | 
					        let storage_name = "shulkerscript:temp_".to_string()
 | 
				
			||||||
            + &chksum_md5::hash(&self.main_namespace_name).to_hex_lowercase();
 | 
					            + &chksum_md5::hash(&self.main_namespace_name).to_hex_lowercase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let temp_count = self.get_temp_count(amount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let paths = (0..amount)
 | 
					        let paths = (0..amount)
 | 
				
			||||||
            .map(|i| {
 | 
					            .map(|i| {
 | 
				
			||||||
                chksum_md5::hash(format!(
 | 
					                chksum_md5::hash(format!(
 | 
				
			||||||
                    "{namespace}\0{j}",
 | 
					                    "{namespace}\0{j}",
 | 
				
			||||||
                    namespace = self.main_namespace_name,
 | 
					                    namespace = self.main_namespace_name,
 | 
				
			||||||
                    j = i + self.temp_counter
 | 
					                    j = i + temp_count
 | 
				
			||||||
                ))
 | 
					                ))
 | 
				
			||||||
                .to_hex_lowercase()
 | 
					                .to_hex_lowercase()
 | 
				
			||||||
                .split_off(16)
 | 
					                .split_off(16)
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .collect::<Vec<_>>();
 | 
					            .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.temp_counter = self.temp_counter.wrapping_add(amount);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.temp_data_storage_locations.extend(
 | 
					        self.temp_data_storage_locations.extend(
 | 
				
			||||||
            paths
 | 
					            paths
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -449,21 +449,29 @@ impl Transpiler {
 | 
				
			||||||
                                Ok(Parameter::Static(string.str_content().to_string().into()))
 | 
					                                Ok(Parameter::Static(string.str_content().to_string().into()))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            Expression::Primary(Primary::TemplateStringLiteral(literal)) => {
 | 
					                            Expression::Primary(Primary::TemplateStringLiteral(literal)) => {
 | 
				
			||||||
                                Ok(Parameter::Static(literal.to_macro_string(scope, handler)?))
 | 
					                                Ok(Parameter::Static(literal.to_macro_string(
 | 
				
			||||||
 | 
					                                    Some(self),
 | 
				
			||||||
 | 
					                                    scope,
 | 
				
			||||||
 | 
					                                    handler,
 | 
				
			||||||
 | 
					                                )?))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            Expression::Primary(primary @ Primary::Identifier(ident)) => {
 | 
					                            Expression::Primary(primary @ Primary::Identifier(ident)) => {
 | 
				
			||||||
                                let var =
 | 
					                                let var =
 | 
				
			||||||
                                    scope.get_variable(ident.span.str()).ok_or_else(|| {
 | 
					                                    scope.get_variable(ident.span.str()).ok_or_else(|| {
 | 
				
			||||||
                                        let err =
 | 
					                                        let err = TranspileError::UnknownIdentifier(
 | 
				
			||||||
                                            TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(ident.span(), scope));
 | 
					                                            UnknownIdentifier::from_scope(ident.span(), scope),
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
                                        handler.receive(Box::new(err.clone()));
 | 
					                                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                                        err
 | 
					                                        err
 | 
				
			||||||
                                    })?;
 | 
					                                    })?;
 | 
				
			||||||
                                match var.as_ref() {
 | 
					                                match var.as_ref() {
 | 
				
			||||||
                                    VariableData::MacroParameter { macro_name, .. } => {
 | 
					                                    VariableData::MacroParameter { macro_name, .. } => {
 | 
				
			||||||
                                        Ok(Parameter::Static(MacroString::MacroString(vec![
 | 
					                                        Ok(Parameter::Static(MacroString::MacroString {
 | 
				
			||||||
                                            MacroStringPart::MacroUsage(macro_name.clone()),
 | 
					                                            parts: vec![MacroStringPart::MacroUsage(
 | 
				
			||||||
                                        ])))
 | 
					                                                macro_name.clone(),
 | 
				
			||||||
 | 
					                                            )],
 | 
				
			||||||
 | 
					                                            prepare_variables: BTreeMap::new(),
 | 
				
			||||||
 | 
					                                        }))
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                    VariableData::BooleanStorage { .. }
 | 
					                                    VariableData::BooleanStorage { .. }
 | 
				
			||||||
| 
						 | 
					@ -605,7 +613,10 @@ impl Transpiler {
 | 
				
			||||||
                                                    crate::util::escape_str(&value).to_string(),
 | 
					                                                    crate::util::escape_str(&value).to_string(),
 | 
				
			||||||
                                                ),
 | 
					                                                ),
 | 
				
			||||||
                                            ),
 | 
					                                            ),
 | 
				
			||||||
                                            MacroString::MacroString(parts) => {
 | 
					                                            MacroString::MacroString {
 | 
				
			||||||
 | 
					                                                parts,
 | 
				
			||||||
 | 
					                                                prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                                            } => {
 | 
				
			||||||
                                                let parts = parts
 | 
					                                                let parts = parts
 | 
				
			||||||
                                                    .into_iter()
 | 
					                                                    .into_iter()
 | 
				
			||||||
                                                    .map(|part| match part {
 | 
					                                                    .map(|part| match part {
 | 
				
			||||||
| 
						 | 
					@ -622,7 +633,10 @@ impl Transpiler {
 | 
				
			||||||
                                                    .collect();
 | 
					                                                    .collect();
 | 
				
			||||||
                                                statics.insert(
 | 
					                                                statics.insert(
 | 
				
			||||||
                                                    arg_name.to_string(),
 | 
					                                                    arg_name.to_string(),
 | 
				
			||||||
                                                    MacroString::MacroString(parts),
 | 
					                                                    MacroString::MacroString {
 | 
				
			||||||
 | 
					                                                        parts,
 | 
				
			||||||
 | 
					                                                        prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                                                    },
 | 
				
			||||||
                                                )
 | 
					                                                )
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
                                        };
 | 
					                                        };
 | 
				
			||||||
| 
						 | 
					@ -634,9 +648,9 @@ impl Transpiler {
 | 
				
			||||||
                                    } => {
 | 
					                                    } => {
 | 
				
			||||||
                                        require_dyn_params = true;
 | 
					                                        require_dyn_params = true;
 | 
				
			||||||
                                        setup_cmds.extend(prepare_cmds);
 | 
					                                        setup_cmds.extend(prepare_cmds);
 | 
				
			||||||
                                        move_cmds.push(Command::Raw(
 | 
					                                        move_cmds.push(Command::Raw(format!(
 | 
				
			||||||
                                                format!(r"data modify storage shulkerscript:function_arguments {arg_name} set from storage {storage_name} {path}")
 | 
					                                            r"data modify storage shulkerscript:function_arguments {arg_name} set from storage {storage_name} {path}"
 | 
				
			||||||
                                            ));
 | 
					                                        )));
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
| 
						 | 
					@ -654,7 +668,9 @@ impl Transpiler {
 | 
				
			||||||
                                    Parameter::Static(s) => match s.as_str() {
 | 
					                                    Parameter::Static(s) => match s.as_str() {
 | 
				
			||||||
                                        Ok(s) => {
 | 
					                                        Ok(s) => {
 | 
				
			||||||
                                            if s.parse::<i32>().is_ok() {
 | 
					                                            if s.parse::<i32>().is_ok() {
 | 
				
			||||||
                                                move_cmds.push(Command::Raw(format!(r"scoreboard players set {target} {objective} {s}")));
 | 
					                                                move_cmds.push(Command::Raw(format!(
 | 
				
			||||||
 | 
					                                                    r"scoreboard players set {target} {objective} {s}"
 | 
				
			||||||
 | 
					                                                )));
 | 
				
			||||||
                                            } else {
 | 
					                                            } else {
 | 
				
			||||||
                                                let err = TranspileError::MismatchedTypes(
 | 
					                                                let err = TranspileError::MismatchedTypes(
 | 
				
			||||||
                                                    MismatchedTypes {
 | 
					                                                    MismatchedTypes {
 | 
				
			||||||
| 
						 | 
					@ -666,11 +682,19 @@ impl Transpiler {
 | 
				
			||||||
                                                return Err(err);
 | 
					                                                return Err(err);
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                        Err(parts) => {
 | 
					                                        Err((parts, preparation_variables)) => {
 | 
				
			||||||
                                            move_cmds.push(Command::UsesMacro(MacroString::MacroString(
 | 
					                                            let (macro_string, preparation_variables) = MacroString::MacroString {
 | 
				
			||||||
                                                        std::iter::once(MacroStringPart::String(format!("scoreboard players set {target} {objective} ")))
 | 
					                                                parts: std::iter::once(MacroStringPart::String(
 | 
				
			||||||
                                                        .chain(parts.iter().cloned()).collect()
 | 
					                                                    format!(
 | 
				
			||||||
                                                    ).into()));
 | 
					                                                        "scoreboard players set {target} {objective} "
 | 
				
			||||||
 | 
					                                                    ),
 | 
				
			||||||
 | 
					                                                ))
 | 
				
			||||||
 | 
					                                                .chain(parts.iter().cloned())
 | 
				
			||||||
 | 
					                                                .collect(),
 | 
				
			||||||
 | 
					                                                prepare_variables: preparation_variables.to_owned(),
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            .into_sb();
 | 
				
			||||||
 | 
					                                            move_cmds.push(Command::UsesMacro(macro_string));
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                    },
 | 
					                                    },
 | 
				
			||||||
                                    Parameter::Storage {
 | 
					                                    Parameter::Storage {
 | 
				
			||||||
| 
						 | 
					@ -703,7 +727,10 @@ impl Transpiler {
 | 
				
			||||||
                                    Parameter::Static(s) => match s.as_str() {
 | 
					                                    Parameter::Static(s) => match s.as_str() {
 | 
				
			||||||
                                        Ok(s) => {
 | 
					                                        Ok(s) => {
 | 
				
			||||||
                                            if let Ok(b) = s.parse::<bool>() {
 | 
					                                            if let Ok(b) = s.parse::<bool>() {
 | 
				
			||||||
                                                move_cmds.push(Command::Raw(format!("data modify storage {target_storage_name} {target_path} set value {}", if b { "1b" } else { "0b" })));
 | 
					                                                move_cmds.push(Command::Raw(format!(
 | 
				
			||||||
 | 
					                                                    "data modify storage {target_storage_name} {target_path} set value {}",
 | 
				
			||||||
 | 
					                                                    if b { "1b" } else { "0b" }
 | 
				
			||||||
 | 
					                                                )));
 | 
				
			||||||
                                            } else {
 | 
					                                            } else {
 | 
				
			||||||
                                                let err = TranspileError::MismatchedTypes(
 | 
					                                                let err = TranspileError::MismatchedTypes(
 | 
				
			||||||
                                                    MismatchedTypes {
 | 
					                                                    MismatchedTypes {
 | 
				
			||||||
| 
						 | 
					@ -715,11 +742,15 @@ impl Transpiler {
 | 
				
			||||||
                                                return Err(err);
 | 
					                                                return Err(err);
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                        Err(parts) => {
 | 
					                                        Err((parts, preparation_cmds)) => {
 | 
				
			||||||
                                            move_cmds.push(Command::UsesMacro(MacroString::MacroString(
 | 
					                                            let (macro_string, preparation_variables) = MacroString::MacroString {
 | 
				
			||||||
                                                        std::iter::once(MacroStringPart::String(format!("data modify storage {target_storage_name} {target_path} set value ")))
 | 
					                                                parts: std::iter::once(MacroStringPart::String(format!("data modify storage {target_storage_name} {target_path} set value ")))
 | 
				
			||||||
                                                        .chain(parts.iter().cloned()).collect()
 | 
					                                                    .chain(parts.iter().cloned())
 | 
				
			||||||
                                                    ).into()));
 | 
					                                                    .collect(),
 | 
				
			||||||
 | 
					                                                prepare_variables: preparation_cmds.to_owned(),
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            .into_sb();
 | 
				
			||||||
 | 
					                                            move_cmds.push(Command::UsesMacro(macro_string));
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                    },
 | 
					                                    },
 | 
				
			||||||
                                    Parameter::Storage {
 | 
					                                    Parameter::Storage {
 | 
				
			||||||
| 
						 | 
					@ -756,7 +787,10 @@ impl Transpiler {
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                        MacroString::String(s)
 | 
					                                        MacroString::String(s)
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    MacroString::MacroString(mut parts) => {
 | 
					                                    MacroString::MacroString {
 | 
				
			||||||
 | 
					                                        mut parts,
 | 
				
			||||||
 | 
					                                        prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                                    } => {
 | 
				
			||||||
                                        parts.insert(
 | 
					                                        parts.insert(
 | 
				
			||||||
                                            0,
 | 
					                                            0,
 | 
				
			||||||
                                            MacroStringPart::String(format!(r#"{k}:""#)),
 | 
					                                            MacroStringPart::String(format!(r#"{k}:""#)),
 | 
				
			||||||
| 
						 | 
					@ -766,7 +800,10 @@ impl Transpiler {
 | 
				
			||||||
                                            ending.push(',');
 | 
					                                            ending.push(',');
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                        parts.push(MacroStringPart::String(ending));
 | 
					                                        parts.push(MacroStringPart::String(ending));
 | 
				
			||||||
                                        MacroString::MacroString(parts)
 | 
					                                        MacroString::MacroString {
 | 
				
			||||||
 | 
					                                            parts,
 | 
				
			||||||
 | 
					                                            prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                }),
 | 
					                                }),
 | 
				
			||||||
                        );
 | 
					                        );
 | 
				
			||||||
| 
						 | 
					@ -775,18 +812,18 @@ impl Transpiler {
 | 
				
			||||||
                            MacroString::String(s) => Command::Raw(format!(
 | 
					                            MacroString::String(s) => Command::Raw(format!(
 | 
				
			||||||
                                r"data merge storage shulkerscript:function_arguments_{storage_suffix} {{{s}}}"
 | 
					                                r"data merge storage shulkerscript:function_arguments_{storage_suffix} {{{s}}}"
 | 
				
			||||||
                            )),
 | 
					                            )),
 | 
				
			||||||
                            MacroString::MacroString(_) => {
 | 
					                            MacroString::MacroString { .. } => {
 | 
				
			||||||
                                let prefix = MacroString::String(
 | 
					                                let prefix = MacroString::String(format!(
 | 
				
			||||||
                                    format!("data merge storage shulkerscript:function_arguments_{storage_suffix} {{"),
 | 
					                                    "data merge storage shulkerscript:function_arguments_{storage_suffix} {{"
 | 
				
			||||||
                                );
 | 
					                                ));
 | 
				
			||||||
                                Command::UsesMacro(
 | 
					                                let (macro_string, prepare_variables) =
 | 
				
			||||||
                                    super::util::join_macro_strings([
 | 
					                                    super::util::join_macro_strings([
 | 
				
			||||||
                                        prefix,
 | 
					                                        prefix,
 | 
				
			||||||
                                        joined_statics,
 | 
					                                        joined_statics,
 | 
				
			||||||
                                        MacroString::String("}".to_string()),
 | 
					                                        MacroString::String("}".to_string()),
 | 
				
			||||||
                                    ])
 | 
					                                    ])
 | 
				
			||||||
                                    .into(),
 | 
					                                    .into_sb();
 | 
				
			||||||
                                )
 | 
					                                Command::UsesMacro(macro_string)
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        };
 | 
					                        };
 | 
				
			||||||
                        setup_cmds.push(statics_cmd);
 | 
					                        setup_cmds.push(statics_cmd);
 | 
				
			||||||
| 
						 | 
					@ -832,7 +869,7 @@ fn comptime_args_hash(args: &[Option<ComptimeValue>]) -> String {
 | 
				
			||||||
            ComptimeValue::String(s) => Cow::Borrowed(s.as_str()),
 | 
					            ComptimeValue::String(s) => Cow::Borrowed(s.as_str()),
 | 
				
			||||||
            ComptimeValue::MacroString(s) => match s.as_str() {
 | 
					            ComptimeValue::MacroString(s) => match s.as_str() {
 | 
				
			||||||
                Ok(s) => s,
 | 
					                Ok(s) => s,
 | 
				
			||||||
                Err(parts) => {
 | 
					                Err((parts, _)) => {
 | 
				
			||||||
                    let s = parts
 | 
					                    let s = parts
 | 
				
			||||||
                        .iter()
 | 
					                        .iter()
 | 
				
			||||||
                        .map(|p| match p {
 | 
					                        .map(|p| match p {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -589,7 +589,9 @@ fn print_function(
 | 
				
			||||||
    let cmd = format!("tellraw {target} {print_args}");
 | 
					    let cmd = format!("tellraw {target} {print_args}");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let cmd = if contains_macro {
 | 
					    let cmd = if contains_macro {
 | 
				
			||||||
        Command::UsesMacro(cmd.parse::<MacroString>().expect("cannot fail").into())
 | 
					        let (macro_string, prepare_variables) =
 | 
				
			||||||
 | 
					            cmd.parse::<MacroString>().expect("cannot fail").into_sb();
 | 
				
			||||||
 | 
					        Command::UsesMacro(macro_string)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        Command::Raw(cmd)
 | 
					        Command::Raw(cmd)
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,14 @@ use std::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use itertools::Itertools;
 | 
					use itertools::Itertools;
 | 
				
			||||||
use shulkerbox::datapack::{self, Command, Datapack, Execute};
 | 
					use shulkerbox::datapack::{self, Command, Datapack, Execute, Group};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::{self, source_file::SourceElement, Handler},
 | 
					    base::{
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        source_file::{SourceElement, Span},
 | 
				
			||||||
 | 
					        Handler,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    semantic::error::UnexpectedExpression,
 | 
					    semantic::error::UnexpectedExpression,
 | 
				
			||||||
    syntax::syntax_tree::{
 | 
					    syntax::syntax_tree::{
 | 
				
			||||||
        declaration::{Declaration, FunctionVariableType, ImportItems},
 | 
					        declaration::{Declaration, FunctionVariableType, ImportItems},
 | 
				
			||||||
| 
						 | 
					@ -24,6 +28,7 @@ use crate::{
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    transpile::{
 | 
					    transpile::{
 | 
				
			||||||
        error::IllegalAnnotationContent,
 | 
					        error::IllegalAnnotationContent,
 | 
				
			||||||
 | 
					        expression::DataLocation,
 | 
				
			||||||
        util::{MacroString, MacroStringPart},
 | 
					        util::{MacroString, MacroStringPart},
 | 
				
			||||||
        variables::FunctionVariableDataType,
 | 
					        variables::FunctionVariableDataType,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -411,7 +416,7 @@ impl Transpiler {
 | 
				
			||||||
                if commands.is_empty() {
 | 
					                if commands.is_empty() {
 | 
				
			||||||
                    Ok(Vec::new())
 | 
					                    Ok(Vec::new())
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Ok(vec![Command::Group(commands)])
 | 
					                    Ok(vec![Command::Group(Group::new(commands))])
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Statement::Semicolon(semi) => match semi.statement() {
 | 
					            Statement::Semicolon(semi) => match semi.statement() {
 | 
				
			||||||
| 
						 | 
					@ -467,7 +472,8 @@ impl Transpiler {
 | 
				
			||||||
            .map(|val| val.to_macro_string());
 | 
					            .map(|val| val.to_macro_string());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let (prepare_cmds, ret_cmd) = if let Ok(val) = comptime_val {
 | 
					        let (prepare_cmds, ret_cmd) = if let Ok(val) = comptime_val {
 | 
				
			||||||
            (Vec::new(), datapack::ReturnCommand::Value(val.into()))
 | 
					            let (macro_string, prepare_variables) = val.into_sb();
 | 
				
			||||||
 | 
					            (Vec::new(), datapack::ReturnCommand::Value(macro_string))
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            match ret.expression() {
 | 
					            match ret.expression() {
 | 
				
			||||||
                Expression::Primary(Primary::Prefix(prefix))
 | 
					                Expression::Primary(Primary::Prefix(prefix))
 | 
				
			||||||
| 
						 | 
					@ -478,7 +484,7 @@ impl Transpiler {
 | 
				
			||||||
                    let cmd = if ret_cmds.len() == 1 {
 | 
					                    let cmd = if ret_cmds.len() == 1 {
 | 
				
			||||||
                        ret_cmds.into_iter().next().unwrap()
 | 
					                        ret_cmds.into_iter().next().unwrap()
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        Command::Group(ret_cmds)
 | 
					                        Command::Group(Group::new(ret_cmds))
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    (Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
 | 
					                    (Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -487,7 +493,7 @@ impl Transpiler {
 | 
				
			||||||
                    let cmd = if ret_cmds.len() == 1 {
 | 
					                    let cmd = if ret_cmds.len() == 1 {
 | 
				
			||||||
                        ret_cmds.into_iter().next().unwrap()
 | 
					                        ret_cmds.into_iter().next().unwrap()
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        Command::Group(ret_cmds)
 | 
					                        Command::Group(Group::new(ret_cmds))
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    (Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
 | 
					                    (Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -513,7 +519,11 @@ impl Transpiler {
 | 
				
			||||||
                                },
 | 
					                                },
 | 
				
			||||||
                                |val| {
 | 
					                                |val| {
 | 
				
			||||||
                                    let cmd = val.to_string_no_macro().map_or_else(
 | 
					                                    let cmd = val.to_string_no_macro().map_or_else(
 | 
				
			||||||
                                        || Command::UsesMacro(val.to_macro_string().into()),
 | 
					                                        || {
 | 
				
			||||||
 | 
					                                            let (macro_string, prepare_variables) =
 | 
				
			||||||
 | 
					                                                val.to_macro_string().into_sb();
 | 
				
			||||||
 | 
					                                            Command::UsesMacro(macro_string)
 | 
				
			||||||
 | 
					                                        },
 | 
				
			||||||
                                        Command::Raw,
 | 
					                                        Command::Raw,
 | 
				
			||||||
                                    );
 | 
					                                    );
 | 
				
			||||||
                                    Ok((
 | 
					                                    Ok((
 | 
				
			||||||
| 
						 | 
					@ -588,6 +598,7 @@ impl Transpiler {
 | 
				
			||||||
        Ok(cmds)
 | 
					        Ok(cmds)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
    pub(super) fn transpile_run_expression(
 | 
					    pub(super) fn transpile_run_expression(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        expression: &Primary,
 | 
					        expression: &Primary,
 | 
				
			||||||
| 
						 | 
					@ -600,22 +611,29 @@ impl Transpiler {
 | 
				
			||||||
                Some(VariableData::ComptimeValue {
 | 
					                Some(VariableData::ComptimeValue {
 | 
				
			||||||
                    value,
 | 
					                    value,
 | 
				
			||||||
                    read_only: _,
 | 
					                    read_only: _,
 | 
				
			||||||
                }) => value.read().unwrap().as_ref().map_or_else(
 | 
					                }) => {
 | 
				
			||||||
                    || {
 | 
					                    if let Some(val) = value.read().unwrap().as_ref() {
 | 
				
			||||||
 | 
					                        let cmds = val.to_string_no_macro().map_or_else(
 | 
				
			||||||
 | 
					                            || {
 | 
				
			||||||
 | 
					                                let (macro_string, prepare_variables) =
 | 
				
			||||||
 | 
					                                    val.to_macro_string().into_sb();
 | 
				
			||||||
 | 
					                                self.transpile_commands_with_variable_macros(
 | 
				
			||||||
 | 
					                                    vec![Command::UsesMacro(macro_string)],
 | 
				
			||||||
 | 
					                                    prepare_variables,
 | 
				
			||||||
 | 
					                                    handler,
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            |s| Ok(vec![Command::Raw(s)]),
 | 
				
			||||||
 | 
					                        )?;
 | 
				
			||||||
 | 
					                        Ok(cmds)
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
                        let err = TranspileError::MissingValue(MissingValue {
 | 
					                        let err = TranspileError::MissingValue(MissingValue {
 | 
				
			||||||
                            expression: ident.span.clone(),
 | 
					                            expression: ident.span.clone(),
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        handler.receive(Box::new(err.clone()));
 | 
					                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                        Err(err)
 | 
					                        Err(err)
 | 
				
			||||||
                    },
 | 
					                    }
 | 
				
			||||||
                    |val| {
 | 
					                }
 | 
				
			||||||
                        let cmd = val.to_string_no_macro().map_or_else(
 | 
					 | 
				
			||||||
                            || Command::UsesMacro(val.to_macro_string().into()),
 | 
					 | 
				
			||||||
                            Command::Raw,
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                        Ok(vec![cmd])
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                Some(_) => {
 | 
					                Some(_) => {
 | 
				
			||||||
                    let err = TranspileError::UnexpectedExpression(UnexpectedExpression(Box::new(
 | 
					                    let err = TranspileError::UnexpectedExpression(UnexpectedExpression(Box::new(
 | 
				
			||||||
                        Expression::Primary(expression.clone()),
 | 
					                        Expression::Primary(expression.clone()),
 | 
				
			||||||
| 
						 | 
					@ -647,12 +665,26 @@ impl Transpiler {
 | 
				
			||||||
            Primary::StringLiteral(string) => {
 | 
					            Primary::StringLiteral(string) => {
 | 
				
			||||||
                Ok(vec![Command::Raw(string.str_content().to_string())])
 | 
					                Ok(vec![Command::Raw(string.str_content().to_string())])
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Primary::TemplateStringLiteral(string) => Ok(vec![Command::UsesMacro(
 | 
					            Primary::TemplateStringLiteral(string) => {
 | 
				
			||||||
                string.to_macro_string(scope, handler)?.into(),
 | 
					                let (macro_string, prepare_variables) = string
 | 
				
			||||||
            )]),
 | 
					                    .to_macro_string(Some(self), scope, handler)?
 | 
				
			||||||
 | 
					                    .into_sb();
 | 
				
			||||||
 | 
					                self.transpile_commands_with_variable_macros(
 | 
				
			||||||
 | 
					                    vec![Command::UsesMacro(macro_string)],
 | 
				
			||||||
 | 
					                    prepare_variables,
 | 
				
			||||||
 | 
					                    handler,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            Primary::Lua(code) => match code.eval_comptime(scope, handler)? {
 | 
					            Primary::Lua(code) => match code.eval_comptime(scope, handler)? {
 | 
				
			||||||
                Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
					                Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
				
			||||||
                Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
 | 
					                Ok(ComptimeValue::MacroString(cmd)) => {
 | 
				
			||||||
 | 
					                    let (macro_string, prepare_variables) = cmd.into_sb();
 | 
				
			||||||
 | 
					                    self.transpile_commands_with_variable_macros(
 | 
				
			||||||
 | 
					                        vec![Command::UsesMacro(macro_string)],
 | 
				
			||||||
 | 
					                        prepare_variables,
 | 
				
			||||||
 | 
					                        handler,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
 | 
					                Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
 | 
				
			||||||
                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
                        expected_type: ExpectedType::String,
 | 
					                        expected_type: ExpectedType::String,
 | 
				
			||||||
| 
						 | 
					@ -676,7 +708,14 @@ impl Transpiler {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
 | 
					                Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
 | 
				
			||||||
                    Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
					                    Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
				
			||||||
                    Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
 | 
					                    Ok(ComptimeValue::MacroString(cmd)) => {
 | 
				
			||||||
 | 
					                        let (macro_string, prepare_variables) = cmd.into_sb();
 | 
				
			||||||
 | 
					                        self.transpile_commands_with_variable_macros(
 | 
				
			||||||
 | 
					                            vec![Command::UsesMacro(macro_string)],
 | 
				
			||||||
 | 
					                            prepare_variables,
 | 
				
			||||||
 | 
					                            handler,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    Ok(_) => {
 | 
					                    Ok(_) => {
 | 
				
			||||||
                        let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                        let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
                            expression: bin.span(),
 | 
					                            expression: bin.span(),
 | 
				
			||||||
| 
						 | 
					@ -723,47 +762,67 @@ impl Transpiler {
 | 
				
			||||||
                TranspiledFunctionArguments::Static(arguments, mut setup_cmds) => {
 | 
					                TranspiledFunctionArguments::Static(arguments, mut setup_cmds) => {
 | 
				
			||||||
                    use std::fmt::Write;
 | 
					                    use std::fmt::Write;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    let cmd = if arguments.is_empty() {
 | 
					                    let cmds = if arguments.is_empty() {
 | 
				
			||||||
                        Command::Raw(function_call)
 | 
					                        vec![Command::Raw(function_call)]
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        let arguments_iter = arguments.iter().map(|(ident, v)| match v {
 | 
					                        let arguments_iter =
 | 
				
			||||||
                            MacroString::String(s) => MacroString::String(format!(
 | 
					                            arguments
 | 
				
			||||||
                                r#"{macro_name}:"{escaped}""#,
 | 
					                                .into_iter()
 | 
				
			||||||
                                macro_name = crate::util::identifier_to_macro(ident),
 | 
					                                .map(|(macro_name, value)| match value {
 | 
				
			||||||
                                escaped = crate::util::escape_str(s)
 | 
					                                    MacroString::String(s) => MacroString::String(format!(
 | 
				
			||||||
                            )),
 | 
					                                        r#"{macro_name}:"{escaped}""#,
 | 
				
			||||||
                            MacroString::MacroString(parts) => MacroString::MacroString(
 | 
					                                        escaped = crate::util::escape_str(&s)
 | 
				
			||||||
                                std::iter::once(MacroStringPart::String(format!(
 | 
					                                    )),
 | 
				
			||||||
                                    r#"{macro_name}:""#,
 | 
					                                    MacroString::MacroString {
 | 
				
			||||||
                                    macro_name = crate::util::identifier_to_macro(ident)
 | 
					                                        parts,
 | 
				
			||||||
                                )))
 | 
					                                        prepare_variables: preparation_cmds,
 | 
				
			||||||
                                .chain(parts.clone().into_iter().map(|part| match part {
 | 
					                                    } => MacroString::MacroString {
 | 
				
			||||||
                                    MacroStringPart::String(s) => MacroStringPart::String(
 | 
					                                        parts: std::iter::once(MacroStringPart::String(format!(
 | 
				
			||||||
                                        crate::util::escape_str(&s).to_string(),
 | 
					                                            r#"{macro_name}:""#,
 | 
				
			||||||
                                    ),
 | 
					                                        )))
 | 
				
			||||||
                                    macro_usage @ MacroStringPart::MacroUsage(_) => macro_usage,
 | 
					                                        .chain(parts.into_iter().map(|part| match part {
 | 
				
			||||||
                                }))
 | 
					                                            MacroStringPart::String(s) => MacroStringPart::String(
 | 
				
			||||||
                                .chain(std::iter::once(MacroStringPart::String('"'.to_string())))
 | 
					                                                crate::util::escape_str(&s).to_string(),
 | 
				
			||||||
                                .collect(),
 | 
					                                            ),
 | 
				
			||||||
                            ),
 | 
					                                            MacroStringPart::MacroUsage(_) => part,
 | 
				
			||||||
                        });
 | 
					                                        }))
 | 
				
			||||||
 | 
					                                        .chain(std::iter::once(MacroStringPart::String(
 | 
				
			||||||
 | 
					                                            '"'.to_string(),
 | 
				
			||||||
 | 
					                                        )))
 | 
				
			||||||
 | 
					                                        .collect(),
 | 
				
			||||||
 | 
					                                        prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
                        let arguments = super::util::join_macro_strings(arguments_iter);
 | 
					                        let arguments = super::util::join_macro_strings(arguments_iter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        match arguments {
 | 
					                        match arguments {
 | 
				
			||||||
                            MacroString::String(arguments) => {
 | 
					                            MacroString::String(arguments) => {
 | 
				
			||||||
                                write!(function_call, " {{{arguments}}}").unwrap();
 | 
					                                write!(function_call, " {{{arguments}}}").unwrap();
 | 
				
			||||||
                                Command::Raw(function_call)
 | 
					                                vec![Command::Raw(function_call)]
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            MacroString::MacroString(mut parts) => {
 | 
					                            MacroString::MacroString {
 | 
				
			||||||
 | 
					                                mut parts,
 | 
				
			||||||
 | 
					                                prepare_variables,
 | 
				
			||||||
 | 
					                            } => {
 | 
				
			||||||
                                function_call.push_str(" {");
 | 
					                                function_call.push_str(" {");
 | 
				
			||||||
                                parts.insert(0, MacroStringPart::String(function_call));
 | 
					                                parts.insert(0, MacroStringPart::String(function_call));
 | 
				
			||||||
                                parts.push(MacroStringPart::String('}'.to_string()));
 | 
					                                parts.push(MacroStringPart::String('}'.to_string()));
 | 
				
			||||||
                                Command::UsesMacro(MacroString::MacroString(parts).into())
 | 
					
 | 
				
			||||||
 | 
					                                let (macro_string, prepare_variables) = MacroString::MacroString {
 | 
				
			||||||
 | 
					                                    parts,
 | 
				
			||||||
 | 
					                                    prepare_variables,
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                .into_sb();
 | 
				
			||||||
 | 
					                                self.transpile_commands_with_variable_macros(
 | 
				
			||||||
 | 
					                                    vec![Command::UsesMacro(macro_string)],
 | 
				
			||||||
 | 
					                                    prepare_variables,
 | 
				
			||||||
 | 
					                                    handler,
 | 
				
			||||||
 | 
					                                )?
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    setup_cmds.push(cmd);
 | 
					                    setup_cmds.extend(cmds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Ok(setup_cmds)
 | 
					                    Ok(setup_cmds)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					@ -963,6 +1022,7 @@ impl Transpiler {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
    fn combine_execute_head_tail(
 | 
					    fn combine_execute_head_tail(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        head: &ExecuteBlockHead,
 | 
					        head: &ExecuteBlockHead,
 | 
				
			||||||
| 
						 | 
					@ -991,84 +1051,158 @@ impl Transpiler {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::As(r#as) => {
 | 
					            ExecuteBlockHead::As(r#as) => {
 | 
				
			||||||
                let selector = r#as.as_selector().to_macro_string(scope, handler)?;
 | 
					                let selector = r#as
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                    .as_selector()
 | 
				
			||||||
                    (pre_cmds, Execute::As(selector.into(), Box::new(tail)))
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                })
 | 
					                let (macro_string, prepare_variables) = selector.into_sb();
 | 
				
			||||||
 | 
					                tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::As(macro_string, Box::new(tail))))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::At(at) => {
 | 
					            ExecuteBlockHead::At(at) => {
 | 
				
			||||||
                let selector = at.at_selector().to_macro_string(scope, handler)?;
 | 
					                let selector = at
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                    .at_selector()
 | 
				
			||||||
                    (pre_cmds, Execute::At(selector.into(), Box::new(tail)))
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                })
 | 
					                let (macro_string, prepare_variables) = selector.into_sb();
 | 
				
			||||||
 | 
					                tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::At(macro_string, Box::new(tail))))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Align(align) => {
 | 
					            ExecuteBlockHead::Align(align) => {
 | 
				
			||||||
                let align = align.align_selector().to_macro_string(scope, handler)?;
 | 
					                let align = align
 | 
				
			||||||
 | 
					                    .align_selector()
 | 
				
			||||||
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = align.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (pre_cmds, Execute::Align(align.into(), Box::new(tail)))
 | 
					                    (pre_cmds, Execute::Align(macro_string, Box::new(tail)))
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Anchored(anchored) => {
 | 
					            ExecuteBlockHead::Anchored(anchored) => {
 | 
				
			||||||
                let anchor = anchored
 | 
					                let anchor =
 | 
				
			||||||
                    .anchored_selector()
 | 
					                    anchored
 | 
				
			||||||
                    .to_macro_string(scope, handler)?;
 | 
					                        .anchored_selector()
 | 
				
			||||||
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = anchor.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (pre_cmds, Execute::Anchored(anchor.into(), Box::new(tail)))
 | 
					                    (pre_cmds, Execute::Anchored(macro_string, Box::new(tail)))
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::In(r#in) => {
 | 
					            ExecuteBlockHead::In(r#in) => {
 | 
				
			||||||
                let dimension = r#in.in_selector().to_macro_string(scope, handler)?;
 | 
					                let dimension = r#in
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                    .in_selector()
 | 
				
			||||||
                    (pre_cmds, Execute::In(dimension.into(), Box::new(tail)))
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                })
 | 
					                let (macro_string, prepare_variables) = dimension.into_sb();
 | 
				
			||||||
 | 
					                tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::In(macro_string, Box::new(tail))))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Positioned(positioned) => {
 | 
					            ExecuteBlockHead::Positioned(positioned) => {
 | 
				
			||||||
                let position = positioned
 | 
					                let position =
 | 
				
			||||||
                    .positioned_selector()
 | 
					                    positioned
 | 
				
			||||||
                    .to_macro_string(scope, handler)?;
 | 
					                        .positioned_selector()
 | 
				
			||||||
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = position.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (
 | 
					                    (pre_cmds, Execute::Positioned(macro_string, Box::new(tail)))
 | 
				
			||||||
                        pre_cmds,
 | 
					 | 
				
			||||||
                        Execute::Positioned(position.into(), Box::new(tail)),
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Rotated(rotated) => {
 | 
					            ExecuteBlockHead::Rotated(rotated) => {
 | 
				
			||||||
                let rotation = rotated.rotated_selector().to_macro_string(scope, handler)?;
 | 
					                let rotation =
 | 
				
			||||||
 | 
					                    rotated
 | 
				
			||||||
 | 
					                        .rotated_selector()
 | 
				
			||||||
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = rotation.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (pre_cmds, Execute::Rotated(rotation.into(), Box::new(tail)))
 | 
					                    (pre_cmds, Execute::Rotated(macro_string, Box::new(tail)))
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Facing(facing) => {
 | 
					            ExecuteBlockHead::Facing(facing) => {
 | 
				
			||||||
                let facing = facing.facing_selector().to_macro_string(scope, handler)?;
 | 
					                let facing =
 | 
				
			||||||
 | 
					                    facing
 | 
				
			||||||
 | 
					                        .facing_selector()
 | 
				
			||||||
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = facing.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (pre_cmds, Execute::Facing(facing.into(), Box::new(tail)))
 | 
					                    (pre_cmds, Execute::Facing(macro_string, Box::new(tail)))
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::AsAt(as_at) => {
 | 
					            ExecuteBlockHead::AsAt(as_at) => {
 | 
				
			||||||
                let selector = as_at.asat_selector().to_macro_string(scope, handler)?;
 | 
					                let selector = as_at
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                    .asat_selector()
 | 
				
			||||||
                    (pre_cmds, Execute::AsAt(selector.into(), Box::new(tail)))
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                })
 | 
					                let (macro_string, prepare_variables) = selector.into_sb();
 | 
				
			||||||
 | 
					                tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::AsAt(macro_string, Box::new(tail))))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::On(on) => {
 | 
					            ExecuteBlockHead::On(on) => {
 | 
				
			||||||
                let dimension = on.on_selector().to_macro_string(scope, handler)?;
 | 
					                let dimension = on
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                    .on_selector()
 | 
				
			||||||
                    (pre_cmds, Execute::On(dimension.into(), Box::new(tail)))
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                })
 | 
					                let (macro_string, prepare_variables) = dimension.into_sb();
 | 
				
			||||||
 | 
					                tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::On(macro_string, Box::new(tail))))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Store(store) => {
 | 
					            ExecuteBlockHead::Store(store) => {
 | 
				
			||||||
                let store = store.store_selector().to_macro_string(scope, handler)?;
 | 
					                let store = store
 | 
				
			||||||
 | 
					                    .store_selector()
 | 
				
			||||||
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = store.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (pre_cmds, Execute::Store(store.into(), Box::new(tail)))
 | 
					                    (pre_cmds, Execute::Store(macro_string, Box::new(tail)))
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Summon(summon) => {
 | 
					            ExecuteBlockHead::Summon(summon) => {
 | 
				
			||||||
                let entity = summon.summon_selector().to_macro_string(scope, handler)?;
 | 
					                let entity =
 | 
				
			||||||
 | 
					                    summon
 | 
				
			||||||
 | 
					                        .summon_selector()
 | 
				
			||||||
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
 | 
					                let (macro_string, prepare_variables) = entity.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, tail)| {
 | 
					                tail.map(|(pre_cmds, tail)| {
 | 
				
			||||||
                    (pre_cmds, Execute::Summon(entity.into(), Box::new(tail)))
 | 
					                    (pre_cmds, Execute::Summon(macro_string, Box::new(tail)))
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub(crate) fn transpile_commands_with_variable_macros(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        cmds: Vec<Command>,
 | 
				
			||||||
 | 
					        prepare_variables: BTreeMap<String, (DataLocation, Vec<Command>, Span)>,
 | 
				
			||||||
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
 | 
					        let storage_name = {
 | 
				
			||||||
 | 
					            let temp = self.get_temp_count(1);
 | 
				
			||||||
 | 
					            let hash = chksum_md5::hash(temp.to_le_bytes());
 | 
				
			||||||
 | 
					            format!("shulkerscript:arguments_{hash}")
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (macro_names, mut prepare_cmds, move_cmds) = prepare_variables.into_iter().try_fold(
 | 
				
			||||||
 | 
					            (HashSet::new(), Vec::new(), Vec::new()),
 | 
				
			||||||
 | 
					            |(mut vars, mut prepare_cmds, mut move_cmds),
 | 
				
			||||||
 | 
					             (macro_name, (data_location, var_cmds, span))| {
 | 
				
			||||||
 | 
					                vars.insert(macro_name.clone());
 | 
				
			||||||
 | 
					                prepare_cmds.extend(var_cmds);
 | 
				
			||||||
 | 
					                let cur_move_cmds = self.move_data(
 | 
				
			||||||
 | 
					                    &data_location,
 | 
				
			||||||
 | 
					                    &DataLocation::Storage {
 | 
				
			||||||
 | 
					                        storage_name: storage_name.clone(),
 | 
				
			||||||
 | 
					                        path: macro_name,
 | 
				
			||||||
 | 
					                        r#type: data_location.storage_type(),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    &span,
 | 
				
			||||||
 | 
					                    handler,
 | 
				
			||||||
 | 
					                )?;
 | 
				
			||||||
 | 
					                move_cmds.extend(cur_move_cmds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TranspileResult::Ok((vars, prepare_cmds, move_cmds))
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        prepare_cmds.extend(move_cmds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if prepare_cmds.is_empty() && macro_names.is_empty() {
 | 
				
			||||||
 | 
					            Ok(cmds)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            prepare_cmds.push(Command::Group(
 | 
				
			||||||
 | 
					                Group::new(cmds)
 | 
				
			||||||
 | 
					                    .always_create_function(true)
 | 
				
			||||||
 | 
					                    .block_pass_macros(macro_names)
 | 
				
			||||||
 | 
					                    .data_storage_name(storage_name),
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(prepare_cmds)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,34 +1,48 @@
 | 
				
			||||||
//! Utility methods for transpiling
 | 
					//! Utility methods for transpiling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{fmt::Display, str::FromStr};
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
 | 
					use std::{collections::BTreeMap, sync::Arc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
 | 
					use shulkerbox::prelude::Command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::{self, source_file::SourceElement as _, Handler},
 | 
					    base::{
 | 
				
			||||||
 | 
					        self,
 | 
				
			||||||
 | 
					        source_file::{SourceElement as _, Span},
 | 
				
			||||||
 | 
					        Handler,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    syntax::syntax_tree::{
 | 
					    syntax::syntax_tree::{
 | 
				
			||||||
        expression::{Expression, Primary, TemplateStringLiteral, TemplateStringLiteralPart},
 | 
					        expression::{Expression, Primary, TemplateStringLiteral, TemplateStringLiteralPart},
 | 
				
			||||||
        AnyStringLiteral,
 | 
					        AnyStringLiteral,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    transpile::{
 | 
					    transpile::{
 | 
				
			||||||
        error::{TranspileError, UnknownIdentifier},
 | 
					        error::{TranspileError, UnknownIdentifier},
 | 
				
			||||||
        expression::ComptimeValue,
 | 
					        expression::{ComptimeValue, DataLocation},
 | 
				
			||||||
        Scope, TranspileResult, VariableData,
 | 
					        Scope, TranspileResult, Transpiler, VariableData,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    util::identifier_to_macro,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					 | 
				
			||||||
use std::sync::Arc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// String that can contain macros
 | 
					/// String that can contain macros
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum MacroString {
 | 
					pub enum MacroString {
 | 
				
			||||||
    /// A normal string
 | 
					    /// A normal string
 | 
				
			||||||
    String(String),
 | 
					    String(String),
 | 
				
			||||||
    /// A string containing expressions
 | 
					    /// A string containing expressions
 | 
				
			||||||
    MacroString(Vec<MacroStringPart>),
 | 
					    MacroString {
 | 
				
			||||||
 | 
					        /// Parts that make up the macro string
 | 
				
			||||||
 | 
					        parts: Vec<MacroStringPart>,
 | 
				
			||||||
 | 
					        /// Variables that need special preparation before using the macro string
 | 
				
			||||||
 | 
					        prepare_variables: BTreeMap<String, (DataLocation, Vec<Command>, Span)>,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Part of a [`MacroString`]
 | 
					/// Part of a [`MacroString`]
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum MacroStringPart {
 | 
					pub enum MacroStringPart {
 | 
				
			||||||
| 
						 | 
					@ -38,11 +52,12 @@ pub enum MacroStringPart {
 | 
				
			||||||
    MacroUsage(String),
 | 
					    MacroUsage(String),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for MacroString {
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
 | 
					impl std::fmt::Display for MacroString {
 | 
				
			||||||
    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::String(s) => s.fmt(f),
 | 
					            Self::String(s) => s.fmt(f),
 | 
				
			||||||
            Self::MacroString(parts) => {
 | 
					            Self::MacroString { parts, .. } => {
 | 
				
			||||||
                for part in parts {
 | 
					                for part in parts {
 | 
				
			||||||
                    match part {
 | 
					                    match part {
 | 
				
			||||||
                        MacroStringPart::String(s) => s.fmt(f)?,
 | 
					                        MacroStringPart::String(s) => s.fmt(f)?,
 | 
				
			||||||
| 
						 | 
					@ -55,13 +70,14 @@ impl Display for MacroString {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl MacroString {
 | 
					impl MacroString {
 | 
				
			||||||
    /// Check if the macro string contains any macros
 | 
					    /// Check if the macro string contains any macros
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn contains_macros(&self) -> bool {
 | 
					    pub fn contains_macros(&self) -> bool {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::String(_) => false,
 | 
					            Self::String(_) => false,
 | 
				
			||||||
            Self::MacroString(parts) => parts
 | 
					            Self::MacroString { parts, .. } => parts
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
                .any(|p| matches!(p, MacroStringPart::MacroUsage(_))),
 | 
					                .any(|p| matches!(p, MacroStringPart::MacroUsage(_))),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -71,11 +87,23 @@ impl MacroString {
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - If the macro string contains macros
 | 
					    /// - If the macro string contains macros
 | 
				
			||||||
    pub fn as_str(&self) -> Result<std::borrow::Cow<'_, str>, &[MacroStringPart]> {
 | 
					    #[expect(clippy::type_complexity)]
 | 
				
			||||||
 | 
					    pub fn as_str(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					    ) -> Result<
 | 
				
			||||||
 | 
					        std::borrow::Cow<'_, str>,
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            &[MacroStringPart],
 | 
				
			||||||
 | 
					            &BTreeMap<String, (DataLocation, Vec<Command>, Span)>,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    > {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::String(s) => Ok(std::borrow::Cow::Borrowed(s)),
 | 
					            Self::String(s) => Ok(std::borrow::Cow::Borrowed(s)),
 | 
				
			||||||
            Self::MacroString(parts) if self.contains_macros() => Err(parts),
 | 
					            Self::MacroString {
 | 
				
			||||||
            Self::MacroString(parts) => Ok(std::borrow::Cow::Owned(
 | 
					                parts,
 | 
				
			||||||
 | 
					                prepare_variables,
 | 
				
			||||||
 | 
					            } if self.contains_macros() => Err((parts, prepare_variables)),
 | 
				
			||||||
 | 
					            Self::MacroString { parts, .. } => Ok(std::borrow::Cow::Owned(
 | 
				
			||||||
                parts
 | 
					                parts
 | 
				
			||||||
                    .iter()
 | 
					                    .iter()
 | 
				
			||||||
                    .map(|p| match p {
 | 
					                    .map(|p| match p {
 | 
				
			||||||
| 
						 | 
					@ -126,6 +154,7 @@ where
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Join multiple macro strings into one
 | 
					/// Join multiple macro strings into one
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[must_use]
 | 
					#[must_use]
 | 
				
			||||||
pub fn join_macro_strings<I>(strings: I) -> MacroString
 | 
					pub fn join_macro_strings<I>(strings: I) -> MacroString
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
| 
						 | 
					@ -139,20 +168,39 @@ where
 | 
				
			||||||
                    s.push_str(&cur);
 | 
					                    s.push_str(&cur);
 | 
				
			||||||
                    MacroString::String(s)
 | 
					                    MacroString::String(s)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                MacroString::MacroString(cur) => {
 | 
					                MacroString::MacroString {
 | 
				
			||||||
 | 
					                    parts: cur,
 | 
				
			||||||
 | 
					                    prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                } => {
 | 
				
			||||||
                    let mut parts = vec![MacroStringPart::String(s)];
 | 
					                    let mut parts = vec![MacroStringPart::String(s)];
 | 
				
			||||||
                    parts.extend(cur);
 | 
					                    parts.extend(cur);
 | 
				
			||||||
                    MacroString::MacroString(parts)
 | 
					                    MacroString::MacroString {
 | 
				
			||||||
 | 
					                        parts,
 | 
				
			||||||
 | 
					                        prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            MacroString::MacroString(mut parts) => match cur {
 | 
					            MacroString::MacroString {
 | 
				
			||||||
 | 
					                mut parts,
 | 
				
			||||||
 | 
					                prepare_variables: mut preparation_cmds,
 | 
				
			||||||
 | 
					            } => match cur {
 | 
				
			||||||
                MacroString::String(cur) => {
 | 
					                MacroString::String(cur) => {
 | 
				
			||||||
                    parts.push(MacroStringPart::String(cur));
 | 
					                    parts.push(MacroStringPart::String(cur));
 | 
				
			||||||
                    MacroString::MacroString(parts)
 | 
					                    MacroString::MacroString {
 | 
				
			||||||
 | 
					                        parts,
 | 
				
			||||||
 | 
					                        prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                MacroString::MacroString(cur) => {
 | 
					                MacroString::MacroString {
 | 
				
			||||||
 | 
					                    parts: cur,
 | 
				
			||||||
 | 
					                    prepare_variables: cur_preparation_cmds,
 | 
				
			||||||
 | 
					                } => {
 | 
				
			||||||
                    parts.extend(cur);
 | 
					                    parts.extend(cur);
 | 
				
			||||||
                    MacroString::MacroString(parts)
 | 
					                    preparation_cmds.extend(cur_preparation_cmds);
 | 
				
			||||||
 | 
					                    MacroString::MacroString {
 | 
				
			||||||
 | 
					                        parts,
 | 
				
			||||||
 | 
					                        prepare_variables: preparation_cmds,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -173,7 +221,8 @@ pub fn add_to_entity_selector(selector: impl Into<String>, additional: &str) ->
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl FromStr for MacroString {
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
 | 
					impl std::str::FromStr for MacroString {
 | 
				
			||||||
    type Err = ();
 | 
					    type Err = ();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn from_str(s: &str) -> Result<Self, Self::Err> {
 | 
					    fn from_str(s: &str) -> Result<Self, Self::Err> {
 | 
				
			||||||
| 
						 | 
					@ -239,7 +288,10 @@ impl FromStr for MacroString {
 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
                .any(|p| matches!(p, MacroStringPart::MacroUsage(_)))
 | 
					                .any(|p| matches!(p, MacroStringPart::MacroUsage(_)))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Ok(Self::MacroString(parts))
 | 
					                Ok(Self::MacroString {
 | 
				
			||||||
 | 
					                    parts,
 | 
				
			||||||
 | 
					                    prepare_variables: BTreeMap::new(),
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                Ok(Self::String(s.to_string()))
 | 
					                Ok(Self::String(s.to_string()))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -249,6 +301,7 @@ impl FromStr for MacroString {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl<S> From<S> for MacroString
 | 
					impl<S> From<S> for MacroString
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
    S: Into<String>,
 | 
					    S: Into<String>,
 | 
				
			||||||
| 
						 | 
					@ -266,12 +319,15 @@ impl AnyStringLiteral {
 | 
				
			||||||
    /// - If an identifier in a template string is not found in the scope
 | 
					    /// - If an identifier in a template string is not found in the scope
 | 
				
			||||||
    pub fn to_macro_string(
 | 
					    pub fn to_macro_string(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
 | 
					        transpiler: Option<&mut Transpiler>,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<MacroString> {
 | 
					    ) -> TranspileResult<MacroString> {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::StringLiteral(literal) => Ok(MacroString::from(literal.str_content().as_ref())),
 | 
					            Self::StringLiteral(literal) => Ok(MacroString::from(literal.str_content().as_ref())),
 | 
				
			||||||
            Self::TemplateStringLiteral(literal) => literal.to_macro_string(scope, handler),
 | 
					            Self::TemplateStringLiteral(literal) => {
 | 
				
			||||||
 | 
					                literal.to_macro_string(transpiler, scope, handler)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -282,70 +338,215 @@ impl TemplateStringLiteral {
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - If an identifier in a template string is not found in the scope
 | 
					    /// - If an identifier in a template string is not found in the scope
 | 
				
			||||||
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
    pub fn to_macro_string(
 | 
					    pub fn to_macro_string(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
 | 
					        mut transpiler: Option<&mut Transpiler>,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<MacroString> {
 | 
					    ) -> TranspileResult<MacroString> {
 | 
				
			||||||
        if self
 | 
					        if self.contains_expression() {
 | 
				
			||||||
            .parts()
 | 
					            let mut prepare_variables = BTreeMap::new();
 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .any(|p| matches!(p, TemplateStringLiteralPart::Expression { .. }))
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            let macro_string = MacroString::MacroString(
 | 
					 | 
				
			||||||
                self.parts()
 | 
					 | 
				
			||||||
                    .iter()
 | 
					 | 
				
			||||||
                    .map(|part| match part {
 | 
					 | 
				
			||||||
                        TemplateStringLiteralPart::Text(text) => Ok(MacroStringPart::String(
 | 
					 | 
				
			||||||
                            crate::util::unescape_template_string(text.span.str()).into_owned(),
 | 
					 | 
				
			||||||
                        )),
 | 
					 | 
				
			||||||
                        TemplateStringLiteralPart::Expression { expression, .. } => {
 | 
					 | 
				
			||||||
                            match expression.as_ref() {
 | 
					 | 
				
			||||||
                                Expression::Primary(Primary::Identifier(identifier)) =>
 | 
					 | 
				
			||||||
                                {
 | 
					 | 
				
			||||||
                                    #[expect(clippy::option_if_let_else)]
 | 
					 | 
				
			||||||
                                    if let Some(var_data) =
 | 
					 | 
				
			||||||
                                        scope.get_variable(identifier.span.str())
 | 
					 | 
				
			||||||
                                    {
 | 
					 | 
				
			||||||
                                        match var_data.as_ref() {
 | 
					 | 
				
			||||||
                                            VariableData::MacroParameter { macro_name, .. } => Ok(
 | 
					 | 
				
			||||||
                                                MacroStringPart::MacroUsage(macro_name.to_owned()),
 | 
					 | 
				
			||||||
                                            ),
 | 
					 | 
				
			||||||
                                            VariableData::ComptimeValue { value, .. } => {
 | 
					 | 
				
			||||||
                                                let value = value.read().unwrap().as_ref().map_or_else(
 | 
					 | 
				
			||||||
                                                    || "null".into(),
 | 
					 | 
				
			||||||
                                                    ComptimeValue::to_macro_string,
 | 
					 | 
				
			||||||
                                                );
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                                match value.as_str() {
 | 
					            let parts = self
 | 
				
			||||||
                                                    Ok(s) => Ok(MacroStringPart::String(s.into_owned())),
 | 
					                .parts()
 | 
				
			||||||
                                                    Err(_) => todo!("comptime value resulting in macro string with macros")
 | 
					                .iter()
 | 
				
			||||||
                                                }
 | 
					                .map(|part| match part {
 | 
				
			||||||
 | 
					                    TemplateStringLiteralPart::Text(text) => Ok(vec![MacroStringPart::String(
 | 
				
			||||||
 | 
					                        crate::util::unescape_template_string(text.span.str()).into_owned(),
 | 
				
			||||||
 | 
					                    )]),
 | 
				
			||||||
 | 
					                    TemplateStringLiteralPart::Expression { expression, .. } => match expression
 | 
				
			||||||
 | 
					                        .as_ref()
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Expression::Primary(Primary::Identifier(identifier)) => {
 | 
				
			||||||
 | 
					                            #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
 | 
					                            if let Some(var_data) = scope.get_variable(identifier.span.str()) {
 | 
				
			||||||
 | 
					                                match var_data.as_ref() {
 | 
				
			||||||
 | 
					                                    VariableData::MacroParameter { macro_name, .. } => {
 | 
				
			||||||
 | 
					                                        Ok(vec![MacroStringPart::MacroUsage(macro_name.to_owned())])
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                    VariableData::ComptimeValue { value, .. } => {
 | 
				
			||||||
 | 
					                                        let value = value.read().unwrap().as_ref().map_or_else(
 | 
				
			||||||
 | 
					                                            || "null".into(),
 | 
				
			||||||
 | 
					                                            ComptimeValue::to_macro_string,
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        match value.as_str() {
 | 
				
			||||||
 | 
					                                            Ok(s) => {
 | 
				
			||||||
 | 
					                                                Ok(vec![MacroStringPart::String(s.into_owned())])
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            Err((inner_parts, inner_prepare_variables)) => {
 | 
				
			||||||
 | 
					                                                prepare_variables
 | 
				
			||||||
 | 
					                                                    .extend(inner_prepare_variables.to_owned());
 | 
				
			||||||
 | 
					                                                Ok(inner_parts.to_vec())
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
                                            _ => todo!("other identifiers in template strings"),
 | 
					 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                    } else {
 | 
					                                    }
 | 
				
			||||||
                                        let err =
 | 
					                                    VariableData::BooleanStorage { storage_name, path } => {
 | 
				
			||||||
                                            TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(identifier.span(), scope));
 | 
					                                        use crate::transpile::expression::StorageType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        let macro_name = if let Some(transpiler) = &mut transpiler {
 | 
				
			||||||
 | 
					                                            let temp_count = transpiler.get_temp_count(1);
 | 
				
			||||||
 | 
					                                            format!(
 | 
				
			||||||
 | 
					                                                "shu_temp_{hash}",
 | 
				
			||||||
 | 
					                                                hash = chksum_md5::hash(temp_count.to_le_bytes())
 | 
				
			||||||
 | 
					                                            )
 | 
				
			||||||
 | 
					                                        } else {
 | 
				
			||||||
 | 
					                                            identifier_to_macro(identifier.span.str()).into_owned()
 | 
				
			||||||
 | 
					                                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        let data_location = DataLocation::Storage {
 | 
				
			||||||
 | 
					                                            storage_name: storage_name.to_owned(),
 | 
				
			||||||
 | 
					                                            path: path.to_owned(),
 | 
				
			||||||
 | 
					                                            r#type: StorageType::Boolean,
 | 
				
			||||||
 | 
					                                        };
 | 
				
			||||||
 | 
					                                        prepare_variables.insert(
 | 
				
			||||||
 | 
					                                            macro_name.clone(),
 | 
				
			||||||
 | 
					                                            (data_location, Vec::new(), expression.span()),
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        Ok(vec![MacroStringPart::MacroUsage(macro_name)])
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                    VariableData::ScoreboardValue { objective, target } => {
 | 
				
			||||||
 | 
					                                        let macro_name = if let Some(transpiler) = &mut transpiler {
 | 
				
			||||||
 | 
					                                            let temp_count = transpiler.get_temp_count(1);
 | 
				
			||||||
 | 
					                                            format!(
 | 
				
			||||||
 | 
					                                                "shu_temp_{hash}",
 | 
				
			||||||
 | 
					                                                hash = chksum_md5::hash(temp_count.to_le_bytes())
 | 
				
			||||||
 | 
					                                            )
 | 
				
			||||||
 | 
					                                        } else {
 | 
				
			||||||
 | 
					                                            identifier_to_macro(identifier.span.str()).into_owned()
 | 
				
			||||||
 | 
					                                        };
 | 
				
			||||||
 | 
					                                        let data_location = DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                                            objective: objective.to_owned(),
 | 
				
			||||||
 | 
					                                            target: target.to_owned(),
 | 
				
			||||||
 | 
					                                        };
 | 
				
			||||||
 | 
					                                        prepare_variables.insert(
 | 
				
			||||||
 | 
					                                            macro_name.clone(),
 | 
				
			||||||
 | 
					                                            (data_location, Vec::new(), expression.span()),
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        Ok(vec![MacroStringPart::MacroUsage(macro_name)])
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
 | 
					                                    _ => {
 | 
				
			||||||
 | 
					                                        use crate::semantic::error::UnexpectedExpression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        let err = TranspileError::UnexpectedExpression(
 | 
				
			||||||
 | 
					                                            UnexpectedExpression(expression.to_owned()),
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
                                        handler.receive(Box::new(err.clone()));
 | 
					                                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                        Err(err)
 | 
					                                        Err(err)
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                Expression::Primary(Primary::MemberAccess(member_access)) => {
 | 
					                            } else {
 | 
				
			||||||
                                    let value = member_access.parent().comptime_member_access(member_access, scope, handler).inspect_err(|err| {
 | 
					                                let err = TranspileError::UnknownIdentifier(
 | 
				
			||||||
                                        handler.receive(Box::new(TranspileError::NotComptime(err.clone())));
 | 
					                                    UnknownIdentifier::from_scope(identifier.span(), scope),
 | 
				
			||||||
                                    })?.to_macro_string();
 | 
					                                );
 | 
				
			||||||
 | 
					                                handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                                    value.as_str().map_or_else(|_| todo!("comptime value resulting in macro string with macros"), |s| Ok(MacroStringPart::String(s.into_owned())))
 | 
					                                Err(err)
 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                                _ => todo!("other expressions in template strings"),
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    })
 | 
					                        Expression::Primary(Primary::MemberAccess(member_access)) => {
 | 
				
			||||||
                    .collect::<TranspileResult<Vec<MacroStringPart>>>()?,
 | 
					                            let value = member_access
 | 
				
			||||||
            );
 | 
					                                .parent()
 | 
				
			||||||
 | 
					                                .comptime_member_access(member_access, scope, handler)
 | 
				
			||||||
 | 
					                                .inspect_err(|err| {
 | 
				
			||||||
 | 
					                                    handler.receive(Box::new(TranspileError::NotComptime(
 | 
				
			||||||
 | 
					                                        err.clone(),
 | 
				
			||||||
 | 
					                                    )));
 | 
				
			||||||
 | 
					                                })?
 | 
				
			||||||
 | 
					                                .to_macro_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok(macro_string)
 | 
					                            match value.as_str() {
 | 
				
			||||||
 | 
					                                Ok(s) => Ok(vec![MacroStringPart::String(s.into_owned())]),
 | 
				
			||||||
 | 
					                                Err((inner_parts, inner_prepare_variables)) => {
 | 
				
			||||||
 | 
					                                    prepare_variables.extend(inner_prepare_variables.to_owned());
 | 
				
			||||||
 | 
					                                    Ok(inner_parts.to_vec())
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        _ => {
 | 
				
			||||||
 | 
					                            if let Some(transpiler) = &mut transpiler {
 | 
				
			||||||
 | 
					                                use crate::transpile::expression::{StorageType, ValueType};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                let temp_count = transpiler.get_temp_count(1);
 | 
				
			||||||
 | 
					                                let macro_name = format!(
 | 
				
			||||||
 | 
					                                    "shu_temp_{hash}",
 | 
				
			||||||
 | 
					                                    hash = chksum_md5::hash(temp_count.to_le_bytes())
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                let data_location =
 | 
				
			||||||
 | 
					                                    if expression.can_yield_type(ValueType::Integer, scope) {
 | 
				
			||||||
 | 
					                                        let (scoreboard_name, [scoreboard_target]) =
 | 
				
			||||||
 | 
					                                            transpiler.get_temp_scoreboard_locations_array();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                                            objective: scoreboard_name,
 | 
				
			||||||
 | 
					                                            target: scoreboard_target,
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    } else if expression.can_yield_type(ValueType::Boolean, scope) {
 | 
				
			||||||
 | 
					                                        let (storage_name, [storage_path]) =
 | 
				
			||||||
 | 
					                                            transpiler.get_temp_storage_locations_array();
 | 
				
			||||||
 | 
					                                        DataLocation::Storage {
 | 
				
			||||||
 | 
					                                            storage_name,
 | 
				
			||||||
 | 
					                                            path: storage_path,
 | 
				
			||||||
 | 
					                                            r#type: StorageType::Boolean,
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    } else if expression.can_yield_type(ValueType::String, scope) {
 | 
				
			||||||
 | 
					                                        let (storage_name, [storage_path]) =
 | 
				
			||||||
 | 
					                                            transpiler.get_temp_storage_locations_array();
 | 
				
			||||||
 | 
					                                        DataLocation::Storage {
 | 
				
			||||||
 | 
					                                            storage_name,
 | 
				
			||||||
 | 
					                                            path: storage_path,
 | 
				
			||||||
 | 
					                                            r#type: StorageType::String,
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    } else {
 | 
				
			||||||
 | 
					                                        use crate::semantic::error::UnexpectedExpression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                        let err = TranspileError::UnexpectedExpression(
 | 
				
			||||||
 | 
					                                            UnexpectedExpression(expression.to_owned()),
 | 
				
			||||||
 | 
					                                        );
 | 
				
			||||||
 | 
					                                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					                                        return Err(err);
 | 
				
			||||||
 | 
					                                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                let commands = transpiler.transpile_expression(
 | 
				
			||||||
 | 
					                                    expression,
 | 
				
			||||||
 | 
					                                    &data_location,
 | 
				
			||||||
 | 
					                                    scope,
 | 
				
			||||||
 | 
					                                    handler,
 | 
				
			||||||
 | 
					                                )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                prepare_variables.insert(
 | 
				
			||||||
 | 
					                                    macro_name.clone(),
 | 
				
			||||||
 | 
					                                    (data_location, commands, expression.span()),
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                Ok(vec![MacroStringPart::MacroUsage(macro_name)])
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                use crate::semantic::error::UnexpectedExpression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                let err = TranspileError::UnexpectedExpression(
 | 
				
			||||||
 | 
					                                    UnexpectedExpression(expression.to_owned()),
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                                handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                Err(err)
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .flat_map(|res| match res {
 | 
				
			||||||
 | 
					                    Ok(parts) => parts.into_iter().map(Ok).collect(),
 | 
				
			||||||
 | 
					                    Err(err) => vec![Err(err)],
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .collect::<TranspileResult<Vec<MacroStringPart>>>()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(MacroString::MacroString {
 | 
				
			||||||
 | 
					                parts,
 | 
				
			||||||
 | 
					                prepare_variables,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Ok(MacroString::String(
 | 
					            Ok(MacroString::String(
 | 
				
			||||||
                self.as_str(scope, handler)?.into_owned(),
 | 
					                self.as_str(scope, handler)?.into_owned(),
 | 
				
			||||||
| 
						 | 
					@ -354,46 +555,62 @@ impl TemplateStringLiteral {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(all(test, feature = "shulkerbox"))]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
 | 
					    use std::str::FromStr as _;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    use assert_struct::assert_struct;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
    fn test_parse_macro_string() {
 | 
					    fn test_parse_macro_string() {
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_struct!(
 | 
				
			||||||
            MacroString::from_str("Hello, $(world)!").unwrap(),
 | 
					            MacroString::from_str("Hello, $(world)!").unwrap(),
 | 
				
			||||||
            MacroString::MacroString(vec![
 | 
					            MacroString::MacroString {
 | 
				
			||||||
                MacroStringPart::String("Hello, ".to_string()),
 | 
					                parts: vec![
 | 
				
			||||||
                MacroStringPart::MacroUsage("world".to_string()),
 | 
					                    MacroStringPart::String("Hello, ".to_string()),
 | 
				
			||||||
                MacroStringPart::String("!".to_string())
 | 
					                    MacroStringPart::MacroUsage("world".to_string()),
 | 
				
			||||||
            ])
 | 
					                    MacroStringPart::String("!".to_string())
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                prepare_variables.is_empty(): true,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_struct!(
 | 
				
			||||||
            MacroString::from_str("Hello, $(world)! $(world").unwrap(),
 | 
					            MacroString::from_str("Hello, $(world)! $(world").unwrap(),
 | 
				
			||||||
            MacroString::MacroString(vec![
 | 
					            MacroString::MacroString {
 | 
				
			||||||
                MacroStringPart::String("Hello, ".to_string()),
 | 
					                parts: vec![
 | 
				
			||||||
                MacroStringPart::MacroUsage("world".to_string()),
 | 
					                    MacroStringPart::String("Hello, ".to_string()),
 | 
				
			||||||
                MacroStringPart::String("! $(world".to_string()),
 | 
					                    MacroStringPart::MacroUsage("world".to_string()),
 | 
				
			||||||
            ])
 | 
					                    MacroStringPart::String("! $(world".to_string()),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                prepare_variables.is_empty(): true,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_struct!(
 | 
				
			||||||
            MacroString::from_str("Hello $(a) from $(b) and $(c)").unwrap(),
 | 
					            MacroString::from_str("Hello $(a) from $(b) and $(c)").unwrap(),
 | 
				
			||||||
            MacroString::MacroString(vec![
 | 
					            MacroString::MacroString {
 | 
				
			||||||
                MacroStringPart::String("Hello ".to_string()),
 | 
					                parts: vec![
 | 
				
			||||||
                MacroStringPart::MacroUsage("a".to_string()),
 | 
					                    MacroStringPart::String("Hello ".to_string()),
 | 
				
			||||||
                MacroStringPart::String(" from ".to_string()),
 | 
					                    MacroStringPart::MacroUsage("a".to_string()),
 | 
				
			||||||
                MacroStringPart::MacroUsage("b".to_string()),
 | 
					                    MacroStringPart::String(" from ".to_string()),
 | 
				
			||||||
                MacroStringPart::String(" and ".to_string()),
 | 
					                    MacroStringPart::MacroUsage("b".to_string()),
 | 
				
			||||||
                MacroStringPart::MacroUsage("c".to_string()),
 | 
					                    MacroStringPart::String(" and ".to_string()),
 | 
				
			||||||
            ])
 | 
					                    MacroStringPart::MacroUsage("c".to_string()),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                prepare_variables.is_empty(): true,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_struct!(
 | 
				
			||||||
            MacroString::from_str("Hello, $(world! $(world)!").unwrap(),
 | 
					            MacroString::from_str("Hello, $(world! $(world)!").unwrap(),
 | 
				
			||||||
            MacroString::MacroString(vec![
 | 
					            MacroString::MacroString {
 | 
				
			||||||
                MacroStringPart::String("Hello, $(world! ".to_string()),
 | 
					                parts: vec![
 | 
				
			||||||
                MacroStringPart::MacroUsage("world".to_string()),
 | 
					                    MacroStringPart::String("Hello, $(world! ".to_string()),
 | 
				
			||||||
                MacroStringPart::String("!".to_string()),
 | 
					                    MacroStringPart::MacroUsage("world".to_string()),
 | 
				
			||||||
            ])
 | 
					                    MacroStringPart::String("!".to_string()),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                prepare_variables.is_empty(): true,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,12 +32,12 @@ use super::{
 | 
				
			||||||
        AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
 | 
					        AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
 | 
				
			||||||
        MismatchedTypes, NotComptime,
 | 
					        MismatchedTypes, NotComptime,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
 | 
					    expression::{DataLocation, ExpectedType, StorageType},
 | 
				
			||||||
    FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
 | 
					    FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use super::{internal_functions::InternalFunction, Transpiler};
 | 
					use super::{expression::ComptimeValue, internal_functions::InternalFunction, Transpiler};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Stores the data required to access a variable.
 | 
					/// Stores the data required to access a variable.
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
| 
						 | 
					@ -409,6 +409,8 @@ impl Transpiler {
 | 
				
			||||||
                handler,
 | 
					                handler,
 | 
				
			||||||
            )?;
 | 
					            )?;
 | 
				
			||||||
            if is_global {
 | 
					            if is_global {
 | 
				
			||||||
 | 
					                use shulkerbox::datapack::Group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let (temp_objective, [temp_target]) = self.get_temp_scoreboard_locations_array();
 | 
					                let (temp_objective, [temp_target]) = self.get_temp_scoreboard_locations_array();
 | 
				
			||||||
                let test_cmd = match declaration.variable_type().keyword {
 | 
					                let test_cmd = match declaration.variable_type().keyword {
 | 
				
			||||||
                    KeywordKind::Int => {
 | 
					                    KeywordKind::Int => {
 | 
				
			||||||
| 
						 | 
					@ -425,7 +427,7 @@ impl Transpiler {
 | 
				
			||||||
                    Condition::Atom(
 | 
					                    Condition::Atom(
 | 
				
			||||||
                        format!("score {temp_target} {temp_objective} matches 0").into(),
 | 
					                        format!("score {temp_target} {temp_objective} matches 0").into(),
 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
                    Box::new(Execute::Run(Box::new(Command::Group(cmds)))),
 | 
					                    Box::new(Execute::Run(Box::new(Command::Group(Group::new(cmds))))),
 | 
				
			||||||
                    None,
 | 
					                    None,
 | 
				
			||||||
                ));
 | 
					                ));
 | 
				
			||||||
                Ok(vec![test_exists_cmd, cond_cmd])
 | 
					                Ok(vec![test_exists_cmd, cond_cmd])
 | 
				
			||||||
| 
						 | 
					@ -634,9 +636,11 @@ impl Transpiler {
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                let index_span = match destination {
 | 
					                                let index_span = match destination {
 | 
				
			||||||
                                    TranspileAssignmentTarget::Indexed(_, expr) => expr.span(),
 | 
					                                    TranspileAssignmentTarget::Indexed(_, expr) => expr.span(),
 | 
				
			||||||
                                    TranspileAssignmentTarget::Identifier(_) => unreachable!(
 | 
					                                    TranspileAssignmentTarget::Identifier(_) => {
 | 
				
			||||||
                                        "indexing value must be present (checked before)"
 | 
					                                        unreachable!(
 | 
				
			||||||
                                    ),
 | 
					                                            "indexing value must be present (checked before)"
 | 
				
			||||||
 | 
					                                        )
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
                                };
 | 
					                                };
 | 
				
			||||||
                                let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
					                                let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
                                    expression: index_span,
 | 
					                                    expression: index_span,
 | 
				
			||||||
| 
						 | 
					@ -688,9 +692,11 @@ impl Transpiler {
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                let index_span = match destination {
 | 
					                                let index_span = match destination {
 | 
				
			||||||
                                    TranspileAssignmentTarget::Indexed(_, expr) => expr.span(),
 | 
					                                    TranspileAssignmentTarget::Indexed(_, expr) => expr.span(),
 | 
				
			||||||
                                    TranspileAssignmentTarget::Identifier(_) => unreachable!(
 | 
					                                    TranspileAssignmentTarget::Identifier(_) => {
 | 
				
			||||||
                                        "indexing value must be present (checked before)"
 | 
					                                        unreachable!(
 | 
				
			||||||
                                    ),
 | 
					                                            "indexing value must be present (checked before)"
 | 
				
			||||||
 | 
					                                        )
 | 
				
			||||||
 | 
					                                    }
 | 
				
			||||||
                                };
 | 
					                                };
 | 
				
			||||||
                                let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
					                                let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
                                    expression: index_span,
 | 
					                                    expression: index_span,
 | 
				
			||||||
| 
						 | 
					@ -847,18 +853,20 @@ impl Transpiler {
 | 
				
			||||||
                        // 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) {
 | 
				
			||||||
                            let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
					                            let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
                                                    annotation: deobfuscate_annotation.span(),
 | 
					                                annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
                                                    message: "Deobfuscate annotation must be a valid scoreboard objective name.".to_string()
 | 
					                                message: "Deobfuscate annotation must be a valid scoreboard objective name."
 | 
				
			||||||
                                                });
 | 
					                                    .to_string(),
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
                            handler.receive(Box::new(err.clone()));
 | 
					                            handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                            return Err(err);
 | 
					                            return Err(err);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Ok(name_eval)
 | 
					                        Ok(name_eval)
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
					                        let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
                                                annotation: deobfuscate_annotation.span(),
 | 
					                            annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
                                                message: "Deobfuscate annotation could not have been evaluated at compile time.".to_string()
 | 
					                            message: "Deobfuscate annotation could not have been evaluated at compile time."
 | 
				
			||||||
                                            });
 | 
					                                .to_string(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
                        handler.receive(Box::new(err.clone()));
 | 
					                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                        Err(err)
 | 
					                        Err(err)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -950,34 +958,36 @@ impl Transpiler {
 | 
				
			||||||
                            // 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) {
 | 
				
			||||||
                                let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
					                                let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
                                            annotation: deobfuscate_annotation.span(),
 | 
					                                    annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
                                            message: "Deobfuscate annotation 'name' must be a valid scoreboard objective name.".to_string()
 | 
					                                    message: "Deobfuscate annotation 'name' must be a valid scoreboard objective name.".to_string(),
 | 
				
			||||||
                                        });
 | 
					                                });
 | 
				
			||||||
                                handler.receive(Box::new(err.clone()));
 | 
					                                handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                                return Err(err);
 | 
					                                return Err(err);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            if !crate::util::is_valid_scoreboard_target(&target_eval) {
 | 
					                            if !crate::util::is_valid_scoreboard_target(&target_eval) {
 | 
				
			||||||
                                let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
					                                let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
                                            annotation: deobfuscate_annotation.span(),
 | 
					                                    annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
                                            message: "Deobfuscate annotation 'target' must be a valid scoreboard player name.".to_string()
 | 
					                                    message: "Deobfuscate annotation 'target' must be a valid scoreboard player name.".to_string(),
 | 
				
			||||||
                                        });
 | 
					                                });
 | 
				
			||||||
                                handler.receive(Box::new(err.clone()));
 | 
					                                handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                                return Err(err);
 | 
					                                return Err(err);
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                            Ok((name_eval, target_eval))
 | 
					                            Ok((name_eval, target_eval))
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
					                            let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
                                        annotation: deobfuscate_annotation.span(),
 | 
					                                annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
                                        message: "Deobfuscate annotation 'name' or 'target' could not have been evaluated at compile time.".to_string()
 | 
					                                message: "Deobfuscate annotation 'name' or 'target' could not have been evaluated at compile time.".to_string(),
 | 
				
			||||||
                                    });
 | 
					                            });
 | 
				
			||||||
                            handler.receive(Box::new(err.clone()));
 | 
					                            handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                            Err(err)
 | 
					                            Err(err)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
					                        let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
                                    annotation: deobfuscate_annotation.span(),
 | 
					                            annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
                                    message: "Deobfuscate annotation 'name' and 'target' must be compile time expressions.".to_string()
 | 
					                            message:
 | 
				
			||||||
                                });
 | 
					                                "Deobfuscate annotation 'name' and 'target' must be compile time expressions."
 | 
				
			||||||
 | 
					                                    .to_string(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
                        handler.receive(Box::new(err.clone()));
 | 
					                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
                        Err(err)
 | 
					                        Err(err)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -1180,9 +1190,7 @@ impl Transpiler {
 | 
				
			||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            Ok(Vec::new())
 | 
					                            Ok(Vec::new())
 | 
				
			||||||
                        } else if matches!(target_type, StorageType::Boolean) {
 | 
					                        } else if matches!(target_type, StorageType::Boolean) {
 | 
				
			||||||
                            let cmd = Command::Raw(format!(
 | 
					                            let cmd = Command::Raw(format!("data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"));
 | 
				
			||||||
                                        "data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"
 | 
					 | 
				
			||||||
                                    ));
 | 
					 | 
				
			||||||
                            Ok(vec![cmd])
 | 
					                            Ok(vec![cmd])
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
| 
						 | 
					@ -1194,6 +1202,37 @@ impl Transpiler {
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                StorageType::String => match to {
 | 
				
			||||||
 | 
					                    DataLocation::ScoreboardValue { .. } | DataLocation::Tag { .. } => {
 | 
				
			||||||
 | 
					                        let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                            expression: expression.span(),
 | 
				
			||||||
 | 
					                            expected_type: to.value_type().into(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					                        Err(err)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    DataLocation::Storage {
 | 
				
			||||||
 | 
					                        storage_name: to_storage_name,
 | 
				
			||||||
 | 
					                        path: to_path,
 | 
				
			||||||
 | 
					                        r#type,
 | 
				
			||||||
 | 
					                    } => {
 | 
				
			||||||
 | 
					                        if r#type == &StorageType::String {
 | 
				
			||||||
 | 
					                            if storage_name == to_storage_name && path == to_path {
 | 
				
			||||||
 | 
					                                Ok(Vec::new())
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                let cmd = Command::Raw(format!("data modify storage {to_storage_name} {to_path} set from storage {storage_name} {path}"));
 | 
				
			||||||
 | 
					                                Ok(vec![cmd])
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                                expression: expression.span(),
 | 
				
			||||||
 | 
					                                expected_type: to.value_type().into(),
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					                            Err(err)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            DataLocation::ScoreboardValue {
 | 
					            DataLocation::ScoreboardValue {
 | 
				
			||||||
                objective,
 | 
					                objective,
 | 
				
			||||||
| 
						 | 
					@ -1206,9 +1245,7 @@ impl Transpiler {
 | 
				
			||||||
                    if objective == target_objective && score_target == target_target {
 | 
					                    if objective == target_objective && score_target == target_target {
 | 
				
			||||||
                        Ok(Vec::new())
 | 
					                        Ok(Vec::new())
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        let cmd = Command::Raw(format!(
 | 
					                        let cmd = Command::Raw(format!("scoreboard players operation {target_target} {target_objective} = {score_target} {objective}"));
 | 
				
			||||||
                            "scoreboard players operation {target_target} {target_objective} = {score_target} {objective}"
 | 
					 | 
				
			||||||
                        ));
 | 
					 | 
				
			||||||
                        Ok(vec![cmd])
 | 
					                        Ok(vec![cmd])
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue