From fedb55c50a901d2dd72433176482a45da531187e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:47:50 +0200 Subject: [PATCH] Implement group command refactor function compilation --- Cargo.toml | 1 + src/datapack/command/execute.rs | 44 ++++++++++++++++------------ src/datapack/command/mod.rs | 51 +++++++++++++++++++++++++++------ src/datapack/function.rs | 13 +++++---- src/datapack/namespace.rs | 40 +++++++++++++++++++------- src/util/compile.rs | 51 +++++++++++++++++++++++++++++---- 6 files changed, 152 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 99aaf65..fca809d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ default = ["zip"] zip = ["dep:zip"] [dependencies] +chksum-md5 = "0.0.0" getset = "0.1.2" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.114" diff --git a/src/datapack/command/execute.rs b/src/datapack/command/execute.rs index 2345b1c..9a54d51 100644 --- a/src/datapack/command/execute.rs +++ b/src/datapack/command/execute.rs @@ -1,8 +1,8 @@ -use std::ops::{BitAnd, BitOr, Deref, Not}; +use std::ops::{BitAnd, BitOr, Not}; use serde::{Deserialize, Serialize}; -use crate::util::compile::{CompileOptions, MutCompilerState, MutFunctionCompilerState}; +use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState}; use super::Command; @@ -32,7 +32,7 @@ impl Execute { &self, options: &CompileOptions, global_state: &MutCompilerState, - function_state: &MutFunctionCompilerState, + function_state: &FunctionCompilerState, ) -> Vec { if let Self::Run(cmd) = self { cmd.compile(options, global_state, function_state) @@ -52,7 +52,7 @@ impl Execute { require_grouping: bool, options: &CompileOptions, global_state: &MutCompilerState, - function_state: &MutFunctionCompilerState, + function_state: &FunctionCompilerState, ) -> Vec { match self { Self::Align(align, next) => format_execute( @@ -172,16 +172,20 @@ impl Execute { global_state, function_state, ), - Self::Run(command) if !require_grouping => command - .compile(options, global_state, function_state) - .into_iter() - .map(|c| prefix.clone() + "run " + &c) - .collect(), - Self::Run(command) => Command::Group(vec![command.deref().clone()]) - .compile(options, global_state, function_state) - .into_iter() - .map(|c| prefix.clone() + "run " + &c) - .collect(), + Self::Run(command) => match &**command { + Command::Execute(ex) => ex.compile_internal( + prefix, + require_grouping, + options, + global_state, + function_state, + ), + command => command + .compile(options, global_state, function_state) + .into_iter() + .map(|c| prefix.clone() + "run " + &c) + .collect(), + }, Self::Runs(commands) if !require_grouping => commands .iter() .flat_map(|c| c.compile(options, global_state, function_state)) @@ -203,7 +207,7 @@ fn format_execute( require_grouping: bool, options: &CompileOptions, global_state: &MutCompilerState, - function_state: &MutFunctionCompilerState, + function_state: &FunctionCompilerState, ) -> Vec { next.compile_internal( prefix + new, @@ -221,12 +225,16 @@ fn compile_if_cond( prefix: String, options: &CompileOptions, global_state: &MutCompilerState, - function_state: &MutFunctionCompilerState, + function_state: &FunctionCompilerState, ) -> Vec { let str_cond = cond.clone().compile(options, global_state, function_state); let require_grouping = el.is_some() || str_cond.len() > 1; let then = if require_grouping { - let mut group_cmd = vec![Command::Execute(then.clone())]; + let mut group_cmd = match then.clone() { + Execute::Run(cmd) => vec![*cmd], + Execute::Runs(cmds) => cmds, + ex => vec![Command::Execute(ex)], + }; if el.is_some() { group_cmd.push("data modify storage shulkerbox:cond if_success set value true".into()); } @@ -318,7 +326,7 @@ impl Condition { &self, _options: &CompileOptions, _global_state: &MutCompilerState, - _function_state: &MutFunctionCompilerState, + _function_state: &FunctionCompilerState, ) -> Vec { match self.normalize() { Self::Atom(a) => vec!["if ".to_string() + &a], diff --git a/src/datapack/command/mod.rs b/src/datapack/command/mod.rs index a5d592d..4ddd1c4 100644 --- a/src/datapack/command/mod.rs +++ b/src/datapack/command/mod.rs @@ -4,9 +4,10 @@ mod execute; pub use execute::{Condition, Execute}; +use chksum_md5 as md5; use serde::{Deserialize, Serialize}; -use crate::util::compile::{CompileOptions, MutCompilerState, MutFunctionCompilerState}; +use crate::util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState}; use super::Function; @@ -34,19 +35,13 @@ impl Command { &self, options: &CompileOptions, global_state: &MutCompilerState, - function_state: &MutFunctionCompilerState, + function_state: &FunctionCompilerState, ) -> Vec { match self { Self::Raw(command) => vec![command.clone()], Self::Debug(message) => compile_debug(message, options), Self::Execute(ex) => ex.compile(options, global_state, function_state), - Self::Group(commands) => { - // TODO: implement correctly - commands - .iter() - .flat_map(|c| c.compile(options, global_state, function_state)) - .collect() - } + Self::Group(commands) => compile_group(commands, options, global_state, function_state), } } } @@ -77,3 +72,41 @@ fn compile_debug(message: &str, option: &CompileOptions) -> Vec { Vec::new() } } + +fn compile_group( + commands: &[Command], + options: &CompileOptions, + global_state: &MutCompilerState, + function_state: &FunctionCompilerState, +) -> Vec { + if commands.len() > 1 { + let generated_functions = { + let generated_functions = function_state.generated_functions(); + let amount = generated_functions.get(); + generated_functions.set(amount + 1); + + amount + }; + + let function_path = { + let pre_hash_path = + function_state.path().to_owned() + ":" + &generated_functions.to_string(); + let hash = md5::hash(pre_hash_path).to_hex_lowercase(); + + "sb/".to_string() + function_state.path() + "/" + &hash[..16] + }; + + let namespace = function_state.namespace(); + + let mut function = Function::new(namespace, &function_path); + function.get_commands_mut().extend(commands.iter().cloned()); + function_state.add_function(&function_path, function); + + vec![format!("function {namespace}:{function_path}")] + } else { + commands + .iter() + .flat_map(|c| c.compile(options, global_state, function_state)) + .collect() + } +} diff --git a/src/datapack/function.rs b/src/datapack/function.rs index 1698c79..51dea10 100644 --- a/src/datapack/function.rs +++ b/src/datapack/function.rs @@ -1,7 +1,5 @@ //! Function struct and implementation -use std::sync::Mutex; - use getset::Getters; use serde::{Deserialize, Serialize}; @@ -48,13 +46,16 @@ impl Function { } /// Compile the function into a virtual file. - pub fn compile(&self, options: &CompileOptions, state: &MutCompilerState) -> VFile { - let function_state = Mutex::new(FunctionCompilerState::default()); - + pub fn compile( + &self, + options: &CompileOptions, + global_state: &MutCompilerState, + function_state: &FunctionCompilerState, + ) -> VFile { let content = self .commands .iter() - .flat_map(|c| c.compile(options, state, &function_state)) + .flat_map(|c| c.compile(options, global_state, function_state)) .collect::>() .join("\n"); VFile::Text(content) diff --git a/src/datapack/namespace.rs b/src/datapack/namespace.rs index eae527f..36a384e 100644 --- a/src/datapack/namespace.rs +++ b/src/datapack/namespace.rs @@ -3,12 +3,15 @@ use serde::{Deserialize, Serialize}; use crate::{ - util::compile::{CompileOptions, MutCompilerState}, + util::compile::{CompileOptions, FunctionCompilerState, MutCompilerState}, virtual_fs::VFolder, }; use super::{function::Function, tag::Tag}; -use std::collections::HashMap; +use std::{ + collections::{HashMap, VecDeque}, + sync::{Arc, Mutex}, +}; /// Namespace of a datapack #[derive(Debug, Clone, Serialize, Deserialize)] @@ -76,16 +79,33 @@ impl Namespace { let mut root_folder = VFolder::new(); // Compile functions - for (path, function) in &self.functions { + let mut functions = self + .functions + .iter() + .map(|(name, content)| (name.clone(), content.clone())) + .collect::>(); + + if !self.main_function.get_commands().is_empty() { + functions.push_front(("main".to_string(), self.main_function.clone())); + } + + let functions = Arc::new(Mutex::new(functions)); + + loop { + let Some((path, function)) = ({ + let mut functions = functions.lock().unwrap(); + let entry = functions.pop_front(); + drop(functions); + entry + }) else { + break; + }; + + let function_state = + FunctionCompilerState::new(&path, &self.name, Arc::downgrade(&functions)); root_folder.add_file( &format!("functions/{}.mcfunction", path), - function.compile(options, state), - ); - } - if !self.main_function.get_commands().is_empty() { - root_folder.add_file( - "functions/main.mcfunction", - self.main_function.compile(options, state), + function.compile(options, state, &function_state), ); } diff --git a/src/util/compile.rs b/src/util/compile.rs index b1b38a5..0336afb 100644 --- a/src/util/compile.rs +++ b/src/util/compile.rs @@ -1,9 +1,16 @@ //! Compile options for the compiler. -use std::sync::Mutex; +use std::{ + cell::Cell, + collections::VecDeque, + sync::{Mutex, Weak}, +}; +use getset::Getters; use serde::{Deserialize, Serialize}; +use crate::datapack::Function; + /// Compile options for the compiler. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CompileOptions { @@ -24,7 +31,41 @@ pub struct CompilerState {} pub type MutCompilerState = Mutex; /// State of the compiler for each function that can change during compilation. -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct FunctionCompilerState {} -/// Mutex for the function compiler state. -pub type MutFunctionCompilerState = Mutex; +#[derive(Debug, Clone, Getters)] +pub struct FunctionCompilerState { + /// Number of generated functions in the current function. + #[get = "pub"] + generated_functions: Cell, + /// Path of the current function. + #[get = "pub"] + path: String, + /// Namespace of the current function. + #[get = "pub"] + namespace: String, + /// Queue of functions to be generated. + functions: FunctionQueue, +} + +type FunctionQueue = Weak>>; + +impl FunctionCompilerState { + /// Create a new function compiler state. + pub fn new(path: &str, namespace: &str, functions: FunctionQueue) -> Self { + Self { + generated_functions: Cell::new(0), + namespace: namespace.to_string(), + path: path.to_string(), + functions, + } + } + + /// Add a function to the queue. + pub fn add_function(&self, name: &str, function: Function) { + if let Some(queue) = self.functions.upgrade() { + queue + .lock() + .unwrap() + .push_back((name.to_string(), function)); + } + } +}