From 34076f98420ae9e57cf0ad2e2d9391e6fca7b5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sat, 6 Apr 2024 14:30:11 +0200 Subject: [PATCH] Update TranspileError enum and add TranspileResult type --- src/transpile/error.rs | 9 +- src/transpile/transpiler.rs | 171 +++++++++++++++++++++++++++--------- 2 files changed, 135 insertions(+), 45 deletions(-) diff --git a/src/transpile/error.rs b/src/transpile/error.rs index e723a27..06c8d8e 100644 --- a/src/transpile/error.rs +++ b/src/transpile/error.rs @@ -2,8 +2,11 @@ /// Errors that can occur during transpilation. #[allow(clippy::module_name_repetitions, missing_docs)] -#[derive(Debug, thiserror::Error, Clone, Copy)] +#[derive(Debug, thiserror::Error, Clone)] pub enum TranspileError { - #[error("No main function was found in the source code.")] - MissingMainFunction, + #[error("Function {} was called but never declared.", .0)] + MissingFunctionDeclaration(String), } + +/// The result of a transpilation operation. +pub type TranspileResult = Result; diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 7ec843e..ba12f37 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -1,4 +1,4 @@ -//! Compiler for `ShulkerScript` +//! Transpiler for `ShulkerScript` use chksum_md5 as md5; use std::{collections::HashMap, sync::RwLock}; @@ -15,7 +15,7 @@ use crate::{ }, }; -use super::error::TranspileError; +use super::error::{TranspileError, TranspileResult}; /// A transpiler for `ShulkerScript`. #[derive(Debug)] @@ -56,10 +56,10 @@ impl Transpiler { pub fn transpile( &mut self, program: &Program, - _handler: &impl Handler, + handler: &impl Handler, ) -> Result<(), TranspileError> { for declaration in program.declarations() { - self.transpile_declaration(declaration); + self.transpile_declaration(declaration, handler); } let mut always_transpile_functions = Vec::new(); @@ -78,14 +78,18 @@ impl Transpiler { } for name in always_transpile_functions { - self.get_or_transpile_function(&name); + self.get_or_transpile_function(&name, handler)?; } Ok(()) } /// Transpiles the given declaration. - fn transpile_declaration(&mut self, declaration: &Declaration) { + fn transpile_declaration( + &mut self, + declaration: &Declaration, + _handler: &impl Handler, + ) { match declaration { Declaration::Function(function) => { let name = function.identifier().span().str().to_string(); @@ -117,7 +121,11 @@ impl Transpiler { /// Gets the function at the given path, or transpiles it if it hasn't been transpiled yet. /// Returns the location of the function or None if the function does not exist. #[allow(clippy::significant_drop_tightening)] - fn get_or_transpile_function(&mut self, name: &str) -> Option { + fn get_or_transpile_function( + &mut self, + name: &str, + handler: &impl Handler, + ) -> TranspileResult { let already_transpiled = { let locations = self.function_locations.read().unwrap(); locations.get(name).is_some() @@ -125,13 +133,21 @@ impl Transpiler { if !already_transpiled { let statements = { let functions = self.functions.read().unwrap(); - let function_data = functions.get(name)?; + let function_data = functions.get(name).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); + let commands = self.transpile_function(&statements, handler)?; let functions = self.functions.read().unwrap(); - let function_data = functions.get(name)?; + 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 @@ -167,52 +183,96 @@ impl Transpiler { } let locations = self.function_locations.read().unwrap(); - locations.get(name).map(String::to_owned) + locations + .get(name) + .ok_or_else(|| { + let error = TranspileError::MissingFunctionDeclaration(name.to_string()); + handler.receive(error.clone()); + error + }) + .map(String::to_owned) } - fn transpile_function(&mut self, statements: &[Statement]) -> Vec { - let mut commands = Vec::new(); - for statement in statements { - commands.extend(self.transpile_statement(statement)); + fn transpile_function( + &mut self, + statements: &[Statement], + handler: &impl Handler, + ) -> TranspileResult> { + let mut errors = Vec::new(); + let commands = statements + .iter() + .filter_map(|statement| { + self.transpile_statement(statement, handler) + .unwrap_or_else(|err| { + errors.push(err); + None + }) + }) + .collect(); + + if !errors.is_empty() { + return Err(errors.remove(0)); } - commands + + Ok(commands) } - fn transpile_statement(&mut self, statement: &Statement) -> Option { + fn transpile_statement( + &mut self, + statement: &Statement, + handler: &impl Handler, + ) -> TranspileResult> { match statement { Statement::LiteralCommand(literal_command) => { - Some(literal_command.clean_command().into()) + Ok(Some(literal_command.clean_command().into())) } Statement::Block(_) => { unreachable!("Only literal commands are allowed in functions at this time.") } - Statement::Conditional(cond) => self.transpile_conditional(cond), + Statement::Conditional(cond) => self.transpile_conditional(cond, handler), Statement::DocComment(doccomment) => { let content = doccomment.content(); - Some(Command::Comment(content.to_string())) + Ok(Some(Command::Comment(content.to_string()))) } Statement::Grouping(group) => { let statements = group.block().statements(); + let mut errors = Vec::new(); let commands = statements .iter() - .filter_map(|statement| self.transpile_statement(statement)) + .filter_map(|statement| { + self.transpile_statement(statement, handler) + .unwrap_or_else(|err| { + errors.push(err); + None + }) + }) .collect::>(); + if !errors.is_empty() { + return Err(errors.remove(0)); + } if commands.is_empty() { - None + Ok(None) } else { - Some(Command::Group(commands)) + Ok(Some(Command::Group(commands))) } } Statement::Semicolon(semi) => match semi.expression() { - Expression::Primary(primary) => self.transpile_primary_expression(primary), + Expression::Primary(primary) => self + .transpile_primary_expression(primary, handler) + .map(Some), }, } } - fn transpile_conditional(&mut self, cond: &Conditional) -> Option { + fn transpile_conditional( + &mut self, + cond: &Conditional, + handler: &impl Handler, + ) -> TranspileResult> { let (_, cond, block, el) = cond.clone().dissolve(); let (_, cond, _) = cond.dissolve(); let statements = block.statements(); + let mut errors = Vec::new(); let el = el .and_then(|el| { @@ -221,54 +281,81 @@ impl Transpiler { if statements.is_empty() { None } else if statements.len() == 1 { - self.transpile_statement(&statements[0]) + self.transpile_statement(&statements[0], handler) + .unwrap_or_else(|err| { + errors.push(err); + None + }) .map(|cmd| Execute::Run(Box::new(cmd))) } else { let commands = statements .iter() - .filter_map(|statement| self.transpile_statement(statement)) + .filter_map(|statement| { + self.transpile_statement(statement, handler) + .unwrap_or_else(|err| { + errors.push(err); + None + }) + }) .collect(); Some(Execute::Runs(commands)) } }) .map(Box::new); + if !errors.is_empty() { + return Err(errors.remove(0)); + } + if statements.is_empty() { if el.is_none() { - None + Ok(None) } else { - Some(Command::Execute(Execute::If( + Ok(Some(Command::Execute(Execute::If( datapack::Condition::from(cond), Box::new(Execute::Runs(Vec::new())), el, - ))) + )))) } } else { let run = if statements.len() > 1 { let commands = statements .iter() - .filter_map(|statement| self.transpile_statement(statement)) + .filter_map(|statement| { + self.transpile_statement(statement, handler) + .unwrap_or_else(|err| { + errors.push(err); + None + }) + }) .collect(); - Execute::Runs(commands) + Some(Execute::Runs(commands)) } else { - Execute::Run(Box::new(self.transpile_statement(&statements[0])?)) + self.transpile_statement(&statements[0], handler)? + .map(|cmd| Execute::Run(Box::new(cmd))) }; - Some(Command::Execute(Execute::If( - datapack::Condition::from(cond), - Box::new(run), - el, - ))) + Ok(run.map(|run| { + Command::Execute(Execute::If( + datapack::Condition::from(cond), + Box::new(run), + el, + )) + })) } } - fn transpile_primary_expression(&mut self, primary: &Primary) -> Option { + fn transpile_primary_expression( + &mut self, + primary: &Primary, + handler: &impl Handler, + ) -> TranspileResult { match primary { Primary::FunctionCall(func) => { let identifier = func.identifier().span(); - let identifier = identifier.str(); - let location = self.get_or_transpile_function(identifier)?; - Some(Command::Raw(format!("function {location}"))) + let identifier_name = identifier.str(); + let location = self.get_or_transpile_function(identifier_name, handler)?; + Ok(Command::Raw(format!("function {location}"))) } } }