2024-03-27 19:27:11 +01:00
|
|
|
//! The `ShulkerScript` language.
|
|
|
|
//!
|
|
|
|
//! `ShulkerScript` is a simple, imperative scripting language for creating Minecraft data packs.
|
|
|
|
|
|
|
|
#![deny(
|
|
|
|
missing_debug_implementations,
|
|
|
|
missing_copy_implementations,
|
|
|
|
clippy::nursery,
|
|
|
|
rustdoc::broken_intra_doc_links,
|
|
|
|
clippy::missing_errors_doc
|
|
|
|
)]
|
2024-03-27 21:39:56 +01:00
|
|
|
#![warn(missing_docs, clippy::all, clippy::pedantic)]
|
2024-03-27 19:27:11 +01:00
|
|
|
#![allow(clippy::missing_panics_doc, clippy::missing_const_for_fn)]
|
|
|
|
|
2024-04-06 14:42:44 +02:00
|
|
|
pub use shulkerbox;
|
|
|
|
|
2024-03-27 19:27:11 +01:00
|
|
|
pub mod base;
|
|
|
|
pub mod lexical;
|
2024-03-27 21:39:56 +01:00
|
|
|
pub mod syntax;
|
2024-04-03 01:27:02 +02:00
|
|
|
pub mod transpile;
|
2024-03-27 19:27:11 +01:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
use std::path::Path;
|
2024-06-10 08:41:24 +02:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
use base::{source_file::SourceFile, Error, FileProvider, Handler, Result};
|
|
|
|
use syntax::{parser::Parser, syntax_tree::program::ProgramFile};
|
2024-04-05 12:59:21 +02:00
|
|
|
|
|
|
|
#[cfg(feature = "shulkerbox")]
|
2024-06-10 08:41:24 +02:00
|
|
|
use shulkerbox::{datapack::Datapack, virtual_fs::VFolder};
|
2024-04-03 00:45:34 +02:00
|
|
|
|
2024-06-10 08:41:24 +02:00
|
|
|
use crate::lexical::token_stream::TokenStream;
|
2024-03-27 19:27:11 +01:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
/// Converts the given source code to tokens and returns a token stream.
|
2024-04-01 20:42:38 +02:00
|
|
|
///
|
|
|
|
/// # Errors
|
2024-08-23 00:06:58 +02:00
|
|
|
/// - If an error occurs while loading the [`SourceFile`].
|
|
|
|
pub fn tokenize(
|
|
|
|
handler: &impl Handler<base::Error>,
|
|
|
|
file_provider: &impl FileProvider,
|
|
|
|
path: &Path,
|
|
|
|
) -> Result<TokenStream> {
|
|
|
|
tracing::info!("Tokenizing the source code at path: {}", path.display());
|
|
|
|
|
|
|
|
let source_file = SourceFile::load(path, file_provider)?;
|
2024-04-01 20:42:38 +02:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
Ok(TokenStream::tokenize(&source_file, handler))
|
2024-04-01 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
/// Parses the given source code and returns the AST of the program.
|
2024-04-01 20:42:38 +02:00
|
|
|
///
|
|
|
|
/// # Errors
|
2024-08-23 00:06:58 +02:00
|
|
|
/// - If an error occurs during [`tokenize()`].
|
2024-04-01 20:42:38 +02:00
|
|
|
/// - If an error occurs while parsing the source code.
|
2024-08-23 00:06:58 +02:00
|
|
|
pub fn parse(
|
|
|
|
handler: &impl Handler<base::Error>,
|
|
|
|
file_provider: &impl FileProvider,
|
|
|
|
path: &Path,
|
|
|
|
) -> Result<ProgramFile> {
|
|
|
|
let tokens = tokenize(handler, file_provider, path)?;
|
|
|
|
|
|
|
|
if handler.has_received() {
|
|
|
|
return Err(Error::Other(
|
|
|
|
"An error occurred while tokenizing the source code.",
|
|
|
|
));
|
|
|
|
}
|
2024-04-01 20:42:38 +02:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
tracing::info!("Parsing the source code at path: {}", path.display());
|
|
|
|
|
|
|
|
let mut parser = Parser::new(&tokens);
|
|
|
|
let program = parser.parse_program(handler).ok_or(Error::Other(
|
|
|
|
"An error occured while parsing the source code.",
|
|
|
|
))?;
|
|
|
|
|
|
|
|
if handler.has_received() {
|
|
|
|
return Err(Error::Other(
|
|
|
|
"An error occurred while parsing the source code.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(program)
|
2024-04-01 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
2024-04-03 00:45:34 +02:00
|
|
|
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
|
|
|
///
|
2024-06-10 08:41:24 +02:00
|
|
|
/// # Parameters:
|
2024-08-23 00:06:58 +02:00
|
|
|
/// - `script_paths`: A list of tuples containing the identifier and the path of each script file.
|
2024-06-10 08:41:24 +02:00
|
|
|
///
|
2024-04-03 00:45:34 +02:00
|
|
|
/// # Errors
|
2024-08-23 00:06:58 +02:00
|
|
|
/// - If an error occurs during [`parse()`]
|
2024-04-03 00:45:34 +02:00
|
|
|
/// - If an error occurs while transpiling the source code.
|
|
|
|
#[cfg(feature = "shulkerbox")]
|
2024-06-24 21:48:40 +02:00
|
|
|
pub fn transpile<F, P>(
|
2024-08-23 00:06:58 +02:00
|
|
|
handler: &impl Handler<base::Error>,
|
2024-06-24 21:48:40 +02:00
|
|
|
file_provider: &F,
|
|
|
|
pack_format: u8,
|
|
|
|
script_paths: &[(String, P)],
|
|
|
|
) -> Result<Datapack>
|
2024-06-09 21:22:48 +02:00
|
|
|
where
|
2024-06-21 19:22:24 +02:00
|
|
|
F: FileProvider,
|
2024-06-09 21:22:48 +02:00
|
|
|
P: AsRef<Path>,
|
|
|
|
{
|
2024-08-23 00:06:58 +02:00
|
|
|
use transpile::Transpiler;
|
|
|
|
|
|
|
|
let programs = script_paths
|
|
|
|
.iter()
|
|
|
|
.map(|(program_identifier, path)| {
|
|
|
|
let program = parse(handler, file_provider, path.as_ref())?;
|
|
|
|
|
|
|
|
Ok((program_identifier, program))
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
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::<Vec<_>>();
|
|
|
|
|
|
|
|
tracing::info!("Transpiling the source code.");
|
|
|
|
|
|
|
|
let mut transpiler = Transpiler::new(pack_format);
|
|
|
|
transpiler.transpile(&programs, handler)?;
|
|
|
|
let datapack = transpiler.into_datapack();
|
|
|
|
|
|
|
|
if handler.has_received() {
|
|
|
|
return Err(Error::Other(
|
|
|
|
"An error occurred while transpiling the source code.",
|
|
|
|
));
|
|
|
|
}
|
2024-04-03 00:45:34 +02:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
Ok(datapack)
|
2024-04-03 00:45:34 +02:00
|
|
|
}
|
|
|
|
|
2024-03-27 19:27:11 +01:00
|
|
|
/// Compiles the given source code.
|
|
|
|
///
|
2024-06-10 08:41:24 +02:00
|
|
|
/// # Parameters:
|
2024-08-23 00:06:58 +02:00
|
|
|
/// - `script_paths`: A list of tuples containing the identifier and the path of each script file.
|
2024-06-10 08:41:24 +02:00
|
|
|
///
|
2024-03-27 19:27:11 +01:00
|
|
|
/// # Errors
|
2024-08-23 00:06:58 +02:00
|
|
|
/// - If an error occurs during [`transpile()`]
|
2024-04-03 00:45:34 +02:00
|
|
|
#[cfg(feature = "shulkerbox")]
|
2024-06-24 21:48:40 +02:00
|
|
|
pub fn compile<F, P>(
|
2024-08-23 00:06:58 +02:00
|
|
|
handler: &impl Handler<base::Error>,
|
2024-06-24 21:48:40 +02:00
|
|
|
file_provider: &F,
|
|
|
|
pack_format: u8,
|
|
|
|
script_paths: &[(String, P)],
|
|
|
|
) -> Result<VFolder>
|
2024-06-09 21:22:48 +02:00
|
|
|
where
|
2024-06-21 19:22:24 +02:00
|
|
|
F: FileProvider,
|
2024-06-09 21:22:48 +02:00
|
|
|
P: AsRef<Path>,
|
|
|
|
{
|
2024-08-23 00:06:58 +02:00
|
|
|
use shulkerbox::prelude::CompileOptions;
|
2024-03-27 19:27:11 +01:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
let datapack = transpile(handler, file_provider, pack_format, script_paths)?;
|
2024-03-27 19:27:11 +01:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
tracing::info!("Compiling the source code.");
|
2024-03-27 19:27:11 +01:00
|
|
|
|
2024-08-23 00:06:58 +02:00
|
|
|
Ok(datapack.compile(&CompileOptions::default()))
|
2024-03-27 19:27:11 +01:00
|
|
|
}
|