add handling for template strings everywhere

This commit is contained in:
Moritz Hölting 2025-09-02 22:47:37 +02:00
parent 6043a4add5
commit 1b85a2f654
8 changed files with 302 additions and 139 deletions

View File

@ -30,12 +30,12 @@ derive_more = { version = "2.0.1", default-features = false, features = [ "deref
enum-as-inner = "0.6.0"
getset = "0.1.2"
itertools = "0.14.0"
mlua = { version = "0.10.2", features = ["lua54", "vendored"], optional = true }
mlua = { version = "0.11.3", features = ["lua54", "vendored"], optional = true }
pathdiff = "0.2.3"
serde = { version = "1.0.217", features = ["derive"], optional = true }
serde_json = { version = "1.0.138", optional = true }
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "89709834da6f39840caa9c6a2eadbdececdc7d44", default-features = false, optional = true }
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "d4689c696a35328c041bcbbfd203abd5818c46d3", default-features = false, optional = true }
strsim = "0.11.1"
strum = { version = "0.27.0", features = ["derive"] }
thiserror = "2.0.11"

View File

@ -984,8 +984,11 @@ impl Parser<'_> {
&mut self,
handler: &impl Handler<base::Error>,
) -> ParseResult<AnyStringLiteral> {
match self.next_significant_token() {
Reading::Atomic(Token::StringLiteral(literal)) => Ok(literal.into()),
match self.stop_at_significant() {
Reading::Atomic(Token::StringLiteral(literal)) => {
self.forward();
Ok(literal.into())
}
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '`' => self
.parse_template_string_literal(handler)
.map(AnyStringLiteral::TemplateStringLiteral),

View File

@ -812,7 +812,7 @@ impl Parser<'_> {
}
Reading::Atomic(Token::Keyword(keyword)) if keyword.keyword.starts_execute_block() => {
// eat the as keyword
// eat the keyword
self.forward();
let argument = match self.stop_at_significant() {

View File

@ -17,7 +17,7 @@ use crate::{
use super::util::{MacroString, MacroStringPart};
type ShulkerboxMacroStringMap = BTreeMap<String, (DataLocation, Vec<Command>, Span)>;
pub(crate) type ShulkerboxMacroStringMap = BTreeMap<String, (DataLocation, Vec<Command>, Span)>;
impl MacroString {
pub fn into_sb(self) -> (ExtMacroString, ShulkerboxMacroStringMap) {

View File

@ -2,6 +2,9 @@
use std::fmt::Display;
#[cfg(feature = "shulkerbox")]
use std::collections::BTreeMap;
#[cfg(feature = "shulkerbox")]
use enum_as_inner::EnumAsInner;
@ -25,6 +28,7 @@ use crate::{
Primary,
},
transpile::{
conversions::ShulkerboxMacroStringMap,
error::{FunctionArgumentsNotAllowed, MissingValue},
variables::FunctionVariableDataType,
TranspileError,
@ -217,10 +221,9 @@ impl StorageType {
pub fn suffix(&self) -> &'static str {
match self {
Self::Boolean | Self::Byte => "b",
Self::Int => "",
Self::Int | Self::String => "",
Self::Long => "l",
Self::Double => "d",
Self::String => "",
}
}
@ -1291,14 +1294,14 @@ impl Transpiler {
..
} | DataLocation::Tag { .. }
) {
let (mut cmds, cond) =
let (mut cmds, prepare_variables, cond) =
self.transpile_primary_expression_as_condition(primary, scope, handler)?;
let store_cmds =
self.store_condition_success(cond, target, primary, handler)?;
cmds.extend(store_cmds);
Ok(cmds)
self.transpile_commands_with_variable_macros(cmds, prepare_variables, handler)
} else {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: target.value_type().into(),
@ -1375,14 +1378,14 @@ impl Transpiler {
}
},
PrefixOperator::LogicalNot(_) => {
let (mut cmds, cond) =
let (mut cmds, prepare_variables, cond) =
self.transpile_primary_expression_as_condition(primary, scope, handler)?;
let store_cmds =
self.store_condition_success(cond, target, primary, handler)?;
cmds.extend(store_cmds);
Ok(cmds)
self.transpile_commands_with_variable_macros(cmds, prepare_variables, handler)
}
PrefixOperator::Run(_) => {
let run_cmds =
@ -1646,13 +1649,13 @@ impl Transpiler {
| BinaryOperator::NotEqual(..)
| BinaryOperator::LogicalAnd(..)
| BinaryOperator::LogicalOr(..) => {
let (mut cmds, cond) =
let (mut cmds, prepare_variables, cond) =
self.transpile_binary_expression_as_condition(binary, scope, handler)?;
let store_cmds = self.store_condition_success(cond, target, binary, handler)?;
cmds.extend(store_cmds);
Ok(cmds)
self.transpile_commands_with_variable_macros(cmds, prepare_variables, handler)
}
}
}
@ -1663,7 +1666,7 @@ impl Transpiler {
expression: &Expression,
scope: &Arc<super::Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<(Vec<Command>, ExtendedCondition)> {
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, ExtendedCondition)> {
match expression {
Expression::Primary(primary) => {
self.transpile_primary_expression_as_condition(primary, scope, handler)
@ -1680,11 +1683,15 @@ impl Transpiler {
primary: &Primary,
scope: &Arc<super::Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<(Vec<Command>, ExtendedCondition)> {
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, ExtendedCondition)> {
use std::collections::BTreeMap;
match primary {
Primary::Boolean(boolean) => {
Ok((Vec::new(), ExtendedCondition::Comptime(boolean.value())))
}
Primary::Boolean(boolean) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Comptime(boolean.value()),
)),
Primary::Integer(_) => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ExpectedType::Boolean,
@ -1695,6 +1702,7 @@ impl Transpiler {
}
Primary::StringLiteral(s) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(s.str_content().to_string().into())),
)),
Primary::TemplateStringLiteral(template_string) => {
@ -1703,6 +1711,7 @@ impl Transpiler {
.into_sb();
Ok((
Vec::new(),
prepare_variables,
ExtendedCondition::Runtime(Condition::Atom(macro_string)),
))
}
@ -1730,6 +1739,7 @@ impl Transpiler {
Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(
format!("function {func_location}").into(),
)),
@ -1742,12 +1752,14 @@ impl Transpiler {
match variable {
VariableData::BooleanStorage { storage_name, path } => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(
format!("data storage {storage_name} {{{path}: 1b}}").into(),
)),
)),
VariableData::MacroParameter { macro_name, .. } => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(
shulkerbox::util::MacroString::MacroString(vec![
shulkerbox::util::MacroStringPart::MacroUsage(
@ -1806,6 +1818,7 @@ impl Transpiler {
{
Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(
format!("data storage {storage_name} {{{path}: 1b}}")
.into(),
@ -1869,7 +1882,7 @@ impl Transpiler {
},
|value| match value {
ComptimeValue::Boolean(b) => {
Ok((Vec::new(), ExtendedCondition::Comptime(b)))
Ok((Vec::new(), BTreeMap::new(), ExtendedCondition::Comptime(b)))
}
ComptimeValue::Integer(_) => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
@ -1881,6 +1894,7 @@ impl Transpiler {
}
ComptimeValue::String(s) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(
shulkerbox::util::MacroString::String(s),
)),
@ -1889,6 +1903,7 @@ impl Transpiler {
let (macro_string, prepare_variables) = s.into_sb();
Ok((
Vec::new(),
prepare_variables,
ExtendedCondition::Runtime(Condition::Atom(macro_string)),
))
}
@ -1896,13 +1911,15 @@ impl Transpiler {
),
Primary::Prefix(prefix) => match prefix.operator() {
PrefixOperator::LogicalNot(_) => {
let (cmds, cond) = self.transpile_primary_expression_as_condition(
prefix.operand(),
scope,
handler,
)?;
let (cmds, prepare_variables, cond) = self
.transpile_primary_expression_as_condition(
prefix.operand(),
scope,
handler,
)?;
Ok((
cmds,
prepare_variables,
match cond {
ExtendedCondition::Runtime(cond) => {
ExtendedCondition::Runtime(Condition::Not(Box::new(cond)))
@ -1928,7 +1945,7 @@ impl Transpiler {
scope,
handler,
)?;
Ok((store_cmds, cond))
Ok((store_cmds, BTreeMap::new(), cond))
}
PrefixOperator::Negate(_) => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
@ -1942,18 +1959,22 @@ impl Transpiler {
Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
Ok(ComptimeValue::String(value)) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Runtime(Condition::Atom(value.into())),
)),
Ok(ComptimeValue::MacroString(value)) => {
let (macro_string, prepare_variables) = value.into_sb();
Ok((
Vec::new(),
prepare_variables,
ExtendedCondition::Runtime(Condition::Atom(macro_string)),
))
}
Ok(ComptimeValue::Boolean(boolean)) => {
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
}
Ok(ComptimeValue::Boolean(boolean)) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Comptime(boolean),
)),
_ => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ExpectedType::Boolean,
@ -1971,7 +1992,7 @@ impl Transpiler {
binary: &Binary,
scope: &Arc<super::Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<(Vec<Command>, ExtendedCondition)> {
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, ExtendedCondition)> {
match binary.operator() {
BinaryOperator::Equal(..)
| BinaryOperator::NotEqual(..)
@ -1980,7 +2001,9 @@ impl Transpiler {
| BinaryOperator::LessThan(_)
| BinaryOperator::LessThanOrEqual(..) => self
.transpile_comparison_operator(binary, scope, handler)
.map(|(cmds, cond)| (cmds, ExtendedCondition::Runtime(cond))),
.map(|(cmds, prepare_variables, cond)| {
(cmds, prepare_variables, ExtendedCondition::Runtime(cond))
}),
BinaryOperator::LogicalAnd(..) | BinaryOperator::LogicalOr(..) => {
self.transpile_logic_operator(binary, scope, handler)
}
@ -2132,7 +2155,7 @@ impl Transpiler {
binary: &Binary,
scope: &Arc<super::Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<(Vec<Command>, Condition)> {
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, Condition)> {
let invert = matches!(binary.operator(), BinaryOperator::NotEqual(..));
// TODO: evaluate comptime values and compare using `matches` and integer ranges
@ -2177,6 +2200,7 @@ impl Transpiler {
Ok((
left_cmds.into_iter().chain(right_cmds).collect(),
BTreeMap::new(),
if invert {
Condition::Not(Box::new(condition))
} else {
@ -2190,38 +2214,45 @@ impl Transpiler {
binary: &Binary,
scope: &Arc<super::Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<(Vec<Command>, ExtendedCondition)> {
) -> TranspileResult<(Vec<Command>, ShulkerboxMacroStringMap, ExtendedCondition)> {
let left = binary.left_operand().as_ref();
let right = binary.right_operand().as_ref();
let (left_cmds, left_cond) =
let (left_cmds, mut left_prep_variables, left_cond) =
self.transpile_expression_as_condition(left, scope, handler)?;
let (right_cmds, right_cond) =
let (right_cmds, right_prep_variables, right_cond) =
self.transpile_expression_as_condition(right, scope, handler)?;
left_prep_variables.extend(right_prep_variables);
let prep_variables = left_prep_variables;
match (binary.operator(), left_cond, right_cond) {
(BinaryOperator::LogicalAnd(..), ExtendedCondition::Comptime(true), other)
| (BinaryOperator::LogicalOr(..), ExtendedCondition::Comptime(false), other) => {
Ok((right_cmds, other))
Ok((right_cmds, prep_variables, other))
}
(BinaryOperator::LogicalAnd(..), other, ExtendedCondition::Comptime(true))
| (BinaryOperator::LogicalOr(..), other, ExtendedCondition::Comptime(false)) => {
Ok((left_cmds, other))
Ok((left_cmds, prep_variables, other))
}
(BinaryOperator::LogicalAnd(..), ExtendedCondition::Comptime(false), _)
| (BinaryOperator::LogicalAnd(..), _, ExtendedCondition::Comptime(false)) => {
Ok((Vec::new(), ExtendedCondition::Comptime(false)))
}
| (BinaryOperator::LogicalAnd(..), _, ExtendedCondition::Comptime(false)) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Comptime(false),
)),
(BinaryOperator::LogicalOr(..), ExtendedCondition::Comptime(true), _)
| (BinaryOperator::LogicalOr(..), _, ExtendedCondition::Comptime(true)) => {
Ok((Vec::new(), ExtendedCondition::Comptime(true)))
}
| (BinaryOperator::LogicalOr(..), _, ExtendedCondition::Comptime(true)) => Ok((
Vec::new(),
BTreeMap::new(),
ExtendedCondition::Comptime(true),
)),
(
BinaryOperator::LogicalAnd(..),
ExtendedCondition::Runtime(left_cond),
ExtendedCondition::Runtime(right_cond),
) => Ok((
left_cmds.into_iter().chain(right_cmds).collect(),
prep_variables,
ExtendedCondition::Runtime(Condition::And(
Box::new(left_cond),
Box::new(right_cond),
@ -2233,6 +2264,7 @@ impl Transpiler {
ExtendedCondition::Runtime(right_cond),
) => Ok((
left_cmds.into_iter().chain(right_cmds).collect(),
prep_variables,
ExtendedCondition::Runtime(Condition::Or(
Box::new(left_cond),
Box::new(right_cond),
@ -2335,11 +2367,17 @@ impl Transpiler {
}
| DataLocation::Tag { .. } => {
let (macro_string, prepare_variables) = value.clone().into_sb();
self.store_condition_success(
let cmds = self.store_condition_success(
ExtendedCondition::Runtime(Condition::Atom(macro_string)),
target,
source,
handler,
)?;
self.transpile_commands_with_variable_macros(
cmds,
prepare_variables,
handler,
)
}
// DataLocation::Storage { storage_name, path, r#type: StorageType::String } => todo!("implement storage string")

View File

@ -682,8 +682,8 @@ impl Transpiler {
return Err(err);
}
}
Err((parts, preparation_variables)) => {
let (macro_string, preparation_variables) = MacroString::MacroString {
Err((parts, prepare_variables)) => {
let (macro_string, prepare_variables) = MacroString::MacroString {
parts: std::iter::once(MacroStringPart::String(
format!(
"scoreboard players set {target} {objective} "
@ -691,10 +691,16 @@ impl Transpiler {
))
.chain(parts.iter().cloned())
.collect(),
prepare_variables: preparation_variables.to_owned(),
prepare_variables: prepare_variables.to_owned(),
}
.into_sb();
move_cmds.push(Command::UsesMacro(macro_string));
let cmds = self
.transpile_commands_with_variable_macros(
vec![Command::UsesMacro(macro_string)],
prepare_variables,
handler,
)?;
move_cmds.extend(cmds);
}
},
Parameter::Storage {
@ -742,15 +748,21 @@ impl Transpiler {
return Err(err);
}
}
Err((parts, preparation_cmds)) => {
let (macro_string, preparation_variables) = MacroString::MacroString {
Err((parts, prepare_cmds)) => {
let (macro_string, prepare_variables) = MacroString::MacroString {
parts: std::iter::once(MacroStringPart::String(format!("data modify storage {target_storage_name} {target_path} set value ")))
.chain(parts.iter().cloned())
.collect(),
prepare_variables: preparation_cmds.to_owned(),
prepare_variables: prepare_cmds.to_owned(),
}
.into_sb();
move_cmds.push(Command::UsesMacro(macro_string));
let cmds = self
.transpile_commands_with_variable_macros(
vec![Command::UsesMacro(macro_string)],
prepare_variables,
handler,
)?;
move_cmds.extend(cmds);
}
},
Parameter::Storage {
@ -808,10 +820,10 @@ impl Transpiler {
}),
);
let storage_suffix = function_location.replace(['/', ':'], "_");
let statics_cmd = match joined_statics {
MacroString::String(s) => Command::Raw(format!(
let statics_cmds = match joined_statics {
MacroString::String(s) => vec![Command::Raw(format!(
r"data merge storage shulkerscript:function_arguments_{storage_suffix} {{{s}}}"
)),
))],
MacroString::MacroString { .. } => {
let prefix = MacroString::String(format!(
"data merge storage shulkerscript:function_arguments_{storage_suffix} {{"
@ -823,10 +835,14 @@ impl Transpiler {
MacroString::String("}".to_string()),
])
.into_sb();
Command::UsesMacro(macro_string)
self.transpile_commands_with_variable_macros(
vec![Command::UsesMacro(macro_string)],
prepare_variables,
handler,
)?
}
};
setup_cmds.push(statics_cmd);
setup_cmds.extend(statics_cmds);
setup_cmds.extend(move_cmds);
Ok(TranspiledFunctionArguments::Dynamic(setup_cmds))

View File

@ -589,8 +589,7 @@ fn print_function(
let cmd = format!("tellraw {target} {print_args}");
let cmd = if contains_macro {
let (macro_string, prepare_variables) =
cmd.parse::<MacroString>().expect("cannot fail").into_sb();
let (macro_string, _) = cmd.parse::<MacroString>().expect("cannot fail").into_sb();
Command::UsesMacro(macro_string)
} else {
Command::Raw(cmd)

View File

@ -27,6 +27,7 @@ use crate::{
AnnotationAssignment,
},
transpile::{
conversions::ShulkerboxMacroStringMap,
error::IllegalAnnotationContent,
expression::DataLocation,
util::{MacroString, MacroStringPart},
@ -471,9 +472,18 @@ impl Transpiler {
.comptime_eval(scope, handler)
.map(|val| val.to_macro_string());
let (prepare_cmds, ret_cmd) = if let Ok(val) = comptime_val {
let (prepare_cmds, ret_cmds) = if let Ok(val) = comptime_val {
let (macro_string, prepare_variables) = val.into_sb();
(Vec::new(), datapack::ReturnCommand::Value(macro_string))
let cmds = self.transpile_commands_with_variable_macros(
vec![Command::Return(datapack::ReturnCommand::Value(
macro_string,
))],
prepare_variables,
handler,
)?;
(Vec::new(), cmds)
} else {
match ret.expression() {
Expression::Primary(Primary::Prefix(prefix))
@ -481,12 +491,25 @@ impl Transpiler {
{
let ret_cmds =
self.transpile_run_expression(prefix.operand(), scope, handler)?;
let cmd = if ret_cmds.len() == 1 {
ret_cmds.into_iter().next().unwrap()
} else {
Command::Group(Group::new(ret_cmds))
let cmd = match ret_cmds.last() {
_ if ret_cmds.len() == 1 => ret_cmds.into_iter().next().unwrap(),
Some(Command::Group(group)) if group.block_pass_macros().is_some() => {
let block_macros =
group.block_pass_macros().expect("checked above").clone();
Command::Group(
Group::new(ret_cmds).with_block_pass_macros(block_macros),
)
}
_ => Command::Group(Group::new(ret_cmds)),
};
(Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
(
Vec::new(),
vec![Command::Return(datapack::ReturnCommand::Command(Box::new(
cmd,
)))],
)
}
Expression::Primary(Primary::FunctionCall(func)) => {
let ret_cmds = self.transpile_function_call(func, scope, handler)?;
@ -495,16 +518,21 @@ impl Transpiler {
} else {
Command::Group(Group::new(ret_cmds))
};
(Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd)))
(
Vec::new(),
vec![Command::Return(datapack::ReturnCommand::Command(Box::new(
cmd,
)))],
)
}
Expression::Primary(Primary::Identifier(ident)) => {
if let Some(var) = scope.get_variable(ident.span.str()) {
match var.as_ref() {
VariableData::BooleanStorage { storage_name, path } => (
Vec::new(),
datapack::ReturnCommand::Command(Box::new(Command::Raw(format!(
"data get storage {storage_name} {path}"
)))),
vec![Command::Return(datapack::ReturnCommand::Command(Box::new(
Command::Raw(format!("data get storage {storage_name} {path}")),
)))],
),
VariableData::ComptimeValue {
value,
@ -518,18 +546,25 @@ impl Transpiler {
Err(err)
},
|val| {
let cmd = val.to_string_no_macro().map_or_else(
|| {
let (macro_string, prepare_variables) =
val.to_macro_string().into_sb();
Command::UsesMacro(macro_string)
},
Command::Raw,
);
Ok((
Vec::new(),
datapack::ReturnCommand::Command(Box::new(cmd)),
))
let cmds = if let Some(s) = val.to_string_no_macro() {
vec![Command::Return(datapack::ReturnCommand::Command(
Box::new(Command::Raw(s)),
))]
} else {
let (macro_string, prepare_variables) =
val.to_macro_string().into_sb();
self.transpile_commands_with_variable_macros(
vec![Command::Return(
datapack::ReturnCommand::Command(Box::new(
Command::UsesMacro(macro_string),
)),
)],
prepare_variables,
handler,
)?
};
Ok((Vec::new(), cmds))
},
)?,
VariableData::MacroParameter {
@ -537,19 +572,21 @@ impl Transpiler {
macro_name,
} => (
Vec::new(),
datapack::ReturnCommand::Command(Box::new(Command::UsesMacro(
shulkerbox::util::MacroString::MacroString(vec![
shulkerbox::util::MacroStringPart::MacroUsage(
vec![Command::Return(datapack::ReturnCommand::Command(Box::new(
Command::UsesMacro(shulkerbox::util::MacroString::MacroString(
vec![shulkerbox::util::MacroStringPart::MacroUsage(
macro_name.clone(),
),
]),
))),
)],
)),
)))],
),
VariableData::ScoreboardValue { objective, target } => (
Vec::new(),
datapack::ReturnCommand::Command(Box::new(Command::Raw(format!(
"scoreboard players get {target} {objective}"
)))),
vec![Command::Return(datapack::ReturnCommand::Command(Box::new(
Command::Raw(format!(
"scoreboard players get {target} {objective}"
)),
)))],
),
_ => {
let err = TranspileError::UnexpectedExpression(
@ -585,15 +622,12 @@ impl Transpiler {
scope,
handler,
)?;
(cmds, ret_cmd)
(cmds, vec![Command::Return(ret_cmd)])
}
}
};
let cmds = prepare_cmds
.into_iter()
.chain(std::iter::once(Command::Return(ret_cmd)))
.collect();
let cmds = prepare_cmds.into_iter().chain(ret_cmds).collect();
Ok(cmds)
}
@ -849,14 +883,15 @@ impl Transpiler {
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Vec<Command>> {
self.transpile_execute_block_internal(execute, program_identifier, scope, handler)
.map(|ex| {
ex.map(|(mut pre_cmds, exec)| {
pre_cmds.push(exec.into());
pre_cmds
})
.unwrap_or_default()
})
if let Some((mut pre_cmds, prepare_variables, exec)) =
self.transpile_execute_block_internal(execute, program_identifier, scope, handler)?
{
pre_cmds.push(exec.into());
self.transpile_commands_with_variable_macros(pre_cmds, prepare_variables, handler)
} else {
Ok(Vec::new())
}
}
fn transpile_execute_block_internal(
@ -865,7 +900,7 @@ impl Transpiler {
program_identifier: &str,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<(Vec<Command>, Execute)>> {
) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
match execute {
ExecuteBlock::HeadTail(head, tail) => {
let tail = match tail {
@ -889,7 +924,7 @@ impl Transpiler {
if commands.is_empty() {
Ok(None)
} else {
Ok(Some((Vec::new(), Execute::Runs(commands))))
Ok(Some((Vec::new(), BTreeMap::new(), Execute::Runs(commands))))
}
}
ExecuteBlockTail::ExecuteBlock(_, execute_block) => self
@ -964,7 +999,7 @@ impl Transpiler {
program_identifier: &str,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<(Vec<Command>, Execute)>> {
) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
let cond_expression = cond.condition().expression().as_ref();
let mut errors = Vec::new();
@ -994,28 +1029,29 @@ impl Transpiler {
if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
if value {
Ok(Some((Vec::new(), then)))
Ok(Some((Vec::new(), BTreeMap::new(), then)))
} else {
Ok(el.map(|el| (Vec::new(), el)))
Ok(el.map(|el| (Vec::new(), BTreeMap::new(), el)))
}
} else {
if !errors.is_empty() {
return Err(errors.remove(0));
}
let (pre_cond_cmds, cond) =
let (pre_cond_cmds, prepare_variables, cond) =
self.transpile_expression_as_condition(cond_expression, scope, handler)?;
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(), then)))
Ok(Some((Vec::new(), prepare_variables, then)))
} else {
Ok(el.map(|el| (Vec::new(), el)))
Ok(el.map(|el| (Vec::new(), prepare_variables, el)))
}
}
}
@ -1026,14 +1062,14 @@ impl Transpiler {
fn combine_execute_head_tail(
&mut self,
head: &ExecuteBlockHead,
tail: Option<(Vec<Command>, Execute)>,
tail: Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>,
program_identifier: &str,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<(Vec<Command>, Execute)>> {
) -> TranspileResult<Option<(Vec<Command>, ShulkerboxMacroStringMap, Execute)>> {
Ok(match head {
ExecuteBlockHead::Conditional(cond) => {
if let Some((mut pre_cmds, tail)) = tail {
if let Some((mut pre_cmds, prepare_variables, tail)) = tail {
self.transpile_conditional(
cond,
tail,
@ -1042,9 +1078,10 @@ impl Transpiler {
scope,
handler,
)?
.map(|(pre_cond_cmds, cond)| {
.map(|(pre_cond_cmds, mut prep_variables, cond)| {
pre_cmds.extend(pre_cond_cmds);
(pre_cmds, cond)
prep_variables.extend(prepare_variables);
(pre_cmds, prep_variables, cond)
})
} else {
None
@ -1055,22 +1092,41 @@ impl Transpiler {
.as_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = selector.into_sb();
tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::As(macro_string, Box::new(tail))))
tail.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, tail)| (pre_cmds, Execute::At(macro_string, Box::new(tail))))
tail.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, tail)| {
(pre_cmds, Execute::Align(macro_string, Box::new(tail)))
tail.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) => {
@ -1079,8 +1135,13 @@ impl Transpiler {
.anchored_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = anchor.into_sb();
tail.map(|(pre_cmds, tail)| {
(pre_cmds, Execute::Anchored(macro_string, Box::new(tail)))
tail.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) => {
@ -1088,7 +1149,14 @@ impl Transpiler {
.in_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = dimension.into_sb();
tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::In(macro_string, Box::new(tail))))
tail.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 =
@ -1096,8 +1164,13 @@ impl Transpiler {
.positioned_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = position.into_sb();
tail.map(|(pre_cmds, tail)| {
(pre_cmds, Execute::Positioned(macro_string, Box::new(tail)))
tail.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) => {
@ -1106,8 +1179,13 @@ impl Transpiler {
.rotated_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = rotation.into_sb();
tail.map(|(pre_cmds, tail)| {
(pre_cmds, Execute::Rotated(macro_string, Box::new(tail)))
tail.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) => {
@ -1116,8 +1194,13 @@ impl Transpiler {
.facing_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = facing.into_sb();
tail.map(|(pre_cmds, tail)| {
(pre_cmds, Execute::Facing(macro_string, Box::new(tail)))
tail.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) => {
@ -1125,22 +1208,41 @@ impl Transpiler {
.asat_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = selector.into_sb();
tail.map(|(pre_cmds, tail)| (pre_cmds, Execute::AsAt(macro_string, Box::new(tail))))
tail.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, tail)| (pre_cmds, Execute::On(macro_string, Box::new(tail))))
tail.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, tail)| {
(pre_cmds, Execute::Store(macro_string, Box::new(tail)))
tail.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) => {
@ -1149,8 +1251,13 @@ impl Transpiler {
.summon_selector()
.to_macro_string(Some(self), scope, handler)?;
let (macro_string, prepare_variables) = entity.into_sb();
tail.map(|(pre_cmds, tail)| {
(pre_cmds, Execute::Summon(macro_string, Box::new(tail)))
tail.map(|(pre_cmds, mut prep_variables, tail)| {
prep_variables.extend(prepare_variables);
(
pre_cmds,
prep_variables,
Execute::Summon(macro_string, Box::new(tail)),
)
})
}
})
@ -1197,9 +1304,9 @@ impl Transpiler {
} else {
prepare_cmds.push(Command::Group(
Group::new(cmds)
.always_create_function(true)
.block_pass_macros(macro_names)
.data_storage_name(storage_name),
.with_always_create_function(true)
.with_block_pass_macros(macro_names)
.with_data_storage_name(storage_name),
));
Ok(prepare_cmds)