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};
|
||||
pub use scope::{SemanticScope, VariableType};
|
||||
|
||||
use super::syntax::syntax_tree::ConnectedList;
|
||||
|
||||
impl ProgramFile {
|
||||
/// Analyzes the semantics of the program.
|
||||
pub fn analyze_semantics(
|
||||
|
@ -172,8 +170,7 @@ impl Function {
|
|||
if let Some(parameters) = self
|
||||
.parameters()
|
||||
.as_ref()
|
||||
.map(ConnectedList::elements)
|
||||
.map(Iterator::collect::<Vec<_>>)
|
||||
.map(|l| l.elements().collect::<Vec<_>>())
|
||||
{
|
||||
if let Some(incompatible) = self.annotations().iter().find(|a| {
|
||||
["tick", "load", "uninstall"].contains(&a.assignment().identifier.span.str())
|
||||
|
|
|
@ -473,7 +473,7 @@ impl SourceElement for TemplateStringLiteralPart {
|
|||
/// 'lua' '(' (Expression (',' Expression)*)? ')' '{' (.*?)* '}'
|
||||
/// ```
|
||||
#[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 {
|
||||
/// The `lua` keyword.
|
||||
#[get = "pub"]
|
||||
|
@ -496,6 +496,12 @@ pub struct LuaCode {
|
|||
/// The right brace of the lua code.
|
||||
#[get = "pub"]
|
||||
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 {
|
||||
|
@ -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.
|
||||
///
|
||||
/// Syntax Synopsis:
|
||||
|
@ -802,6 +871,8 @@ impl Parser<'_> {
|
|||
left_brace: tree.open,
|
||||
code: tree.tree?,
|
||||
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>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
|
||||
if let Some(res) = self.eval_result.get().cloned() {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
// required to keep the lua instance alive
|
||||
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 {
|
||||
expression: self.span(),
|
||||
})
|
||||
})
|
||||
})?;
|
||||
|
||||
self.eval_result.set(res.clone()).ok();
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
|
||||
|
|
|
@ -49,6 +49,9 @@ pub use variables::{Scope, VariableData};
|
|||
|
||||
pub mod util;
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
mod checks;
|
||||
|
||||
/// Data of a function.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct FunctionData {
|
||||
|
|
|
@ -943,111 +943,104 @@ impl Transpiler {
|
|||
) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
|
||||
match execute {
|
||||
ExecuteBlock::HeadTail(head, tail) => {
|
||||
let tail = match tail {
|
||||
ExecuteBlockTail::Block(block) => {
|
||||
let mut errors = Vec::new();
|
||||
let commands = block
|
||||
.statements()
|
||||
.iter()
|
||||
.flat_map(|s| {
|
||||
self.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))))
|
||||
if let ExecuteBlockHead::Conditional(cond) = head {
|
||||
let cond_eval = cond.condition().expression().comptime_eval(scope, handler);
|
||||
if cond_eval.is_err()
|
||||
|| cond_eval.is_ok_and(|val| !matches!(val, ComptimeValue::Boolean(_)))
|
||||
{
|
||||
match tail {
|
||||
ExecuteBlockTail::Block(block) => {
|
||||
block.check_no_comptime_assignments(scope, handler)?;
|
||||
}
|
||||
ExecuteBlockTail::ExecuteBlock(_, ex) => {
|
||||
ex.check_no_comptime_assignments(scope, handler)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) => {
|
||||
let statements = block.statements();
|
||||
let then = if statements.is_empty() {
|
||||
Some(Execute::Runs(Vec::new()))
|
||||
} else if statements.len() > 1 {
|
||||
let mut errors = Vec::new();
|
||||
let commands = statements
|
||||
.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)))
|
||||
}
|
||||
};
|
||||
let cond_eval = cond.condition().expression().comptime_eval(scope, handler);
|
||||
if cond_eval.is_err()
|
||||
|| cond_eval.is_ok_and(|val| !matches!(val, ComptimeValue::Boolean(_)))
|
||||
{
|
||||
block.check_no_comptime_assignments(scope, handler)?;
|
||||
el.block().check_no_comptime_assignments(scope, handler)?;
|
||||
}
|
||||
|
||||
then.map_or_else(
|
||||
|| Ok(None),
|
||||
|then| {
|
||||
self.transpile_conditional(
|
||||
cond,
|
||||
then,
|
||||
Some(el),
|
||||
self.transpile_conditional(
|
||||
cond,
|
||||
|transpiler, program_identifier, scope, handler| {
|
||||
let ex = transpiler.transpile_conditional_block(
|
||||
block,
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
)
|
||||
)?;
|
||||
Ok(Some((Vec::new(), BTreeMap::new(), ex)))
|
||||
},
|
||||
Some(el),
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transpile_conditional(
|
||||
fn transpile_conditional_block(
|
||||
&mut self,
|
||||
cond: &Conditional,
|
||||
then: Execute,
|
||||
el: Option<&Else>,
|
||||
block: &Block,
|
||||
program_identifier: &str,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
|
||||
let cond_expression = cond.condition().expression().as_ref();
|
||||
|
||||
let mut errors = Vec::new();
|
||||
|
||||
let el = el.and_then(|el| {
|
||||
let (_, block) = el.clone().dissolve();
|
||||
let statements = block.statements();
|
||||
let cmds = statements
|
||||
) -> TranspileResult<Execute> {
|
||||
let statements = block.statements();
|
||||
if statements.is_empty() {
|
||||
Ok(Execute::Runs(Vec::new()))
|
||||
} else if statements.len() > 1 {
|
||||
let mut errors = Vec::new();
|
||||
let commands = statements
|
||||
.iter()
|
||||
.flat_map(|statement| {
|
||||
self.transpile_statement(statement, program_identifier, scope, handler)
|
||||
|
@ -1056,118 +1049,226 @@ impl Transpiler {
|
|||
Vec::new()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
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 {
|
||||
.collect();
|
||||
if !errors.is_empty() {
|
||||
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) =
|
||||
self.transpile_expression_as_condition(cond_expression, scope, handler)?;
|
||||
if cmds.len() > 1 {
|
||||
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 {
|
||||
ExtendedCondition::Runtime(cond) => Ok(Some((
|
||||
pre_cond_cmds,
|
||||
prepare_variables,
|
||||
Execute::If(cond, Box::new(then), el.map(Box::new)),
|
||||
))),
|
||||
ExtendedCondition::Comptime(cond) => {
|
||||
if cond {
|
||||
Ok(Some((Vec::new(), prepare_variables, then)))
|
||||
} else {
|
||||
Ok(el.map(|el| (Vec::new(), prepare_variables, el)))
|
||||
}
|
||||
fn transpile_conditional<F, H>(
|
||||
&mut self,
|
||||
cond: &Conditional,
|
||||
compile_then: F,
|
||||
el: Option<&Else>,
|
||||
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)>>,
|
||||
{
|
||||
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 combine_execute_head_tail(
|
||||
fn transpile_else(
|
||||
&mut self,
|
||||
head: &ExecuteBlockHead,
|
||||
tail: Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>,
|
||||
el: &Else,
|
||||
program_identifier: &str,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
|
||||
Ok(match head {
|
||||
ExecuteBlockHead::Conditional(cond) => {
|
||||
if let Some((mut pre_cmds, prepare_variables, tail)) = tail {
|
||||
self.transpile_conditional(
|
||||
cond,
|
||||
tail,
|
||||
None,
|
||||
program_identifier,
|
||||
scope,
|
||||
handler,
|
||||
)?
|
||||
.map(|(pre_cond_cmds, mut prep_variables, cond)| {
|
||||
pre_cmds.extend(pre_cond_cmds);
|
||||
prep_variables.extend(prepare_variables);
|
||||
(pre_cmds, prep_variables, cond)
|
||||
) -> (Option<Execute>, Vec<TranspileError>) {
|
||||
let mut else_errors = Vec::new();
|
||||
|
||||
let block = el.block();
|
||||
let statements = block.statements();
|
||||
let cmds = statements
|
||||
.iter()
|
||||
.flat_map(|statement| {
|
||||
self.transpile_statement(statement, program_identifier, scope, handler)
|
||||
.unwrap_or_else(|err| {
|
||||
else_errors.push(err);
|
||||
Vec::new()
|
||||
})
|
||||
} 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) => {
|
||||
let selector = r#as
|
||||
.as_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = selector.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::As(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::As(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::At(at) => {
|
||||
let selector = at
|
||||
.at_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = selector.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::At(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::At(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Align(align) => {
|
||||
let align = align
|
||||
.align_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = align.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Align(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Align(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Anchored(anchored) => {
|
||||
let anchor =
|
||||
|
@ -1175,28 +1276,32 @@ impl Transpiler {
|
|||
.anchored_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = anchor.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Anchored(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Anchored(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::In(r#in) => {
|
||||
let dimension = r#in
|
||||
.in_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = dimension.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::In(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::In(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Positioned(positioned) => {
|
||||
let position =
|
||||
|
@ -1204,14 +1309,16 @@ impl Transpiler {
|
|||
.positioned_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = position.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Positioned(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Positioned(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Rotated(rotated) => {
|
||||
let rotation =
|
||||
|
@ -1219,14 +1326,16 @@ impl Transpiler {
|
|||
.rotated_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = rotation.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Rotated(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Rotated(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Facing(facing) => {
|
||||
let facing =
|
||||
|
@ -1234,56 +1343,64 @@ impl Transpiler {
|
|||
.facing_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = facing.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Facing(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Facing(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::AsAt(as_at) => {
|
||||
let selector = as_at
|
||||
.asat_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = selector.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::AsAt(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::AsAt(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::On(on) => {
|
||||
let dimension = on
|
||||
.on_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = dimension.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::On(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::On(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Store(store) => {
|
||||
let store = store
|
||||
.store_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = store.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Store(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Store(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
ExecuteBlockHead::Summon(summon) => {
|
||||
let entity =
|
||||
|
@ -1291,14 +1408,16 @@ impl Transpiler {
|
|||
.summon_selector()
|
||||
.to_macro_string(Some(self), scope, handler)?;
|
||||
let (macro_string, prepare_variables) = entity.into_sb();
|
||||
tail.map(|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Summon(macro_string, Box::new(tail)),
|
||||
)
|
||||
})
|
||||
compile_tail(self, program_identifier, scope, handler)?.map(
|
||||
|(pre_cmds, mut prep_variables, tail)| {
|
||||
prep_variables.extend(prepare_variables);
|
||||
(
|
||||
pre_cmds,
|
||||
prep_variables,
|
||||
Execute::Summon(macro_string, Box::new(tail)),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue