From 2ed6e56ef13851406aa40b277619b12c51b616f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Fri, 5 Apr 2024 12:59:21 +0200 Subject: [PATCH] Refactor transpile module and error handling --- src/base/error.rs | 4 +- src/lib.rs | 12 ++-- src/transpile/error.rs | 4 +- src/transpile/mod.rs | 2 + src/transpile/transpiler.rs | 120 ++++++++++++++++++++++++------------ 5 files changed, 96 insertions(+), 46 deletions(-) diff --git a/src/base/error.rs b/src/base/error.rs index 2eefe7d..160855e 100644 --- a/src/base/error.rs +++ b/src/base/error.rs @@ -10,8 +10,8 @@ pub enum Error { TokenizeError(#[from] crate::lexical::token::TokenizeError), #[error("An error occurred while parsing the source code.")] ParseError(#[from] crate::syntax::error::Error), - #[error("An error occurred while compiling the source code.")] - CompileError(#[from] crate::transpile::error::TranspileError), + #[error("An error occurred while transpiling the source code.")] + TranspileError(#[from] crate::transpile::error::TranspileError), #[error("An error occurred")] Other(&'static str), } diff --git a/src/lib.rs b/src/lib.rs index 7e7aabc..494be49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ use std::{cell::Cell, fmt::Display, path::Path}; use base::{source_file::SourceFile, Handler, Result}; use syntax::syntax_tree::program::Program; + +#[cfg(feature = "shulkerbox")] use transpile::transpiler::Transpiler; #[cfg(feature = "shulkerbox")] @@ -103,8 +105,9 @@ pub fn transpile(path: &Path) -> Result { )); } - let mut transpiler = Transpiler::new(); - let datapack = transpiler.transpile(&program, &printer)?; + let mut transpiler = Transpiler::new("shulkerscript-pack", 27); + transpiler.transpile(&program, &printer)?; + let datapack = transpiler.into_datapack(); Ok(datapack) } @@ -144,8 +147,9 @@ pub fn compile(path: &Path) -> Result { // println!("program: {program:#?}"); - let mut transpiler = Transpiler::new(); - let datapack = transpiler.transpile(&program, &printer)?; + let mut transpiler = Transpiler::new("shulkerscript-pack", 27); + transpiler.transpile(&program, &printer)?; + let datapack = transpiler.into_datapack(); // println!("datapack: {datapack:#?}"); diff --git a/src/transpile/error.rs b/src/transpile/error.rs index 5bc69d0..e723a27 100644 --- a/src/transpile/error.rs +++ b/src/transpile/error.rs @@ -1,6 +1,6 @@ -//! Errors that can occur during compilation. +//! Errors that can occur during transpilation. -/// Errors that can occur during compilation. +/// Errors that can occur during transpilation. #[allow(clippy::module_name_repetitions, missing_docs)] #[derive(Debug, thiserror::Error, Clone, Copy)] pub enum TranspileError { diff --git a/src/transpile/mod.rs b/src/transpile/mod.rs index d2ba5e0..23b4849 100644 --- a/src/transpile/mod.rs +++ b/src/transpile/mod.rs @@ -1,6 +1,8 @@ //! The transpile module is responsible for transpiling the abstract syntax tree into a data pack. #[doc(hidden)] +#[cfg(feature = "shulkerbox")] pub mod conversions; pub mod error; +#[cfg(feature = "shulkerbox")] pub mod transpiler; diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 2c5b992..9d8ed0b 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -12,21 +12,37 @@ use crate::{ use super::error::{self, TranspileError}; /// A transpiler for `ShulkerScript`. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct Transpiler { - functions: HashMap, AnnotationMap)>, + datapack: shulkerbox::datapack::Datapack, + functions: HashMap, + function_locations: HashMap, +} + +#[derive(Debug, Clone)] +struct FunctionData { + namespace: String, + statements: Vec, + annotations: HashMap>, } -type AnnotationMap = HashMap>; impl Transpiler { /// Creates a new transpiler. #[must_use] - pub fn new() -> Self { + pub fn new(pack_name: &str, pack_format: u8) -> Self { Self { + datapack: shulkerbox::datapack::Datapack::new(pack_name, pack_format), functions: HashMap::new(), + function_locations: HashMap::new(), } } + /// Consumes the transpiler and returns the resulting datapack. + #[must_use] + pub fn into_datapack(self) -> Datapack { + self.datapack + } + /// Transpiles the given program. /// /// # Errors @@ -35,49 +51,77 @@ impl Transpiler { &mut self, program: &Program, handler: &impl Handler, - ) -> Result { + ) -> Result<(), TranspileError> { for declaration in program.declarations() { - match declaration { - Declaration::Function(function) => { - let name = function.identifier().span().str().to_string(); - let statements = function.block().statements().clone(); - let annotations = function - .annotations() - .iter() - .map(|annotation| { - let key = annotation.identifier(); - let value = annotation.value(); - ( - key.span().str().to_string(), - value.as_ref().map(|(_, ref v)| v.str_content().to_string()), - ) - }) - .collect(); - self.functions.insert(name, (statements, annotations)) - } - }; + self.transpile_declaration(declaration); } - let Some((main_function, main_annotations)) = self.functions.get("main") else { + self.get_or_transpile_function("main").ok_or_else(|| { handler.receive(TranspileError::MissingMainFunction); - return Err(TranspileError::MissingMainFunction); + TranspileError::MissingMainFunction + })?; + + Ok(()) + } + + /// Transpiles the given declaration. + fn transpile_declaration(&mut self, declaration: &Declaration) { + match declaration { + Declaration::Function(function) => { + let name = function.identifier().span().str().to_string(); + let statements = function.block().statements().clone(); + let annotations = function + .annotations() + .iter() + .map(|annotation| { + let key = annotation.identifier(); + let value = annotation.value(); + ( + key.span().str().to_string(), + value.as_ref().map(|(_, ref v)| v.str_content().to_string()), + ) + }) + .collect(); + self.functions.insert( + name, + FunctionData { + namespace: "shulkerscript".to_string(), + statements, + annotations, + }, + ); + } }; + } - let main_commands = compile_function(main_function); - // TODO: change this - let mut datapack = shulkerbox::datapack::Datapack::new("shulkerscript-pack", 27); - let namespace = datapack.namespace_mut("shulkerscript"); - let main_function = namespace.function_mut("main"); - main_function.get_commands_mut().extend(main_commands); + /// Gets the function at the given path, or transpiles it if it hasn't been transpiled yet. + fn get_or_transpile_function(&mut self, path: &str) -> Option<&str> { + let already_transpiled = self.function_locations.get(path); + if already_transpiled.is_none() { + let function_data = self.functions.get(path)?; + let commands = compile_function(&function_data.statements); - if main_annotations.contains_key("tick") { - datapack.add_tick("shulkerscript:main"); - } - if main_annotations.contains_key("load") { - datapack.add_load("shulkerscript:main"); + let function = self + .datapack + .namespace_mut(&function_data.namespace) + .function_mut(path); + function.get_commands_mut().extend(commands); + + let function_location = + format!("{namespace}:{path}", namespace = function_data.namespace); + + if function_data.annotations.contains_key("tick") { + self.datapack.add_tick(&function_location); + } + if function_data.annotations.contains_key("load") { + self.datapack.add_load(&function_location); + } + + self.function_locations + .insert(path.to_string(), function_location); } - Ok(datapack) + self.function_locations.get(path).map(String::as_str) } }