From 2c9fa613ed756dca53899bbd0a9be233e3e344e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:10:02 +0100 Subject: [PATCH] pass on macros to group --- src/datapack/command/mod.rs | 71 ++++++++++++++++++++++++++++++------- src/datapack/function.rs | 10 +++++- src/util/macro_string.rs | 17 ++++++++- 3 files changed, 84 insertions(+), 14 deletions(-) diff --git a/src/datapack/command/mod.rs b/src/datapack/command/mod.rs index 2d0f715..e4a0ad3 100644 --- a/src/datapack/command/mod.rs +++ b/src/datapack/command/mod.rs @@ -1,7 +1,11 @@ //! Represents a command that can be included in a function. mod execute; -use std::{collections::HashMap, ops::RangeInclusive, sync::OnceLock}; +use std::{ + collections::{HashMap, HashSet}, + ops::RangeInclusive, + sync::OnceLock, +}; pub use execute::{Condition, Execute}; @@ -10,7 +14,10 @@ use chksum_md5 as md5; use super::Function; use crate::{ prelude::Datapack, - util::{compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, MacroString}, + util::{ + compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, + MacroString, + }, }; /// Represents a command that can be included in a function. @@ -47,7 +54,7 @@ impl Command { ) -> Vec { match self { Self::Raw(command) => vec![command.clone()], - Self::UsesMacro(command) => vec![compile_macro(command)], + Self::UsesMacro(command) => vec![command.compile()], Self::Debug(message) => compile_debug(message, options), Self::Execute(ex) => ex.compile(options, global_state, function_state), Self::Group(commands) => compile_group(commands, options, global_state, function_state), @@ -79,6 +86,26 @@ impl Command { Self::Execute(ex) => ex.validate(pack_formats), } } + + /// Check whether the command contains a macro. + #[must_use] + pub fn contains_macro(&self, options: &CompileOptions) -> bool { + match self { + Self::Raw(_) | Self::Comment(_) | Self::Debug(_) | Self::Execute(_) => false, + Self::UsesMacro(cmd) => cmd.contains_macro(), + Self::Group(commands) => group_contains_macro(commands, options), + } + } + + /// Returns the names of the macros used + #[must_use] + pub fn get_macros(&self) -> HashSet<&str> { + match self { + Self::Raw(_) | Self::Comment(_) | Self::Debug(_) | Self::Execute(_) => HashSet::new(), + Self::UsesMacro(cmd) => cmd.get_macros(), + Self::Group(commands) => group_get_macros(commands), + } + } } impl From<&str> for Command { @@ -108,14 +135,6 @@ fn compile_debug(message: &str, option: &CompileOptions) -> Vec { } } -fn compile_macro(command: &MacroString) -> String { - if command.contains_macro() { - format!("${}", command.compile()) - } else { - command.compile() - } -} - #[tracing::instrument(skip_all, fields(commands = ?commands))] fn compile_group( commands: &[Command], @@ -130,6 +149,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); // calculate a hashed path for the function in the `sb` subfolder let function_path = { @@ -149,7 +169,22 @@ fn compile_group( function.get_commands_mut().extend(commands.iter().cloned()); function_state.add_function(&function_path, function); - vec![format!("function {namespace}:{function_path}")] + 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 + }); + function_invocation.push_str(&format!(" {{{macros_block}}}")); + } + + vec![function_invocation] } else { commands .iter() @@ -158,6 +193,18 @@ fn compile_group( } } +fn group_contains_macro(commands: &[Command], options: &CompileOptions) -> bool { + commands.iter().any(|cmd| cmd.contains_macro(options)) +} + +fn group_get_macros(commands: &[Command]) -> HashSet<&str> { + let mut macros = HashSet::new(); + for cmd in commands { + macros.extend(cmd.get_macros()); + } + macros +} + #[allow(clippy::too_many_lines)] fn validate_raw_cmd(cmd: &str, pack_formats: &RangeInclusive) -> bool { static CMD_FORMATS: OnceLock>> = OnceLock::new(); diff --git a/src/datapack/function.rs b/src/datapack/function.rs index 34bf309..80ca510 100644 --- a/src/datapack/function.rs +++ b/src/datapack/function.rs @@ -62,7 +62,15 @@ impl Function { let content = self .commands .iter() - .flat_map(|c| c.compile(options, global_state, function_state)) + .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() + } else { + cmds + } + }) .collect::>() .join("\n"); VFile::Text(content) diff --git a/src/util/macro_string.rs b/src/util/macro_string.rs index c85c33a..e1d9b27 100644 --- a/src/util/macro_string.rs +++ b/src/util/macro_string.rs @@ -1,6 +1,6 @@ #![allow(clippy::module_name_repetitions)] -use std::borrow::Cow; +use std::{borrow::Cow, collections::HashSet}; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -64,6 +64,21 @@ impl MacroString { } } } + + /// Returns the names of the macros used in the [`MacroString`] + #[must_use] + pub fn get_macros(&self) -> HashSet<&str> { + match self { + Self::String(_) => HashSet::new(), + Self::MacroString(parts) => parts + .iter() + .filter_map(|p| match p { + MacroStringPart::String(_) => None, + MacroStringPart::MacroUsage(m) => Some(m.as_str()), + }) + .collect(), + } + } } impl From for MacroString {