check that assignments to comptime variables are only done in comptime conditionals
This commit is contained in:
		
							parent
							
								
									7f276a4139
								
							
						
					
					
						commit
						0f6d9b301f
					
				| 
						 | 
					@ -42,8 +42,6 @@ mod scope;
 | 
				
			||||||
use error::{IncompatibleFunctionAnnotation, InvalidNamespaceName, UnexpectedExpression};
 | 
					use error::{IncompatibleFunctionAnnotation, InvalidNamespaceName, UnexpectedExpression};
 | 
				
			||||||
pub use scope::{SemanticScope, VariableType};
 | 
					pub use scope::{SemanticScope, VariableType};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::syntax::syntax_tree::ConnectedList;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl ProgramFile {
 | 
					impl ProgramFile {
 | 
				
			||||||
    /// Analyzes the semantics of the program.
 | 
					    /// Analyzes the semantics of the program.
 | 
				
			||||||
    pub fn analyze_semantics(
 | 
					    pub fn analyze_semantics(
 | 
				
			||||||
| 
						 | 
					@ -172,8 +170,7 @@ impl Function {
 | 
				
			||||||
        if let Some(parameters) = self
 | 
					        if let Some(parameters) = self
 | 
				
			||||||
            .parameters()
 | 
					            .parameters()
 | 
				
			||||||
            .as_ref()
 | 
					            .as_ref()
 | 
				
			||||||
            .map(ConnectedList::elements)
 | 
					            .map(|l| l.elements().collect::<Vec<_>>())
 | 
				
			||||||
            .map(Iterator::collect::<Vec<_>>)
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            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())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -473,7 +473,7 @@ impl SourceElement for TemplateStringLiteralPart {
 | 
				
			||||||
///     'lua' '(' (Expression (',' Expression)*)? ')' '{' (.*?)* '}'
 | 
					///     'lua' '(' (Expression (',' Expression)*)? ')' '{' (.*?)* '}'
 | 
				
			||||||
/// ```
 | 
					/// ```
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
					#[derive(Debug, Clone, Getters)]
 | 
				
			||||||
pub struct LuaCode {
 | 
					pub struct LuaCode {
 | 
				
			||||||
    /// The `lua` keyword.
 | 
					    /// The `lua` keyword.
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
| 
						 | 
					@ -496,6 +496,12 @@ pub struct LuaCode {
 | 
				
			||||||
    /// The right brace of the lua code.
 | 
					    /// The right brace of the lua code.
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    right_brace: Punctuation,
 | 
					    right_brace: Punctuation,
 | 
				
			||||||
 | 
					    /// Result of the evaluation
 | 
				
			||||||
 | 
					    #[cfg(all(feature = "lua", feature = "shulkerbox"))]
 | 
				
			||||||
 | 
					    #[cfg_attr(feature = "serde", serde(skip))]
 | 
				
			||||||
 | 
					    pub(crate) eval_result: std::sync::OnceLock<
 | 
				
			||||||
 | 
					        Result<crate::transpile::expression::ComptimeValue, crate::transpile::error::NotComptime>,
 | 
				
			||||||
 | 
					    >,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SourceElement for LuaCode {
 | 
					impl SourceElement for LuaCode {
 | 
				
			||||||
| 
						 | 
					@ -520,6 +526,69 @@ impl LuaCode {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PartialEq, Eq, PartialOrd, Ord, Hash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PartialEq for LuaCode {
 | 
				
			||||||
 | 
					    fn eq(&self, other: &Self) -> bool {
 | 
				
			||||||
 | 
					        self.lua_keyword == other.lua_keyword
 | 
				
			||||||
 | 
					            && self.left_parenthesis == other.left_parenthesis
 | 
				
			||||||
 | 
					            && self.inputs == other.inputs
 | 
				
			||||||
 | 
					            && self.right_parenthesis == other.right_parenthesis
 | 
				
			||||||
 | 
					            && self.left_brace == other.left_brace
 | 
				
			||||||
 | 
					            && self.code == other.code
 | 
				
			||||||
 | 
					            && self.right_brace == other.right_brace
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					impl Eq for LuaCode {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Ord for LuaCode {
 | 
				
			||||||
 | 
					    fn cmp(&self, other: &Self) -> Ordering {
 | 
				
			||||||
 | 
					        let cmp_lua = self.lua_keyword.cmp(&other.lua_keyword);
 | 
				
			||||||
 | 
					        if cmp_lua != Ordering::Equal {
 | 
				
			||||||
 | 
					            return cmp_lua;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let cmp_left_paren = self.left_parenthesis.cmp(&other.left_parenthesis);
 | 
				
			||||||
 | 
					        if cmp_left_paren != Ordering::Equal {
 | 
				
			||||||
 | 
					            return cmp_left_paren;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let cmp_inputs = self.inputs.cmp(&other.inputs);
 | 
				
			||||||
 | 
					        if cmp_inputs != Ordering::Equal {
 | 
				
			||||||
 | 
					            return cmp_inputs;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let cmp_right_paren = self.right_parenthesis.cmp(&other.right_parenthesis);
 | 
				
			||||||
 | 
					        if cmp_right_paren != Ordering::Equal {
 | 
				
			||||||
 | 
					            return cmp_right_paren;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let cmp_left_brace = self.left_brace.cmp(&other.left_brace);
 | 
				
			||||||
 | 
					        if cmp_left_brace != Ordering::Equal {
 | 
				
			||||||
 | 
					            return cmp_left_brace;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let cmp_code = self.code.cmp(&other.code);
 | 
				
			||||||
 | 
					        if cmp_code != Ordering::Equal {
 | 
				
			||||||
 | 
					            return cmp_code;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        self.right_brace.cmp(&other.right_brace)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl PartialOrd for LuaCode {
 | 
				
			||||||
 | 
					    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 | 
				
			||||||
 | 
					        Some(self.cmp(other))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::hash::Hash for LuaCode {
 | 
				
			||||||
 | 
					    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
				
			||||||
 | 
					        self.lua_keyword.hash(state);
 | 
				
			||||||
 | 
					        self.left_parenthesis.hash(state);
 | 
				
			||||||
 | 
					        self.inputs.hash(state);
 | 
				
			||||||
 | 
					        self.right_parenthesis.hash(state);
 | 
				
			||||||
 | 
					        self.left_brace.hash(state);
 | 
				
			||||||
 | 
					        self.code.hash(state);
 | 
				
			||||||
 | 
					        self.right_brace.hash(state);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents a member access in the syntax tree.
 | 
					/// Represents a member access in the syntax tree.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Syntax Synopsis:
 | 
					/// Syntax Synopsis:
 | 
				
			||||||
| 
						 | 
					@ -802,6 +871,8 @@ impl Parser<'_> {
 | 
				
			||||||
                    left_brace: tree.open,
 | 
					                    left_brace: tree.open,
 | 
				
			||||||
                    code: tree.tree?,
 | 
					                    code: tree.tree?,
 | 
				
			||||||
                    right_brace: tree.close,
 | 
					                    right_brace: tree.close,
 | 
				
			||||||
 | 
					                    #[cfg(all(feature = "lua", feature = "shulkerbox"))]
 | 
				
			||||||
 | 
					                    eval_result: std::sync::OnceLock::new(),
 | 
				
			||||||
                })))
 | 
					                })))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,81 @@
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::{
 | 
				
			||||||
 | 
					    base::{self, Handler},
 | 
				
			||||||
 | 
					    syntax::syntax_tree::statement::{
 | 
				
			||||||
 | 
					        execute_block::{ExecuteBlock, ExecuteBlockTail},
 | 
				
			||||||
 | 
					        AssignmentDestination, Block, SemicolonStatement, Statement,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    transpile::{error::AssignmentError, Scope, TranspileError, TranspileResult, VariableData},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Block {
 | 
				
			||||||
 | 
					    pub(super) fn check_no_comptime_assignments(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<()> {
 | 
				
			||||||
 | 
					        for statement in self.statements() {
 | 
				
			||||||
 | 
					            match statement {
 | 
				
			||||||
 | 
					                Statement::Semicolon(sem) => {
 | 
				
			||||||
 | 
					                    if let SemicolonStatement::Assignment(assignment) = sem.statement() {
 | 
				
			||||||
 | 
					                        if let AssignmentDestination::Identifier(ident) = assignment.destination() {
 | 
				
			||||||
 | 
					                            if let Some(VariableData::ComptimeValue { .. }) =
 | 
				
			||||||
 | 
					                                scope.get_variable(ident.span.str()).as_deref()
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                let err = TranspileError::AssignmentError(AssignmentError {
 | 
				
			||||||
 | 
					                                    identifier: ident.span.clone(),
 | 
				
			||||||
 | 
					                                    message: "cannot assign to a compile-time variable in a conditional execute block"
 | 
				
			||||||
 | 
					                                        .to_string(),
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                                handler.receive(Box::new(err.clone()));
 | 
				
			||||||
 | 
					                                return Err(err);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Statement::Grouping(group) => {
 | 
				
			||||||
 | 
					                    group
 | 
				
			||||||
 | 
					                        .block()
 | 
				
			||||||
 | 
					                        .check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Statement::ExecuteBlock(ex) => {
 | 
				
			||||||
 | 
					                    ex.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Statement::WhileLoop(while_loop) => {
 | 
				
			||||||
 | 
					                    while_loop
 | 
				
			||||||
 | 
					                        .block()
 | 
				
			||||||
 | 
					                        .check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                _ => (),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ExecuteBlock {
 | 
				
			||||||
 | 
					    pub(super) fn check_no_comptime_assignments(
 | 
				
			||||||
 | 
					        &self,
 | 
				
			||||||
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<()> {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::HeadTail(_, tail) => match tail {
 | 
				
			||||||
 | 
					                ExecuteBlockTail::Block(block) => {
 | 
				
			||||||
 | 
					                    block.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                ExecuteBlockTail::ExecuteBlock(_, ex) => {
 | 
				
			||||||
 | 
					                    ex.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Self::IfElse(_, then, el) => {
 | 
				
			||||||
 | 
					                then.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					                el.block().check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -87,14 +87,22 @@ mod enabled {
 | 
				
			||||||
            scope: &Arc<Scope>,
 | 
					            scope: &Arc<Scope>,
 | 
				
			||||||
            handler: &impl Handler<base::Error>,
 | 
					            handler: &impl Handler<base::Error>,
 | 
				
			||||||
        ) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
 | 
					        ) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
 | 
				
			||||||
 | 
					            if let Some(res) = self.eval_result.get().cloned() {
 | 
				
			||||||
 | 
					                return Ok(res);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // required to keep the lua instance alive
 | 
					            // required to keep the lua instance alive
 | 
				
			||||||
            let (lua_result, _lua) = self.eval(scope, handler)?;
 | 
					            let (lua_result, _lua) = self.eval(scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.handle_lua_result(lua_result, handler).map(|res| {
 | 
					            let res = self.handle_lua_result(lua_result, handler).map(|res| {
 | 
				
			||||||
                res.ok_or_else(|| NotComptime {
 | 
					                res.ok_or_else(|| NotComptime {
 | 
				
			||||||
                    expression: self.span(),
 | 
					                    expression: self.span(),
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            })
 | 
					            })?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.eval_result.set(res.clone()).ok();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(res)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
 | 
					        fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,6 +49,9 @@ pub use variables::{Scope, VariableData};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod util;
 | 
					pub mod util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
 | 
					mod checks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Data of a function.
 | 
					/// Data of a function.
 | 
				
			||||||
#[derive(Clone, PartialEq, Eq)]
 | 
					#[derive(Clone, PartialEq, Eq)]
 | 
				
			||||||
pub struct FunctionData {
 | 
					pub struct FunctionData {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -943,111 +943,104 @@ impl Transpiler {
 | 
				
			||||||
    ) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
 | 
					    ) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
 | 
				
			||||||
        match execute {
 | 
					        match execute {
 | 
				
			||||||
            ExecuteBlock::HeadTail(head, tail) => {
 | 
					            ExecuteBlock::HeadTail(head, tail) => {
 | 
				
			||||||
                let tail = match tail {
 | 
					                if let ExecuteBlockHead::Conditional(cond) = head {
 | 
				
			||||||
                    ExecuteBlockTail::Block(block) => {
 | 
					                    let cond_eval = cond.condition().expression().comptime_eval(scope, handler);
 | 
				
			||||||
                        let mut errors = Vec::new();
 | 
					                    if cond_eval.is_err()
 | 
				
			||||||
                        let commands = block
 | 
					                        || cond_eval.is_ok_and(|val| !matches!(val, ComptimeValue::Boolean(_)))
 | 
				
			||||||
                            .statements()
 | 
					                    {
 | 
				
			||||||
                            .iter()
 | 
					                        match tail {
 | 
				
			||||||
                            .flat_map(|s| {
 | 
					                            ExecuteBlockTail::Block(block) => {
 | 
				
			||||||
                                self.transpile_statement(s, program_identifier, scope, handler)
 | 
					                                block.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
                                    .unwrap_or_else(|err| {
 | 
					                            }
 | 
				
			||||||
                                        errors.push(err);
 | 
					                            ExecuteBlockTail::ExecuteBlock(_, ex) => {
 | 
				
			||||||
                                        Vec::new()
 | 
					                                ex.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
                                    })
 | 
					                            }
 | 
				
			||||||
                            })
 | 
					 | 
				
			||||||
                            .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        if !errors.is_empty() {
 | 
					 | 
				
			||||||
                            return Err(errors.remove(0));
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        if commands.is_empty() {
 | 
					 | 
				
			||||||
                            Ok(None)
 | 
					 | 
				
			||||||
                        } else {
 | 
					 | 
				
			||||||
                            Ok(Some((Vec::new(), BTreeMap::new(), Execute::Runs(commands))))
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    ExecuteBlockTail::ExecuteBlock(_, execute_block) => self
 | 
					                }
 | 
				
			||||||
                        .transpile_execute_block_internal(
 | 
					 | 
				
			||||||
                            execute_block,
 | 
					 | 
				
			||||||
                            program_identifier,
 | 
					 | 
				
			||||||
                            scope,
 | 
					 | 
				
			||||||
                            handler,
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                }?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                self.combine_execute_head_tail(head, tail, program_identifier, scope, handler)
 | 
					                self.combine_execute_head_tail(
 | 
				
			||||||
 | 
					                    head,
 | 
				
			||||||
 | 
					                    |transpiler, program_identifier, scope, handler| match tail {
 | 
				
			||||||
 | 
					                        ExecuteBlockTail::Block(block) => {
 | 
				
			||||||
 | 
					                            let mut errors = Vec::new();
 | 
				
			||||||
 | 
					                            let commands = block
 | 
				
			||||||
 | 
					                                .statements()
 | 
				
			||||||
 | 
					                                .iter()
 | 
				
			||||||
 | 
					                                .flat_map(|s| {
 | 
				
			||||||
 | 
					                                    transpiler
 | 
				
			||||||
 | 
					                                        .transpile_statement(s, program_identifier, scope, handler)
 | 
				
			||||||
 | 
					                                        .unwrap_or_else(|err| {
 | 
				
			||||||
 | 
					                                            errors.push(err);
 | 
				
			||||||
 | 
					                                            Vec::new()
 | 
				
			||||||
 | 
					                                        })
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
 | 
					                                .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if !errors.is_empty() {
 | 
				
			||||||
 | 
					                                return Err(errors.remove(0));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            if commands.is_empty() {
 | 
				
			||||||
 | 
					                                Ok(None)
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                Ok(Some((Vec::new(), BTreeMap::new(), Execute::Runs(commands))))
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        ExecuteBlockTail::ExecuteBlock(_, execute_block) => transpiler
 | 
				
			||||||
 | 
					                            .transpile_execute_block_internal(
 | 
				
			||||||
 | 
					                                execute_block,
 | 
				
			||||||
 | 
					                                program_identifier,
 | 
				
			||||||
 | 
					                                scope,
 | 
				
			||||||
 | 
					                                handler,
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    program_identifier,
 | 
				
			||||||
 | 
					                    scope,
 | 
				
			||||||
 | 
					                    handler,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlock::IfElse(cond, block, el) => {
 | 
					            ExecuteBlock::IfElse(cond, block, el) => {
 | 
				
			||||||
                let statements = block.statements();
 | 
					                let cond_eval = cond.condition().expression().comptime_eval(scope, handler);
 | 
				
			||||||
                let then = if statements.is_empty() {
 | 
					                if cond_eval.is_err()
 | 
				
			||||||
                    Some(Execute::Runs(Vec::new()))
 | 
					                    || cond_eval.is_ok_and(|val| !matches!(val, ComptimeValue::Boolean(_)))
 | 
				
			||||||
                } else if statements.len() > 1 {
 | 
					                {
 | 
				
			||||||
                    let mut errors = Vec::new();
 | 
					                    block.check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
                    let commands = statements
 | 
					                    el.block().check_no_comptime_assignments(scope, handler)?;
 | 
				
			||||||
                        .iter()
 | 
					                }
 | 
				
			||||||
                        .flat_map(|statement| {
 | 
					 | 
				
			||||||
                            self.transpile_statement(statement, program_identifier, scope, handler)
 | 
					 | 
				
			||||||
                                .unwrap_or_else(|err| {
 | 
					 | 
				
			||||||
                                    errors.push(err);
 | 
					 | 
				
			||||||
                                    Vec::new()
 | 
					 | 
				
			||||||
                                })
 | 
					 | 
				
			||||||
                        })
 | 
					 | 
				
			||||||
                        .collect();
 | 
					 | 
				
			||||||
                    if !errors.is_empty() {
 | 
					 | 
				
			||||||
                        return Err(errors.remove(0));
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    Some(Execute::Runs(commands))
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    let cmds = self.transpile_statement(
 | 
					 | 
				
			||||||
                        &statements[0],
 | 
					 | 
				
			||||||
                        program_identifier,
 | 
					 | 
				
			||||||
                        scope,
 | 
					 | 
				
			||||||
                        handler,
 | 
					 | 
				
			||||||
                    )?;
 | 
					 | 
				
			||||||
                    if cmds.len() > 1 {
 | 
					 | 
				
			||||||
                        Some(Execute::Runs(cmds))
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        cmds.into_iter()
 | 
					 | 
				
			||||||
                            .next()
 | 
					 | 
				
			||||||
                            .map(|cmd| Execute::Run(Box::new(cmd)))
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                then.map_or_else(
 | 
					                self.transpile_conditional(
 | 
				
			||||||
                    || Ok(None),
 | 
					                    cond,
 | 
				
			||||||
                    |then| {
 | 
					                    |transpiler, program_identifier, scope, handler| {
 | 
				
			||||||
                        self.transpile_conditional(
 | 
					                        let ex = transpiler.transpile_conditional_block(
 | 
				
			||||||
                            cond,
 | 
					                            block,
 | 
				
			||||||
                            then,
 | 
					 | 
				
			||||||
                            Some(el),
 | 
					 | 
				
			||||||
                            program_identifier,
 | 
					                            program_identifier,
 | 
				
			||||||
                            scope,
 | 
					                            scope,
 | 
				
			||||||
                            handler,
 | 
					                            handler,
 | 
				
			||||||
                        )
 | 
					                        )?;
 | 
				
			||||||
 | 
					                        Ok(Some((Vec::new(), BTreeMap::new(), ex)))
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
 | 
					                    Some(el),
 | 
				
			||||||
 | 
					                    program_identifier,
 | 
				
			||||||
 | 
					                    scope,
 | 
				
			||||||
 | 
					                    handler,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn transpile_conditional(
 | 
					    fn transpile_conditional_block(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        cond: &Conditional,
 | 
					        block: &Block,
 | 
				
			||||||
        then: Execute,
 | 
					 | 
				
			||||||
        el: Option<&Else>,
 | 
					 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
 | 
					    ) -> TranspileResult<Execute> {
 | 
				
			||||||
        let cond_expression = cond.condition().expression().as_ref();
 | 
					        let statements = block.statements();
 | 
				
			||||||
 | 
					        if statements.is_empty() {
 | 
				
			||||||
        let mut errors = Vec::new();
 | 
					            Ok(Execute::Runs(Vec::new()))
 | 
				
			||||||
 | 
					        } else if statements.len() > 1 {
 | 
				
			||||||
        let el = el.and_then(|el| {
 | 
					            let mut errors = Vec::new();
 | 
				
			||||||
            let (_, block) = el.clone().dissolve();
 | 
					            let commands = statements
 | 
				
			||||||
            let statements = block.statements();
 | 
					 | 
				
			||||||
            let cmds = statements
 | 
					 | 
				
			||||||
                .iter()
 | 
					                .iter()
 | 
				
			||||||
                .flat_map(|statement| {
 | 
					                .flat_map(|statement| {
 | 
				
			||||||
                    self.transpile_statement(statement, program_identifier, scope, handler)
 | 
					                    self.transpile_statement(statement, program_identifier, scope, handler)
 | 
				
			||||||
| 
						 | 
					@ -1056,118 +1049,226 @@ impl Transpiler {
 | 
				
			||||||
                            Vec::new()
 | 
					                            Vec::new()
 | 
				
			||||||
                        })
 | 
					                        })
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .collect::<Vec<_>>();
 | 
					                .collect();
 | 
				
			||||||
 | 
					 | 
				
			||||||
            match cmds.len() {
 | 
					 | 
				
			||||||
                0 => None,
 | 
					 | 
				
			||||||
                1 => Some(Execute::Run(Box::new(
 | 
					 | 
				
			||||||
                    cmds.into_iter().next().expect("length is 1"),
 | 
					 | 
				
			||||||
                ))),
 | 
					 | 
				
			||||||
                _ => Some(Execute::Runs(cmds)),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
 | 
					 | 
				
			||||||
            if value {
 | 
					 | 
				
			||||||
                Ok(Some((Vec::new(), BTreeMap::new(), then)))
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                Ok(el.map(|el| (Vec::new(), BTreeMap::new(), el)))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            if !errors.is_empty() {
 | 
					            if !errors.is_empty() {
 | 
				
			||||||
                return Err(errors.remove(0));
 | 
					                return Err(errors.remove(0));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Ok(Execute::Runs(commands))
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            let cmds =
 | 
				
			||||||
 | 
					                self.transpile_statement(&statements[0], program_identifier, scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let (pre_cond_cmds, prepare_variables, cond) =
 | 
					            if cmds.len() > 1 {
 | 
				
			||||||
                self.transpile_expression_as_condition(cond_expression, scope, handler)?;
 | 
					                Ok(Execute::Runs(cmds))
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Ok(cmds.into_iter().next().map_or_else(
 | 
				
			||||||
 | 
					                    || Execute::Runs(Vec::new()),
 | 
				
			||||||
 | 
					                    |cmd| Execute::Run(Box::new(cmd)),
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            match cond {
 | 
					    fn transpile_conditional<F, H>(
 | 
				
			||||||
                ExtendedCondition::Runtime(cond) => Ok(Some((
 | 
					        &mut self,
 | 
				
			||||||
                    pre_cond_cmds,
 | 
					        cond: &Conditional,
 | 
				
			||||||
                    prepare_variables,
 | 
					        compile_then: F,
 | 
				
			||||||
                    Execute::If(cond, Box::new(then), el.map(Box::new)),
 | 
					        el: Option<&Else>,
 | 
				
			||||||
                ))),
 | 
					        program_identifier: &str,
 | 
				
			||||||
                ExtendedCondition::Comptime(cond) => {
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
                    if cond {
 | 
					        handler: &H,
 | 
				
			||||||
                        Ok(Some((Vec::new(), prepare_variables, then)))
 | 
					    ) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>>
 | 
				
			||||||
                    } else {
 | 
					    where
 | 
				
			||||||
                        Ok(el.map(|el| (Vec::new(), prepare_variables, el)))
 | 
					        H: Handler<base::Error>,
 | 
				
			||||||
                    }
 | 
					        F: FnOnce(
 | 
				
			||||||
 | 
					            &mut Self,
 | 
				
			||||||
 | 
					            &str,
 | 
				
			||||||
 | 
					            &Arc<Scope>,
 | 
				
			||||||
 | 
					            &H,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					            -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let cond_expression = cond.condition().expression().as_ref();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut errors = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
 | 
				
			||||||
 | 
					            return if value {
 | 
				
			||||||
 | 
					                let then = compile_then(self, program_identifier, scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Ok(then)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                let (el, mut else_errors) = el.map_or_else(
 | 
				
			||||||
 | 
					                    || (None, Vec::new()),
 | 
				
			||||||
 | 
					                    |el| self.transpile_else(el, program_identifier, scope, handler),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                if !else_errors.is_empty() {
 | 
				
			||||||
 | 
					                    return Err(else_errors.remove(0));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Ok(el.map(|el| (Vec::new(), BTreeMap::new(), el)))
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let then = compile_then(self, program_identifier, scope, handler)?;
 | 
				
			||||||
 | 
					        let (el, else_errors) = el.map_or_else(
 | 
				
			||||||
 | 
					            || (None, Vec::new()),
 | 
				
			||||||
 | 
					            |el| self.transpile_else(el, program_identifier, scope, handler),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        errors.extend(else_errors);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if !errors.is_empty() {
 | 
				
			||||||
 | 
					            return Err(errors.remove(0));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (pre_cond_cmds, prepare_variables, cond) =
 | 
				
			||||||
 | 
					            self.transpile_expression_as_condition(cond_expression, scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match cond {
 | 
				
			||||||
 | 
					            ExtendedCondition::Runtime(cond) => {
 | 
				
			||||||
 | 
					                if let Some((mut prepare_cmds, mut then_prepare_variables, then)) = then {
 | 
				
			||||||
 | 
					                    prepare_cmds.extend(pre_cond_cmds);
 | 
				
			||||||
 | 
					                    then_prepare_variables.extend(prepare_variables);
 | 
				
			||||||
 | 
					                    Ok(Some((
 | 
				
			||||||
 | 
					                        prepare_cmds,
 | 
				
			||||||
 | 
					                        then_prepare_variables,
 | 
				
			||||||
 | 
					                        Execute::If(cond, Box::new(then), el.map(Box::new)),
 | 
				
			||||||
 | 
					                    )))
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Ok(Some((
 | 
				
			||||||
 | 
					                        pre_cond_cmds,
 | 
				
			||||||
 | 
					                        prepare_variables,
 | 
				
			||||||
 | 
					                        Execute::If(cond, Box::new(Execute::Runs(Vec::new())), el.map(Box::new)),
 | 
				
			||||||
 | 
					                    )))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ExtendedCondition::Comptime(cond) => {
 | 
				
			||||||
 | 
					                if cond {
 | 
				
			||||||
 | 
					                    then.map_or_else(
 | 
				
			||||||
 | 
					                        || {
 | 
				
			||||||
 | 
					                            Ok(Some((
 | 
				
			||||||
 | 
					                                Vec::new(),
 | 
				
			||||||
 | 
					                                BTreeMap::new(),
 | 
				
			||||||
 | 
					                                Execute::Runs(Vec::new()),
 | 
				
			||||||
 | 
					                            )))
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        |then| Ok(Some(then)),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Ok(el.map(|el| (Vec::new(), prepare_variables, el)))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[expect(clippy::too_many_lines)]
 | 
					    fn transpile_else(
 | 
				
			||||||
    fn combine_execute_head_tail(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        head: &ExecuteBlockHead,
 | 
					        el: &Else,
 | 
				
			||||||
        tail: Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>,
 | 
					 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
 | 
					    ) -> (Option<Execute>, Vec<TranspileError>) {
 | 
				
			||||||
        Ok(match head {
 | 
					        let mut else_errors = Vec::new();
 | 
				
			||||||
            ExecuteBlockHead::Conditional(cond) => {
 | 
					
 | 
				
			||||||
                if let Some((mut pre_cmds, prepare_variables, tail)) = tail {
 | 
					        let block = el.block();
 | 
				
			||||||
                    self.transpile_conditional(
 | 
					        let statements = block.statements();
 | 
				
			||||||
                        cond,
 | 
					        let cmds = statements
 | 
				
			||||||
                        tail,
 | 
					            .iter()
 | 
				
			||||||
                        None,
 | 
					            .flat_map(|statement| {
 | 
				
			||||||
                        program_identifier,
 | 
					                self.transpile_statement(statement, program_identifier, scope, handler)
 | 
				
			||||||
                        scope,
 | 
					                    .unwrap_or_else(|err| {
 | 
				
			||||||
                        handler,
 | 
					                        else_errors.push(err);
 | 
				
			||||||
                    )?
 | 
					                        Vec::new()
 | 
				
			||||||
                    .map(|(pre_cond_cmds, mut prep_variables, cond)| {
 | 
					 | 
				
			||||||
                        pre_cmds.extend(pre_cond_cmds);
 | 
					 | 
				
			||||||
                        prep_variables.extend(prepare_variables);
 | 
					 | 
				
			||||||
                        (pre_cmds, prep_variables, cond)
 | 
					 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                } else {
 | 
					            })
 | 
				
			||||||
                    None
 | 
					            .collect::<Vec<_>>();
 | 
				
			||||||
                }
 | 
					
 | 
				
			||||||
            }
 | 
					        let el = match cmds.len() {
 | 
				
			||||||
 | 
					            0 => None,
 | 
				
			||||||
 | 
					            1 => Some(Execute::Run(Box::new(
 | 
				
			||||||
 | 
					                cmds.into_iter().next().expect("length is 1"),
 | 
				
			||||||
 | 
					            ))),
 | 
				
			||||||
 | 
					            _ => Some(Execute::Runs(cmds)),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        (el, else_errors)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
 | 
					    fn combine_execute_head_tail<H, F>(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        head: &ExecuteBlockHead,
 | 
				
			||||||
 | 
					        compile_tail: F,
 | 
				
			||||||
 | 
					        program_identifier: &str,
 | 
				
			||||||
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
 | 
					        handler: &H,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        H: Handler<base::Error>,
 | 
				
			||||||
 | 
					        F: FnOnce(
 | 
				
			||||||
 | 
					            &mut Self,
 | 
				
			||||||
 | 
					            &str,
 | 
				
			||||||
 | 
					            &Arc<Scope>,
 | 
				
			||||||
 | 
					            &H,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					            -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Ok(match head {
 | 
				
			||||||
 | 
					            ExecuteBlockHead::Conditional(cond) => self.transpile_conditional(
 | 
				
			||||||
 | 
					                cond,
 | 
				
			||||||
 | 
					                compile_tail,
 | 
				
			||||||
 | 
					                None,
 | 
				
			||||||
 | 
					                program_identifier,
 | 
				
			||||||
 | 
					                scope,
 | 
				
			||||||
 | 
					                handler,
 | 
				
			||||||
 | 
					            )?,
 | 
				
			||||||
            ExecuteBlockHead::As(r#as) => {
 | 
					            ExecuteBlockHead::As(r#as) => {
 | 
				
			||||||
                let selector = r#as
 | 
					                let selector = r#as
 | 
				
			||||||
                    .as_selector()
 | 
					                    .as_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = selector.into_sb();
 | 
					                let (macro_string, prepare_variables) = selector.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::As(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::As(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::At(at) => {
 | 
					            ExecuteBlockHead::At(at) => {
 | 
				
			||||||
                let selector = at
 | 
					                let selector = at
 | 
				
			||||||
                    .at_selector()
 | 
					                    .at_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = selector.into_sb();
 | 
					                let (macro_string, prepare_variables) = selector.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::At(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::At(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Align(align) => {
 | 
					            ExecuteBlockHead::Align(align) => {
 | 
				
			||||||
                let align = align
 | 
					                let align = align
 | 
				
			||||||
                    .align_selector()
 | 
					                    .align_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = align.into_sb();
 | 
					                let (macro_string, prepare_variables) = align.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Align(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Align(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Anchored(anchored) => {
 | 
					            ExecuteBlockHead::Anchored(anchored) => {
 | 
				
			||||||
                let anchor =
 | 
					                let anchor =
 | 
				
			||||||
| 
						 | 
					@ -1175,28 +1276,32 @@ impl Transpiler {
 | 
				
			||||||
                        .anchored_selector()
 | 
					                        .anchored_selector()
 | 
				
			||||||
                        .to_macro_string(Some(self), scope, handler)?;
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = anchor.into_sb();
 | 
					                let (macro_string, prepare_variables) = anchor.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Anchored(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Anchored(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::In(r#in) => {
 | 
					            ExecuteBlockHead::In(r#in) => {
 | 
				
			||||||
                let dimension = r#in
 | 
					                let dimension = r#in
 | 
				
			||||||
                    .in_selector()
 | 
					                    .in_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = dimension.into_sb();
 | 
					                let (macro_string, prepare_variables) = dimension.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::In(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::In(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Positioned(positioned) => {
 | 
					            ExecuteBlockHead::Positioned(positioned) => {
 | 
				
			||||||
                let position =
 | 
					                let position =
 | 
				
			||||||
| 
						 | 
					@ -1204,14 +1309,16 @@ impl Transpiler {
 | 
				
			||||||
                        .positioned_selector()
 | 
					                        .positioned_selector()
 | 
				
			||||||
                        .to_macro_string(Some(self), scope, handler)?;
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = position.into_sb();
 | 
					                let (macro_string, prepare_variables) = position.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Positioned(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Positioned(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Rotated(rotated) => {
 | 
					            ExecuteBlockHead::Rotated(rotated) => {
 | 
				
			||||||
                let rotation =
 | 
					                let rotation =
 | 
				
			||||||
| 
						 | 
					@ -1219,14 +1326,16 @@ impl Transpiler {
 | 
				
			||||||
                        .rotated_selector()
 | 
					                        .rotated_selector()
 | 
				
			||||||
                        .to_macro_string(Some(self), scope, handler)?;
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = rotation.into_sb();
 | 
					                let (macro_string, prepare_variables) = rotation.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Rotated(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Rotated(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Facing(facing) => {
 | 
					            ExecuteBlockHead::Facing(facing) => {
 | 
				
			||||||
                let facing =
 | 
					                let facing =
 | 
				
			||||||
| 
						 | 
					@ -1234,56 +1343,64 @@ impl Transpiler {
 | 
				
			||||||
                        .facing_selector()
 | 
					                        .facing_selector()
 | 
				
			||||||
                        .to_macro_string(Some(self), scope, handler)?;
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = facing.into_sb();
 | 
					                let (macro_string, prepare_variables) = facing.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Facing(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Facing(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::AsAt(as_at) => {
 | 
					            ExecuteBlockHead::AsAt(as_at) => {
 | 
				
			||||||
                let selector = as_at
 | 
					                let selector = as_at
 | 
				
			||||||
                    .asat_selector()
 | 
					                    .asat_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = selector.into_sb();
 | 
					                let (macro_string, prepare_variables) = selector.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::AsAt(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::AsAt(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::On(on) => {
 | 
					            ExecuteBlockHead::On(on) => {
 | 
				
			||||||
                let dimension = on
 | 
					                let dimension = on
 | 
				
			||||||
                    .on_selector()
 | 
					                    .on_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = dimension.into_sb();
 | 
					                let (macro_string, prepare_variables) = dimension.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::On(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::On(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Store(store) => {
 | 
					            ExecuteBlockHead::Store(store) => {
 | 
				
			||||||
                let store = store
 | 
					                let store = store
 | 
				
			||||||
                    .store_selector()
 | 
					                    .store_selector()
 | 
				
			||||||
                    .to_macro_string(Some(self), scope, handler)?;
 | 
					                    .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = store.into_sb();
 | 
					                let (macro_string, prepare_variables) = store.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Store(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Store(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            ExecuteBlockHead::Summon(summon) => {
 | 
					            ExecuteBlockHead::Summon(summon) => {
 | 
				
			||||||
                let entity =
 | 
					                let entity =
 | 
				
			||||||
| 
						 | 
					@ -1291,14 +1408,16 @@ impl Transpiler {
 | 
				
			||||||
                        .summon_selector()
 | 
					                        .summon_selector()
 | 
				
			||||||
                        .to_macro_string(Some(self), scope, handler)?;
 | 
					                        .to_macro_string(Some(self), scope, handler)?;
 | 
				
			||||||
                let (macro_string, prepare_variables) = entity.into_sb();
 | 
					                let (macro_string, prepare_variables) = entity.into_sb();
 | 
				
			||||||
                tail.map(|(pre_cmds, mut prep_variables, tail)| {
 | 
					                compile_tail(self, program_identifier, scope, handler)?.map(
 | 
				
			||||||
                    prep_variables.extend(prepare_variables);
 | 
					                    |(pre_cmds, mut prep_variables, tail)| {
 | 
				
			||||||
                    (
 | 
					                        prep_variables.extend(prepare_variables);
 | 
				
			||||||
                        pre_cmds,
 | 
					                        (
 | 
				
			||||||
                        prep_variables,
 | 
					                            pre_cmds,
 | 
				
			||||||
                        Execute::Summon(macro_string, Box::new(tail)),
 | 
					                            prep_variables,
 | 
				
			||||||
                    )
 | 
					                            Execute::Summon(macro_string, Box::new(tail)),
 | 
				
			||||||
                })
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue