From 0d93faf87fe1e888a2080dbe0c573b9ed3843671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:17:17 +0100 Subject: [PATCH] Add ShulkerScript basic compiler for only main function and literal commands --- Cargo.toml | 1 + src/base/error.rs | 2 ++ src/compile/compiler.rs | 76 +++++++++++++++++++++++++++++++++++++++++ src/compile/error.rs | 9 +++++ src/compile/mod.rs | 4 +++ src/lib.rs | 14 +++++--- 6 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/compile/compiler.rs create mode 100644 src/compile/error.rs create mode 100644 src/compile/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 0acfe81..8934748 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ colored = "2.1.0" derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] } enum-as-inner = "0.6.0" getset = "0.1.2" +shulkerbox = { path = "../shulkerbox" } strum = { version = "0.26.2", features = ["derive"] } strum_macros = "0.26.2" thiserror = "1.0.58" diff --git a/src/base/error.rs b/src/base/error.rs index 2e3dfe3..de12dbf 100644 --- a/src/base/error.rs +++ b/src/base/error.rs @@ -10,6 +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::compile::error::CompileError), #[error("An error occurred")] Other(&'static str), } diff --git a/src/compile/compiler.rs b/src/compile/compiler.rs new file mode 100644 index 0000000..e81388a --- /dev/null +++ b/src/compile/compiler.rs @@ -0,0 +1,76 @@ +//! Compiler for `ShulkerScript` + +use std::collections::HashMap; + +use shulkerbox::datapack::{Command, Datapack}; + +use crate::{ + base::{source_file::SourceElement, Handler}, + syntax::syntax_tree::{declaration::Declaration, program::Program, statement::Statement}, +}; + +use super::error::{self, CompileError}; + +/// A compiler for `ShulkerScript`. +#[derive(Debug, Clone, Default)] +pub struct Compiler { + functions: HashMap>, +} + +impl Compiler { + /// Creates a new compiler. + #[must_use] + pub fn new() -> Self { + Self { + functions: HashMap::new(), + } + } + + /// Compiles the given program. + /// + /// # Errors + /// - [`CompileError::MissingMainFunction`] If the main function is missing. + pub fn compile( + &mut self, + program: &Program, + handler: &impl Handler, + ) -> Result { + for declaration in program.declarations() { + match declaration { + Declaration::Function(function) => self.functions.insert( + function.identifier().span().str().to_string(), + function.block().statements().clone(), + ), + }; + } + + let Some(main_function) = self.functions.get("main") else { + handler.receive(CompileError::MissingMainFunction); + return Err(CompileError::MissingMainFunction); + }; + + 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); + + Ok(datapack) + } +} + +fn compile_function(statements: &[Statement]) -> Vec { + let mut commands = Vec::new(); + for statement in statements { + match statement { + Statement::LiteralCommand(literal_command) => { + commands.push(literal_command.clean_command().into()); + } + Statement::Block(_) => { + unreachable!("Only literal commands are allowed in functions at this time.") + } + } + } + commands +} diff --git a/src/compile/error.rs b/src/compile/error.rs new file mode 100644 index 0000000..be1eeab --- /dev/null +++ b/src/compile/error.rs @@ -0,0 +1,9 @@ +//! Errors that can occur during compilation. + +/// Errors that can occur during compilation. +#[allow(clippy::module_name_repetitions, missing_docs)] +#[derive(Debug, thiserror::Error, Clone, Copy)] +pub enum CompileError { + #[error("No main function was found in the source code.")] + MissingMainFunction, +} diff --git a/src/compile/mod.rs b/src/compile/mod.rs new file mode 100644 index 0000000..a234824 --- /dev/null +++ b/src/compile/mod.rs @@ -0,0 +1,4 @@ +//! The compile module is responsible for compiling the abstract syntax tree into a data pack. + +pub mod compiler; +pub mod error; diff --git a/src/lib.rs b/src/lib.rs index fe0919d..bfcddf3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,12 +13,15 @@ #![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)] pub mod base; +pub mod compile; pub mod lexical; pub mod syntax; use std::{cell::Cell, fmt::Display, path::PathBuf}; use base::{source_file::SourceFile, Handler, Result}; +use compile::compiler::Compiler; +use shulkerbox::{util::compile::CompileOptions, virtual_fs::VFolder}; use crate::{base::Error, lexical::token_stream::TokenStream, syntax::parser::Parser}; @@ -26,7 +29,7 @@ use crate::{base::Error, lexical::token_stream::TokenStream, syntax::parser::Par /// /// # Errors /// - If an error occurs while reading the file. -pub fn compile(path: PathBuf) -> Result<()> { +pub fn compile(path: PathBuf) -> Result { let source_file = SourceFile::load(path)?; let printer = Printer::new(); @@ -40,11 +43,14 @@ pub fn compile(path: PathBuf) -> Result<()> { } let mut parser = Parser::new(&tokens); - let result = parser.parse_program(&printer).ok_or(Error::Other( + let program = parser.parse_program(&printer).ok_or(Error::Other( "An error occured while parsing the source code.", ))?; - println!("result: {result:#?}"); + // println!("result: {result:#?}"); + + let mut compiler = Compiler::new(); + let datapack = compiler.compile(&program, &printer)?; if printer.has_printed() { return Err(Error::Other( @@ -52,7 +58,7 @@ pub fn compile(path: PathBuf) -> Result<()> { )); } - Ok(()) + Ok(datapack.compile(&CompileOptions::default())) } struct Printer {