From de285639a2035e6263669d5163f68dc087bb4ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sun, 9 Jun 2024 21:22:48 +0200 Subject: [PATCH] implement transpile function for multiple script paths --- src/lib.rs | 134 +++++++++++++++++++++----------- src/transpile/transpiler.rs | 147 ++++++++++++++++++++++++------------ 2 files changed, 187 insertions(+), 94 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9179772..6c739e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,34 +83,62 @@ pub fn parse(path: &Path) -> Result { /// - If an error occurs while parsing the source code. /// - If an error occurs while transpiling the source code. #[cfg(feature = "shulkerbox")] -pub fn transpile(path: &Path) -> Result { - let source_file = SourceFile::load(path)?; - +pub fn transpile

(script_paths: &[(String, P)]) -> Result +where + P: AsRef, +{ let printer = Printer::new(); - let tokens = TokenStream::tokenize(&source_file, &printer); + let programs = script_paths + .iter() + .map(|(program_identifier, path)| { + let source_file = SourceFile::load(path.as_ref())?; - if printer.has_printed() { - return Err(Error::Other( - "An error occurred while tokenizing the source code.", - )); + let tokens = TokenStream::tokenize(&source_file, &printer); + + // println!("tokens: {tokens:#?}"); + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while tokenizing the source code.", + )); + } + + let mut parser = Parser::new(&tokens); + let program = parser.parse_program(&printer).ok_or(Error::Other( + "An error occured while parsing the source code.", + ))?; + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while parsing the source code.", + )); + } + + Ok((program_identifier, program)) + }) + .collect::>(); + + if programs.iter().any(Result::is_err) { + return Err(programs.into_iter().find_map(Result::err).unwrap()); } + let programs = programs + .into_iter() + .filter_map(Result::ok) + .collect::>(); - let mut parser = Parser::new(&tokens); - let program = parser.parse_program(&printer).ok_or(Error::Other( - "An error occured while parsing the source code.", - ))?; - - if printer.has_printed() { - return Err(Error::Other( - "An error occurred while parsing the source code.", - )); - } - - let mut transpiler = Transpiler::new("shulkerscript-pack", 27); - transpiler.transpile(&program, &printer)?; + let mut transpiler = Transpiler::new(27); + transpiler.transpile(&programs, &printer)?; let datapack = transpiler.into_datapack(); + // println!("datapack: {datapack:#?}"); + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while transpiling the source code.", + )); + } + Ok(datapack) } @@ -121,36 +149,52 @@ pub fn transpile(path: &Path) -> Result { /// - If an error occurs while parsing the source code. /// - If an error occurs while transpiling the source code. #[cfg(feature = "shulkerbox")] -pub fn compile(path: &Path) -> Result { - let source_file = SourceFile::load(path)?; - +pub fn compile

(script_paths: &[(String, P)]) -> Result +where + P: AsRef, +{ let printer = Printer::new(); - let tokens = TokenStream::tokenize(&source_file, &printer); + let programs = script_paths + .iter() + .map(|(program_identifier, path)| { + let source_file = SourceFile::load(path.as_ref())?; - // println!("tokens: {tokens:#?}"); + let tokens = TokenStream::tokenize(&source_file, &printer); - if printer.has_printed() { - return Err(Error::Other( - "An error occurred while tokenizing the source code.", - )); + // println!("tokens: {tokens:#?}"); + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while tokenizing the source code.", + )); + } + + let mut parser = Parser::new(&tokens); + let program = parser.parse_program(&printer).ok_or(Error::Other( + "An error occured while parsing the source code.", + ))?; + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while parsing the source code.", + )); + } + + Ok((program_identifier, program)) + }) + .collect::>(); + + if programs.iter().any(Result::is_err) { + return Err(programs.into_iter().find_map(Result::err).unwrap()); } + let programs = programs + .into_iter() + .filter_map(Result::ok) + .collect::>(); - let mut parser = Parser::new(&tokens); - let program = parser.parse_program(&printer).ok_or(Error::Other( - "An error occured while parsing the source code.", - ))?; - - if printer.has_printed() { - return Err(Error::Other( - "An error occurred while parsing the source code.", - )); - } - - // println!("program: {program:#?}"); - - let mut transpiler = Transpiler::new("shulkerscript-pack", 27); - transpiler.transpile(&program, &printer)?; + let mut transpiler = Transpiler::new(27); + transpiler.transpile(&programs, &printer)?; let datapack = transpiler.into_datapack(); // println!("datapack: {datapack:#?}"); diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 324de3a..c71ca9d 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -24,7 +24,8 @@ use super::error::{TranspileError, TranspileResult}; #[derive(Debug)] pub struct Transpiler { datapack: shulkerbox::datapack::Datapack, - functions: RwLock>, + /// Key: (program identifier, function name) + functions: RwLock>, function_locations: RwLock>, } @@ -38,9 +39,9 @@ struct FunctionData { impl Transpiler { /// Creates a new transpiler. #[must_use] - pub fn new(pack_name: &str, pack_format: u8) -> Self { + pub fn new(pack_format: u8) -> Self { Self { - datapack: shulkerbox::datapack::Datapack::new(pack_name, pack_format), + datapack: shulkerbox::datapack::Datapack::new(pack_format), functions: RwLock::new(HashMap::new()), function_locations: RwLock::new(HashMap::new()), } @@ -52,19 +53,36 @@ impl Transpiler { self.datapack } - /// Transpiles the given program. + /// Transpiles the given programs. /// /// # Errors /// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing - pub fn transpile( + pub fn transpile( + &mut self, + programs: &[(Ident, ProgramFile)], + handler: &impl Handler, + ) -> Result<(), TranspileError> + where + Ident: AsRef, + { + for (identifier, program) in programs { + self.transpile_program(program, identifier.as_ref(), handler)?; + } + + Ok(()) + } + + /// Transpiles the given program. + fn transpile_program( &mut self, program: &ProgramFile, + identifier: &str, handler: &impl Handler, - ) -> Result<(), TranspileError> { + ) -> TranspileResult<()> { let namespace = program.namespace(); for declaration in program.declarations() { - self.transpile_declaration(declaration, namespace, handler); + self.transpile_declaration(declaration, namespace, identifier, handler); } let mut always_transpile_functions = Vec::new(); @@ -72,18 +90,18 @@ impl Transpiler { #[allow(clippy::significant_drop_in_scrutinee)] { let functions = self.functions.read().unwrap(); - for (name, data) in functions.iter() { + for (function_identifier, data) in functions.iter() { let always_transpile_function = data.annotations.contains_key("tick") || data.annotations.contains_key("load") || data.annotations.contains_key("deobfuscate"); if always_transpile_function { - always_transpile_functions.push(name.clone()); + always_transpile_functions.push(function_identifier.to_owned()); }; } } - for name in always_transpile_functions { - self.get_or_transpile_function(&name, handler)?; + for (program_identifier, name) in always_transpile_functions { + self.get_or_transpile_function(&name, &program_identifier, handler)?; } Ok(()) @@ -94,6 +112,7 @@ impl Transpiler { &mut self, declaration: &Declaration, namespace: &Namespace, + program_identifier: &str, _handler: &impl Handler, ) { match declaration { @@ -113,7 +132,7 @@ impl Transpiler { }) .collect(); self.functions.write().unwrap().insert( - name, + (program_identifier.to_string(), name), FunctionData { namespace: namespace.namespace_name().str_content().to_string(), statements, @@ -130,6 +149,7 @@ impl Transpiler { fn get_or_transpile_function( &mut self, name: &str, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult { let already_transpiled = { @@ -139,27 +159,34 @@ impl Transpiler { if !already_transpiled { let statements = { let functions = self.functions.read().unwrap(); - let function_data = functions.get(name).ok_or_else(|| { + let function_data = functions + .get(&(program_identifier.to_string(), name.to_string())) + .ok_or_else(|| { + let error = TranspileError::MissingFunctionDeclaration(name.to_string()); + handler.receive(error.clone()); + error + })?; + function_data.statements.clone() + }; + let commands = self.transpile_function(&statements, program_identifier, handler)?; + + let functions = self.functions.read().unwrap(); + let function_data = functions + .get(&(program_identifier.to_string(), name.to_string())) + .ok_or_else(|| { let error = TranspileError::MissingFunctionDeclaration(name.to_string()); handler.receive(error.clone()); error })?; - function_data.statements.clone() - }; - let commands = self.transpile_function(&statements, handler)?; - - let functions = self.functions.read().unwrap(); - let function_data = functions.get(name).ok_or_else(|| { - let error = TranspileError::MissingFunctionDeclaration(name.to_string()); - handler.receive(error.clone()); - error - })?; let modified_name = function_data .annotations .get("deobfuscate") .map_or_else( - || Some("shu/".to_string() + &md5::hash(name).to_hex_lowercase()[..16]), + || { + let hash_data = program_identifier.to_string() + "\0" + name; + Some("shu/".to_string() + &md5::hash(hash_data).to_hex_lowercase()[..16]) + }, Clone::clone, ) .unwrap_or_else(|| name.to_string()); @@ -202,13 +229,14 @@ impl Transpiler { fn transpile_function( &mut self, statements: &[Statement], + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult> { let mut errors = Vec::new(); let commands = statements .iter() .filter_map(|statement| { - self.transpile_statement(statement, handler) + self.transpile_statement(statement, program_identifier, handler) .unwrap_or_else(|err| { errors.push(err); None @@ -226,6 +254,7 @@ impl Transpiler { fn transpile_statement( &mut self, statement: &Statement, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult> { match statement { @@ -233,9 +262,9 @@ impl Transpiler { Ok(Some(literal_command.clean_command().into())) } Statement::Run(run) => match run.expression() { - Expression::Primary(Primary::FunctionCall(func)) => { - self.transpile_function_call(func, handler).map(Some) - } + Expression::Primary(Primary::FunctionCall(func)) => self + .transpile_function_call(func, program_identifier, handler) + .map(Some), Expression::Primary(Primary::StringLiteral(string)) => { Ok(Some(Command::Raw(string.str_content().to_string()))) } @@ -246,7 +275,9 @@ impl Transpiler { Statement::Block(_) => { unreachable!("Only literal commands are allowed in functions at this time.") } - Statement::ExecuteBlock(execute) => self.transpile_execute_block(execute, handler), + Statement::ExecuteBlock(execute) => { + self.transpile_execute_block(execute, program_identifier, handler) + } Statement::DocComment(doccomment) => { let content = doccomment.content(); Ok(Some(Command::Comment(content.to_string()))) @@ -257,7 +288,7 @@ impl Transpiler { let commands = statements .iter() .filter_map(|statement| { - self.transpile_statement(statement, handler) + self.transpile_statement(statement, program_identifier, handler) .unwrap_or_else(|err| { errors.push(err); None @@ -275,9 +306,9 @@ impl Transpiler { } #[allow(clippy::match_wildcard_for_single_variants)] Statement::Semicolon(semi) => match semi.expression() { - Expression::Primary(Primary::FunctionCall(func)) => { - self.transpile_function_call(func, handler).map(Some) - } + Expression::Primary(Primary::FunctionCall(func)) => self + .transpile_function_call(func, program_identifier, handler) + .map(Some), unexpected => { let error = TranspileError::UnexpectedExpression(unexpected.clone()); handler.receive(error.clone()); @@ -290,26 +321,30 @@ impl Transpiler { fn transpile_function_call( &mut self, func: &FunctionCall, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult { let identifier = func.identifier().span(); let identifier_name = identifier.str(); - let location = self.get_or_transpile_function(identifier_name, handler)?; + let location = + self.get_or_transpile_function(identifier_name, program_identifier, handler)?; Ok(Command::Raw(format!("function {location}"))) } fn transpile_execute_block( &mut self, execute: &ExecuteBlock, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult> { - self.transpile_execute_block_internal(execute, handler) + self.transpile_execute_block_internal(execute, program_identifier, handler) .map(|ex| ex.map(Command::Execute)) } fn transpile_execute_block_internal( &mut self, execute: &ExecuteBlock, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult> { match execute { @@ -321,10 +356,11 @@ impl Transpiler { .statements() .iter() .filter_map(|s| { - self.transpile_statement(s, handler).unwrap_or_else(|err| { - errors.push(err); - None - }) + self.transpile_statement(s, program_identifier, handler) + .unwrap_or_else(|err| { + errors.push(err); + None + }) }) .collect::>(); @@ -337,12 +373,15 @@ impl Transpiler { Ok(Some(Execute::Runs(commands))) } } - ExecuteBlockTail::ExecuteBlock(_, execute_block) => { - self.transpile_execute_block_internal(execute_block, handler) - } + ExecuteBlockTail::ExecuteBlock(_, execute_block) => self + .transpile_execute_block_internal( + execute_block, + program_identifier, + handler, + ), }?; - self.combine_execute_head_tail(head, tail, handler) + self.combine_execute_head_tail(head, tail, program_identifier, handler) } ExecuteBlock::IfElse(cond, block, el) => { let statements = block.statements(); @@ -353,7 +392,7 @@ impl Transpiler { let commands = statements .iter() .filter_map(|statement| { - self.transpile_statement(statement, handler) + self.transpile_statement(statement, program_identifier, handler) .unwrap_or_else(|err| { errors.push(err); None @@ -365,13 +404,21 @@ impl Transpiler { } Some(Execute::Runs(commands)) } else { - self.transpile_statement(&statements[0], handler)? + self.transpile_statement(&statements[0], program_identifier, handler)? .map(|cmd| Execute::Run(Box::new(cmd))) }; then.map_or_else( || Ok(None), - |then| self.transpile_conditional(cond, then, Some(el), handler), + |then| { + self.transpile_conditional( + cond, + then, + Some(el), + program_identifier, + handler, + ) + }, ) } } @@ -382,6 +429,7 @@ impl Transpiler { cond: &Conditional, then: Execute, el: Option<&Else>, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult> { let (_, cond) = cond.clone().dissolve(); @@ -395,7 +443,7 @@ impl Transpiler { if statements.is_empty() { None } else if statements.len() == 1 { - self.transpile_statement(&statements[0], handler) + self.transpile_statement(&statements[0], program_identifier, handler) .unwrap_or_else(|err| { errors.push(err); None @@ -405,7 +453,7 @@ impl Transpiler { let commands = statements .iter() .filter_map(|statement| { - self.transpile_statement(statement, handler) + self.transpile_statement(statement, program_identifier, handler) .unwrap_or_else(|err| { errors.push(err); None @@ -432,12 +480,13 @@ impl Transpiler { &mut self, head: &ExecuteBlockHead, tail: Option, + program_identifier: &str, handler: &impl Handler, ) -> TranspileResult> { Ok(match head { ExecuteBlockHead::Conditional(cond) => { if let Some(tail) = tail { - self.transpile_conditional(cond, tail, None, handler)? + self.transpile_conditional(cond, tail, None, program_identifier, handler)? } else { None }