add macro string to execute command
This commit is contained in:
parent
72f98849ea
commit
897e85c2d7
|
@ -1,9 +1,15 @@
|
|||
use chksum_md5 as md5;
|
||||
use std::ops::{BitAnd, BitOr, Not};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ops::{BitAnd, BitOr, Not},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
prelude::Command,
|
||||
util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
|
||||
util::{
|
||||
compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
|
||||
MacroString,
|
||||
},
|
||||
};
|
||||
|
||||
use super::Execute;
|
||||
|
@ -80,10 +86,18 @@ fn compile_pre_20_format(
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
Command::Group(group_cmd)
|
||||
let group = Command::Group(group_cmd);
|
||||
let allows_prefix = !group.forbid_prefix();
|
||||
group
|
||||
.compile(options, global_state, function_state)
|
||||
.iter()
|
||||
.map(|s| (true, "run ".to_string() + s))
|
||||
.map(|s| {
|
||||
if allows_prefix {
|
||||
(true, "run ".to_string() + s)
|
||||
} else {
|
||||
(false, s.clone())
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
then.compile_internal(String::new(), false, options, global_state, function_state)
|
||||
|
@ -111,11 +125,10 @@ fn compile_pre_20_format(
|
|||
tracing::error!("No success_uid found for each_or_cmd, using default");
|
||||
"if_success"
|
||||
});
|
||||
Condition::Atom(format!("data storage shulkerbox:cond {{{success_uid}:1b}}")).compile(
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
)
|
||||
Condition::Atom(MacroString::from(format!(
|
||||
"data storage shulkerbox:cond {{{success_uid}:1b}}"
|
||||
)))
|
||||
.compile(options, global_state, function_state)
|
||||
} else {
|
||||
str_cond
|
||||
};
|
||||
|
@ -128,9 +141,10 @@ fn compile_pre_20_format(
|
|||
tracing::error!("No success_uid found for each_or_cmd, using default");
|
||||
"if_success"
|
||||
});
|
||||
let else_cond =
|
||||
(!Condition::Atom(format!("data storage shulkerbox:cond {{{success_uid}:1b}}")))
|
||||
.compile(options, global_state, function_state);
|
||||
let else_cond = (!Condition::Atom(MacroString::from(format!(
|
||||
"data storage shulkerbox:cond {{{success_uid}:1b}}"
|
||||
))))
|
||||
.compile(options, global_state, function_state);
|
||||
let el = el.compile_internal(
|
||||
String::new(),
|
||||
else_cond.len() > 1,
|
||||
|
@ -186,7 +200,9 @@ fn compile_since_20_format(
|
|||
) -> Vec<(bool, String)> {
|
||||
let then_count = then.get_count(options);
|
||||
|
||||
let str_cond = cond.clone().compile(options, global_state, function_state);
|
||||
let str_cond = cond
|
||||
.clone()
|
||||
.compile_keep_macros(options, global_state, function_state);
|
||||
|
||||
// if the conditions have multiple parts joined by a disjunction or an else part, commands need to be grouped
|
||||
if el.is_some() || str_cond.len() > 1 {
|
||||
|
@ -199,10 +215,12 @@ fn compile_since_20_format(
|
|||
global_state,
|
||||
function_state,
|
||||
);
|
||||
Command::Group(group_cmds)
|
||||
let group = Command::Group(group_cmds);
|
||||
let allows_prefix = !group.forbid_prefix();
|
||||
group
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|s| (true, s))
|
||||
.map(|s| (allows_prefix, s))
|
||||
.collect()
|
||||
} else if then_count > 1 {
|
||||
let then_cmd = match then.clone() {
|
||||
|
@ -210,36 +228,48 @@ fn compile_since_20_format(
|
|||
Execute::Runs(cmds) => cmds,
|
||||
ex => vec![Command::Execute(ex)],
|
||||
};
|
||||
let then_cmd_str = Command::Group(then_cmd)
|
||||
.compile(options, global_state, function_state)
|
||||
let group_cmd = Command::Group(then_cmd);
|
||||
let then_cmd = if group_cmd.forbid_prefix() {
|
||||
group_cmd
|
||||
} else {
|
||||
Command::Concat(
|
||||
Box::new(Command::Raw("run ".to_string())),
|
||||
Box::new(group_cmd),
|
||||
)
|
||||
};
|
||||
combine_conditions_commands_concat(str_cond, &then_cmd)
|
||||
.into_iter()
|
||||
.map(|s| (true, format!("run {s}")))
|
||||
.collect::<Vec<_>>();
|
||||
combine_conditions_commands(str_cond, &then_cmd_str)
|
||||
.into_iter()
|
||||
.map(|(use_prefix, cmd)| {
|
||||
let cmd = if use_prefix {
|
||||
prefix.to_string() + &cmd
|
||||
} else {
|
||||
cmd
|
||||
};
|
||||
(use_prefix, cmd)
|
||||
.map(|cmd| {
|
||||
(
|
||||
cmd.forbid_prefix(),
|
||||
cmd.compile(options, global_state, function_state),
|
||||
)
|
||||
})
|
||||
.flat_map(|(forbid_prefix, cmds)| {
|
||||
cmds.into_iter()
|
||||
.map(move |cmd| (!forbid_prefix, prefix.to_string() + &cmd))
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
let str_cmd =
|
||||
then.compile_internal(String::new(), false, options, global_state, function_state);
|
||||
combine_conditions_commands(str_cond, &str_cmd)
|
||||
.into_iter()
|
||||
.map(|(use_prefix, cmd)| {
|
||||
let cmd = if use_prefix {
|
||||
prefix.to_string() + &cmd
|
||||
} else {
|
||||
cmd
|
||||
};
|
||||
(use_prefix, cmd)
|
||||
})
|
||||
.collect()
|
||||
combine_conditions_commands_concat(
|
||||
str_cond,
|
||||
&Command::Concat(
|
||||
Box::new(Command::Raw("run ".to_string())),
|
||||
Box::new(Command::Execute(then.clone())),
|
||||
),
|
||||
)
|
||||
.into_iter()
|
||||
.map(|cmd| {
|
||||
(
|
||||
cmd.forbid_prefix(),
|
||||
cmd.compile(options, global_state, function_state),
|
||||
)
|
||||
})
|
||||
.flat_map(|(forbid_prefix, cmds)| {
|
||||
cmds.into_iter()
|
||||
.map(move |cmd| (!forbid_prefix, prefix.to_string() + &cmd))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,8 +293,29 @@ fn combine_conditions_commands(
|
|||
.collect()
|
||||
}
|
||||
|
||||
fn combine_conditions_commands_concat(
|
||||
conditions: Vec<MacroString>,
|
||||
command: &Command,
|
||||
) -> Vec<Command> {
|
||||
if command.forbid_prefix() {
|
||||
vec![command.clone()]
|
||||
} else {
|
||||
conditions
|
||||
.into_iter()
|
||||
.map(|cond| {
|
||||
let prefix = if cond.contains_macro() {
|
||||
Command::UsesMacro(cond + " ")
|
||||
} else {
|
||||
Command::Raw(cond.compile() + " ")
|
||||
};
|
||||
Command::Concat(Box::new(prefix), Box::new(command.clone()))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_return_group_case_since_20(
|
||||
str_cond: Vec<String>,
|
||||
str_cond: Vec<MacroString>,
|
||||
then: &Execute,
|
||||
el: Option<&Execute>,
|
||||
prefix: &str,
|
||||
|
@ -278,15 +329,28 @@ fn handle_return_group_case_since_20(
|
|||
Execute::Runs(cmds) => cmds,
|
||||
ex => vec![Command::Execute(ex)],
|
||||
};
|
||||
let then_cmd_str = Command::Group(then_cmd)
|
||||
.compile(options, global_state, function_state)
|
||||
let group = Command::Group(then_cmd);
|
||||
let then_cmd_concat = if group.forbid_prefix() {
|
||||
group
|
||||
} else {
|
||||
Command::Concat(
|
||||
Box::new(Command::Raw("run return run ".to_string())),
|
||||
Box::new(group),
|
||||
)
|
||||
};
|
||||
let then_cond_concat = combine_conditions_commands_concat(str_cond, &then_cmd_concat);
|
||||
let mut group_cmds = then_cond_concat
|
||||
.into_iter()
|
||||
.map(|s| (true, format!("run return run {s}")))
|
||||
.collect::<Vec<_>>();
|
||||
let then_cond_str = combine_conditions_commands(str_cond, &then_cmd_str);
|
||||
let mut group_cmds = then_cond_str
|
||||
.into_iter()
|
||||
.map(|(_, cmd)| Command::Raw(format!("execute {cmd}")))
|
||||
.map(|cmd| {
|
||||
if cmd.forbid_prefix() {
|
||||
cmd
|
||||
} else {
|
||||
Command::Concat(
|
||||
Box::new(Command::Raw("execute ".to_string())),
|
||||
Box::new(cmd),
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if let Some(el) = el {
|
||||
handle_else_since_20(
|
||||
|
@ -311,7 +375,7 @@ fn handle_else_since_20(
|
|||
) {
|
||||
let el_cmd = match el {
|
||||
Execute::If(cond, then, el) => handle_return_group_case_since_20(
|
||||
cond.compile(options, global_state, function_state),
|
||||
cond.compile_keep_macros(options, global_state, function_state),
|
||||
&then,
|
||||
el.as_deref(),
|
||||
prefix,
|
||||
|
@ -321,7 +385,7 @@ fn handle_else_since_20(
|
|||
),
|
||||
Execute::Run(cmd) => match *cmd {
|
||||
Command::Execute(Execute::If(cond, then, el)) => handle_return_group_case_since_20(
|
||||
cond.compile(options, global_state, function_state),
|
||||
cond.compile_keep_macros(options, global_state, function_state),
|
||||
&then,
|
||||
el.as_deref(),
|
||||
prefix,
|
||||
|
@ -343,7 +407,7 @@ fn handle_else_since_20(
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Condition {
|
||||
Atom(String),
|
||||
Atom(MacroString),
|
||||
Not(Box<Condition>),
|
||||
And(Box<Condition>, Box<Condition>),
|
||||
Or(Box<Condition>, Box<Condition>),
|
||||
|
@ -392,21 +456,21 @@ impl Condition {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert the condition into a string.
|
||||
/// Convert the condition into a [`MacroString`].
|
||||
///
|
||||
/// Will fail if the condition contains an `Or` variant. Use `compile` instead.
|
||||
fn str_cond(&self) -> Option<String> {
|
||||
fn str_cond(&self) -> Option<MacroString> {
|
||||
match self {
|
||||
Self::Atom(s) => Some("if ".to_string() + s),
|
||||
Self::Atom(s) => Some(MacroString::from("if ") + s.clone()),
|
||||
Self::Not(n) => match *(*n).clone() {
|
||||
Self::Atom(s) => Some("unless ".to_string() + &s),
|
||||
Self::Atom(s) => Some(MacroString::from("unless ") + s),
|
||||
_ => None,
|
||||
},
|
||||
Self::And(a, b) => {
|
||||
let a = a.str_cond()?;
|
||||
let b = b.str_cond()?;
|
||||
|
||||
Some(a + " " + &b)
|
||||
Some(a + MacroString::from(" ") + b)
|
||||
}
|
||||
Self::Or(..) => None,
|
||||
}
|
||||
|
@ -414,11 +478,24 @@ impl Condition {
|
|||
|
||||
/// Compile the condition into a list of strings that can be used in Minecraft.
|
||||
pub fn compile(
|
||||
&self,
|
||||
options: &CompileOptions,
|
||||
global_state: &MutCompilerState,
|
||||
function_state: &FunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
self.compile_keep_macros(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|s| s.compile())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Compile the condition into a list of macro strings.
|
||||
pub fn compile_keep_macros(
|
||||
&self,
|
||||
_options: &CompileOptions,
|
||||
_global_state: &MutCompilerState,
|
||||
_function_state: &FunctionCompilerState,
|
||||
) -> Vec<String> {
|
||||
) -> Vec<MacroString> {
|
||||
let truth_table = self.to_truth_table();
|
||||
|
||||
truth_table
|
||||
|
@ -427,13 +504,37 @@ impl Condition {
|
|||
c.str_cond()
|
||||
.expect("Truth table should not contain Or variants")
|
||||
})
|
||||
.collect()
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Check whether the condition contains a macro.
|
||||
#[must_use]
|
||||
pub fn contains_macro(&self) -> bool {
|
||||
match self {
|
||||
Self::Atom(s) => s.contains_macro(),
|
||||
Self::Not(n) => n.contains_macro(),
|
||||
Self::And(a, b) | Self::Or(a, b) => a.contains_macro() || b.contains_macro(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the names of the macros used
|
||||
#[must_use]
|
||||
pub fn get_macros(&self) -> HashSet<&str> {
|
||||
match self {
|
||||
Self::Atom(s) => s.get_macros(),
|
||||
Self::Not(n) => n.get_macros(),
|
||||
Self::And(a, b) | Self::Or(a, b) => {
|
||||
let mut set = a.get_macros();
|
||||
set.extend(b.get_macros());
|
||||
set
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Condition {
|
||||
fn from(s: &str) -> Self {
|
||||
Self::Atom(s.to_string())
|
||||
Self::Atom(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,12 +560,6 @@ impl BitOr for Condition {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Execute> for Command {
|
||||
fn from(ex: Execute) -> Self {
|
||||
Self::Execute(ex)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -473,8 +568,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_condition() {
|
||||
let c1 = Condition::from("foo");
|
||||
let c2 = Condition::Atom("bar".to_string());
|
||||
let c3 = Condition::Atom("baz".to_string());
|
||||
let c2 = Condition::Atom("bar".into());
|
||||
let c3 = Condition::Atom("baz".into());
|
||||
|
||||
assert_eq!(
|
||||
(c1.clone() & c2.clone()).normalize(),
|
||||
|
@ -517,10 +612,10 @@ mod tests {
|
|||
#[allow(clippy::redundant_clone)]
|
||||
#[test]
|
||||
fn test_truth_table() {
|
||||
let c1 = Condition::Atom("foo".to_string());
|
||||
let c2 = Condition::Atom("bar".to_string());
|
||||
let c3 = Condition::Atom("baz".to_string());
|
||||
let c4 = Condition::Atom("foobar".to_string());
|
||||
let c1 = Condition::Atom("foo".into());
|
||||
let c2 = Condition::Atom("bar".into());
|
||||
let c3 = Condition::Atom("baz".into());
|
||||
let c4 = Condition::Atom("foobar".into());
|
||||
|
||||
assert_eq!(
|
||||
(c1.clone() & c2.clone()).to_truth_table(),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::ops::RangeInclusive;
|
||||
use std::{collections::HashSet, ops::RangeInclusive};
|
||||
|
||||
use super::Command;
|
||||
use crate::util::{
|
||||
compile::{CompileOptions, FunctionCompilerState, MutCompilerState},
|
||||
ExtendableQueue,
|
||||
ExtendableQueue, MacroString,
|
||||
};
|
||||
|
||||
mod conditional;
|
||||
|
@ -15,18 +15,18 @@ pub use conditional::Condition;
|
|||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Execute {
|
||||
Align(String, Box<Execute>),
|
||||
Anchored(String, Box<Execute>),
|
||||
As(String, Box<Execute>),
|
||||
At(String, Box<Execute>),
|
||||
AsAt(String, Box<Execute>),
|
||||
Facing(String, Box<Execute>),
|
||||
In(String, Box<Execute>),
|
||||
On(String, Box<Execute>),
|
||||
Positioned(String, Box<Execute>),
|
||||
Rotated(String, Box<Execute>),
|
||||
Store(String, Box<Execute>),
|
||||
Summon(String, Box<Execute>),
|
||||
Align(MacroString, Box<Execute>),
|
||||
Anchored(MacroString, Box<Execute>),
|
||||
As(MacroString, Box<Execute>),
|
||||
At(MacroString, Box<Execute>),
|
||||
AsAt(MacroString, Box<Execute>),
|
||||
Facing(MacroString, Box<Execute>),
|
||||
In(MacroString, Box<Execute>),
|
||||
On(MacroString, Box<Execute>),
|
||||
Positioned(MacroString, Box<Execute>),
|
||||
Rotated(MacroString, Box<Execute>),
|
||||
Store(MacroString, Box<Execute>),
|
||||
Summon(MacroString, Box<Execute>),
|
||||
If(Condition, Box<Execute>, Option<Box<Execute>>),
|
||||
Run(Box<Command>),
|
||||
Runs(Vec<Command>),
|
||||
|
@ -78,15 +78,23 @@ impl Execute {
|
|||
| Self::On(arg, next)
|
||||
| Self::Positioned(arg, next)
|
||||
| Self::Rotated(arg, next)
|
||||
| Self::Store(arg, next) => next.compile_internal(
|
||||
format!("{prefix}{op} {arg} ", op = self.variant_name()),
|
||||
| Self::Store(arg, next)
|
||||
| Self::Summon(arg, next) => next.compile_internal(
|
||||
format!(
|
||||
"{prefix}{op} {arg} ",
|
||||
op = self.variant_name(),
|
||||
arg = arg.compile()
|
||||
),
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::AsAt(selector, next) => next.compile_internal(
|
||||
format!("{prefix}as {selector} at @s "),
|
||||
format!(
|
||||
"{prefix}as {selector} at @s ",
|
||||
selector = selector.compile()
|
||||
),
|
||||
require_grouping,
|
||||
options,
|
||||
global_state,
|
||||
|
@ -101,13 +109,6 @@ impl Execute {
|
|||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Summon(arg, next) => next.compile_internal(
|
||||
format!("{prefix}{op} {arg} ", op = self.variant_name()),
|
||||
true,
|
||||
options,
|
||||
global_state,
|
||||
function_state,
|
||||
),
|
||||
Self::Run(command) => match &**command {
|
||||
Command::Execute(ex) => ex.compile_internal(
|
||||
prefix,
|
||||
|
@ -119,19 +120,27 @@ impl Execute {
|
|||
command => command
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| map_run_cmd(c, &prefix))
|
||||
.map(|c| map_run_cmd(command.forbid_prefix(), c, &prefix))
|
||||
.collect(),
|
||||
},
|
||||
Self::Runs(commands) if !require_grouping => commands
|
||||
.iter()
|
||||
.flat_map(|c| c.compile(options, global_state, function_state))
|
||||
.map(|c| map_run_cmd(c, &prefix))
|
||||
.collect(),
|
||||
Self::Runs(commands) => Command::Group(commands.clone())
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| map_run_cmd(c, &prefix))
|
||||
.flat_map(|c| {
|
||||
let forbid_prefix = c.forbid_prefix();
|
||||
c.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(move |c| (forbid_prefix, c))
|
||||
})
|
||||
.map(|(forbid_prefix, c)| map_run_cmd(forbid_prefix, c, &prefix))
|
||||
.collect(),
|
||||
Self::Runs(commands) => {
|
||||
let group = Command::Group(commands.clone());
|
||||
group
|
||||
.compile(options, global_state, function_state)
|
||||
.into_iter()
|
||||
.map(|c| map_run_cmd(group.forbid_prefix(), c, &prefix))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,12 +209,87 @@ impl Execute {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the execute command contains a macro.
|
||||
#[must_use]
|
||||
pub fn contains_macro(&self) -> bool {
|
||||
match self {
|
||||
Self::Facing(s, next)
|
||||
| Self::Store(s, next)
|
||||
| Self::Positioned(s, next)
|
||||
| Self::Rotated(s, next)
|
||||
| Self::In(s, next)
|
||||
| Self::As(s, next)
|
||||
| Self::At(s, next)
|
||||
| Self::AsAt(s, next)
|
||||
| Self::Align(s, next)
|
||||
| Self::Anchored(s, next)
|
||||
| Self::Summon(s, next)
|
||||
| Self::On(s, next) => s.contains_macro() || next.contains_macro(),
|
||||
Self::If(cond, then, el) => {
|
||||
cond.contains_macro()
|
||||
|| then.contains_macro()
|
||||
|| el.as_deref().map_or(false, Self::contains_macro)
|
||||
}
|
||||
Self::Run(cmd) => cmd.contains_macro(),
|
||||
Self::Runs(cmds) => cmds.iter().any(super::Command::contains_macro),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the names of the macros used
|
||||
#[must_use]
|
||||
pub fn get_macros(&self) -> HashSet<&str> {
|
||||
match self {
|
||||
Self::Facing(s, _)
|
||||
| Self::Store(s, _)
|
||||
| Self::Positioned(s, _)
|
||||
| Self::Rotated(s, _)
|
||||
| Self::In(s, _)
|
||||
| Self::As(s, _)
|
||||
| Self::At(s, _)
|
||||
| Self::AsAt(s, _)
|
||||
| Self::Align(s, _)
|
||||
| Self::Anchored(s, _)
|
||||
| Self::Summon(s, _)
|
||||
| Self::On(s, _) => s.get_macros(),
|
||||
Self::If(cond, then, el) => {
|
||||
let mut macros = cond.get_macros();
|
||||
macros.extend(then.get_macros());
|
||||
if let Some(el) = el {
|
||||
macros.extend(el.get_macros());
|
||||
}
|
||||
macros
|
||||
}
|
||||
Self::Run(cmd) => cmd.get_macros(),
|
||||
Self::Runs(cmds) => cmds.iter().flat_map(|cmd| cmd.get_macros()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Execute> for Command {
|
||||
fn from(execute: Execute) -> Self {
|
||||
Self::Execute(execute)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Command> for Execute {
|
||||
fn from(command: Command) -> Self {
|
||||
Self::Run(Box::new(command))
|
||||
}
|
||||
}
|
||||
impl<V> From<V> for Execute
|
||||
where
|
||||
V: Into<Vec<Command>>,
|
||||
{
|
||||
fn from(value: V) -> Self {
|
||||
Self::Runs(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Combine command parts, respecting if the second part is a comment
|
||||
/// The first tuple element is a boolean indicating if the prefix should be used
|
||||
fn map_run_cmd(cmd: String, prefix: &str) -> (bool, String) {
|
||||
if cmd.starts_with('#') || cmd.is_empty() || cmd.chars().all(char::is_whitespace) {
|
||||
fn map_run_cmd(forbid_prefix: bool, cmd: String, prefix: &str) -> (bool, String) {
|
||||
if forbid_prefix || cmd.is_empty() || cmd.chars().all(char::is_whitespace) {
|
||||
(false, cmd)
|
||||
} else {
|
||||
(true, prefix.to_string() + "run " + &cmd)
|
||||
|
@ -219,7 +303,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_compile() {
|
||||
let compiled = Execute::As(
|
||||
"@ְa".to_string(),
|
||||
"@a".into(),
|
||||
Box::new(Execute::If(
|
||||
"block ~ ~-1 ~ minecraft:stone".into(),
|
||||
Box::new(Execute::Run(Box::new("say hi".into()))),
|
||||
|
@ -234,7 +318,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
compiled,
|
||||
vec!["execute as @ְa if block ~ ~-1 ~ minecraft:stone run say hi".to_string()]
|
||||
vec!["execute as @a if block ~ ~-1 ~ minecraft:stone run say hi".to_string()]
|
||||
);
|
||||
|
||||
let direct = Execute::Run(Box::new("say direct".into())).compile(
|
||||
|
|
|
@ -36,6 +36,8 @@ pub enum Command {
|
|||
Group(Vec<Command>),
|
||||
/// Comment to be added to the function
|
||||
Comment(String),
|
||||
/// Command that is a concatenation of two commands
|
||||
Concat(Box<Command>, Box<Command>),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
|
@ -59,6 +61,23 @@ impl Command {
|
|||
Self::Execute(ex) => ex.compile(options, global_state, function_state),
|
||||
Self::Group(commands) => compile_group(commands, options, global_state, function_state),
|
||||
Self::Comment(comment) => vec!["#".to_string() + comment],
|
||||
Self::Concat(a, b) => {
|
||||
let a = a.compile(options, global_state, function_state);
|
||||
let b = b.compile(options, global_state, function_state);
|
||||
a.into_iter()
|
||||
.flat_map(|a| {
|
||||
b.iter().map(move |b| {
|
||||
if a.is_empty() {
|
||||
b.clone()
|
||||
} else if b.is_empty() {
|
||||
a.clone()
|
||||
} else {
|
||||
a.clone() + b
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,27 +92,36 @@ impl Command {
|
|||
Self::UsesMacro(cmd) => cmd.line_count(),
|
||||
Self::Execute(ex) => ex.get_count(options),
|
||||
Self::Group(_) => 1,
|
||||
Self::Concat(a, b) => a.get_count(options) + b.get_count(options) - 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the command is valid with the given pack format.
|
||||
#[must_use]
|
||||
pub fn validate(&self, pack_formats: &RangeInclusive<u8>) -> bool {
|
||||
match self {
|
||||
let command_valid = match self {
|
||||
Self::Comment(_) | Self::Debug(_) | Self::Group(_) => true,
|
||||
Self::Raw(cmd) => validate_raw_cmd(cmd, pack_formats),
|
||||
Self::UsesMacro(cmd) => validate_raw_cmd(&cmd.compile(), pack_formats),
|
||||
Self::Execute(ex) => ex.validate(pack_formats),
|
||||
Self::Concat(a, b) => a.validate(pack_formats) && b.validate(pack_formats),
|
||||
};
|
||||
if pack_formats.start() < &16 {
|
||||
command_valid && !self.contains_macro()
|
||||
} else {
|
||||
command_valid
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the command contains a macro.
|
||||
#[must_use]
|
||||
pub fn contains_macro(&self, options: &CompileOptions) -> bool {
|
||||
pub fn contains_macro(&self) -> bool {
|
||||
match self {
|
||||
Self::Raw(_) | Self::Comment(_) | Self::Execute(_) => false,
|
||||
Self::Raw(_) | Self::Comment(_) => false,
|
||||
Self::UsesMacro(s) | Self::Debug(s) => s.contains_macro(),
|
||||
Self::Group(commands) => group_contains_macro(commands, options),
|
||||
Self::Group(commands) => group_contains_macro(commands),
|
||||
Self::Execute(ex) => ex.contains_macro(),
|
||||
Self::Concat(a, b) => a.contains_macro() || b.contains_macro(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +129,26 @@ impl Command {
|
|||
#[must_use]
|
||||
pub fn get_macros(&self) -> HashSet<&str> {
|
||||
match self {
|
||||
Self::Raw(_) | Self::Comment(_) | Self::Execute(_) => HashSet::new(),
|
||||
Self::Raw(_) | Self::Comment(_) => HashSet::new(),
|
||||
Self::UsesMacro(s) | Self::Debug(s) => s.get_macros(),
|
||||
Self::Group(commands) => group_get_macros(commands),
|
||||
Self::Execute(ex) => ex.get_macros(),
|
||||
Self::Concat(a, b) => {
|
||||
let mut macros = a.get_macros();
|
||||
macros.extend(b.get_macros());
|
||||
macros
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the command should not have a prefix.
|
||||
#[must_use]
|
||||
pub fn forbid_prefix(&self) -> bool {
|
||||
match self {
|
||||
Self::Comment(_) => true,
|
||||
Self::Raw(_) | Self::Debug(_) | Self::Execute(_) | Self::UsesMacro(_) => false,
|
||||
Self::Group(commands) => commands.len() == 1 && commands[0].forbid_prefix(),
|
||||
Self::Concat(a, _) => a.forbid_prefix(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +194,7 @@ fn compile_group(
|
|||
// only create a function if there are more than one command
|
||||
if command_count > 1 {
|
||||
let uid = function_state.request_uid();
|
||||
let pass_macros = group_contains_macro(commands, options);
|
||||
let pass_macros = group_contains_macro(commands);
|
||||
|
||||
// calculate a hashed path for the function in the `sb` subfolder
|
||||
let function_path = {
|
||||
|
@ -172,15 +217,11 @@ fn compile_group(
|
|||
let mut function_invocation = format!("function {namespace}:{function_path}");
|
||||
|
||||
if pass_macros {
|
||||
let macros_block =
|
||||
group_get_macros(commands)
|
||||
.into_iter()
|
||||
.fold(String::new(), |mut s, m| {
|
||||
use std::fmt::Write;
|
||||
|
||||
write!(&mut s, "{m}:$({m})").expect("can always write to string");
|
||||
s
|
||||
});
|
||||
let macros_block = group_get_macros(commands)
|
||||
.into_iter()
|
||||
.map(|m| format!("{m}:$({m})"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
function_invocation.push_str(&format!(" {{{macros_block}}}"));
|
||||
}
|
||||
|
||||
|
@ -193,8 +234,8 @@ fn compile_group(
|
|||
}
|
||||
}
|
||||
|
||||
fn group_contains_macro(commands: &[Command], options: &CompileOptions) -> bool {
|
||||
commands.iter().any(|cmd| cmd.contains_macro(options))
|
||||
fn group_contains_macro(commands: &[Command]) -> bool {
|
||||
commands.iter().any(Command::contains_macro)
|
||||
}
|
||||
|
||||
fn group_get_macros(commands: &[Command]) -> HashSet<&str> {
|
||||
|
|
|
@ -65,8 +65,16 @@ impl Function {
|
|||
.flat_map(|c| {
|
||||
let cmds = c.compile(options, global_state, function_state);
|
||||
|
||||
if c.contains_macro(options) {
|
||||
cmds.into_iter().map(|c| format!("${c}")).collect()
|
||||
if c.contains_macro() {
|
||||
cmds.into_iter()
|
||||
.map(|c| {
|
||||
if c.starts_with('#') {
|
||||
c
|
||||
} else {
|
||||
format!("${c}")
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
cmds
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use std::{borrow::Cow, collections::HashSet};
|
||||
use std::{borrow::Cow, collections::HashSet, ops::Add};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -102,3 +102,46 @@ impl From<&str> for MacroStringPart {
|
|||
Self::String(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for MacroString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
match (self, rhs) {
|
||||
(Self::String(mut s1), Self::String(s2)) => {
|
||||
s1.push_str(&s2);
|
||||
Self::String(s1)
|
||||
}
|
||||
(Self::String(s1), Self::MacroString(s2)) => Self::MacroString(
|
||||
std::iter::once(MacroStringPart::String(s1))
|
||||
.chain(s2)
|
||||
.collect(),
|
||||
),
|
||||
(Self::MacroString(mut s1), Self::String(s2)) => {
|
||||
s1.push(MacroStringPart::String(s2));
|
||||
Self::MacroString(s1)
|
||||
}
|
||||
(Self::MacroString(mut s1), Self::MacroString(s2)) => {
|
||||
s1.extend(s2);
|
||||
Self::MacroString(s1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&str> for MacroString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: &str) -> Self::Output {
|
||||
match self {
|
||||
Self::String(mut s1) => {
|
||||
s1.push_str(rhs);
|
||||
Self::String(s1)
|
||||
}
|
||||
Self::MacroString(mut s1) => {
|
||||
s1.push(MacroStringPart::String(rhs.to_string()));
|
||||
Self::MacroString(s1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue