allow custom handlers instead of only printer
This commit is contained in:
parent
bc25da6f2c
commit
83d5f329f9
|
@ -1,3 +1,5 @@
|
||||||
|
use std::{cell::Cell, fmt::Display};
|
||||||
|
|
||||||
/// Represents a trait responsible for handling diagnostics in the interpreter.
|
/// Represents a trait responsible for handling diagnostics in the interpreter.
|
||||||
pub trait Handler<T> {
|
pub trait Handler<T> {
|
||||||
/// Receive an error and handles it.
|
/// Receive an error and handles it.
|
||||||
|
@ -6,13 +8,89 @@ pub trait Handler<T> {
|
||||||
fn has_received(&self) -> bool;
|
fn has_received(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is a struct that implements [`Handler`] trait by doing nothing with the errors.
|
/// Is a struct that implements [`Handler`] trait by doing nothing with the errors and
|
||||||
|
/// never signifying that it has received a message.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct DummyHandler;
|
pub struct VoidHandler;
|
||||||
|
|
||||||
impl<T> Handler<T> for DummyHandler {
|
impl<T> Handler<T> for VoidHandler {
|
||||||
fn receive<E: Into<T>>(&self, _error: E) {}
|
fn receive<E: Into<T>>(&self, _error: E) {}
|
||||||
fn has_received(&self) -> bool {
|
fn has_received(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A handler that does not print the error to the standard error stream.
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct SilentHandler {
|
||||||
|
received: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SilentHandler {
|
||||||
|
/// Creates a new [`SilentHandler`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Handler<T> for SilentHandler {
|
||||||
|
fn receive<E: Into<T>>(&self, _error: E) {
|
||||||
|
self.received.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_received(&self) -> bool {
|
||||||
|
self.received.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple error handler that prints the error to the standard error stream.
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct PrintHandler {
|
||||||
|
printed: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrintHandler {
|
||||||
|
/// Creates a new [`PrintHandler`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Handler<T> for PrintHandler {
|
||||||
|
fn receive<E: Into<T>>(&self, error: E) {
|
||||||
|
eprintln!("{}", error.into());
|
||||||
|
self.printed.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_received(&self) -> bool {
|
||||||
|
self.printed.get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_void_handler() {
|
||||||
|
let handler = VoidHandler;
|
||||||
|
Handler::<&str>::receive(&handler, "error");
|
||||||
|
assert!(!Handler::<&str>::has_received(&handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_silent_handler() {
|
||||||
|
let handler = SilentHandler::new();
|
||||||
|
Handler::<&str>::receive(&handler, "error");
|
||||||
|
assert!(Handler::<&str>::has_received(&handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_print_handler() {
|
||||||
|
let handler = PrintHandler::new();
|
||||||
|
Handler::<&str>::receive(&handler, "error");
|
||||||
|
assert!(Handler::<&str>::has_received(&handler));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,12 +4,14 @@
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("An error occurred while working with Input/Output.")]
|
#[error("An error occurred while working with Input/Output.")]
|
||||||
IoError(String),
|
IoError(String),
|
||||||
|
#[error("An error occurred while lexing the source code.")]
|
||||||
|
LexicalError(#[from] crate::lexical::Error),
|
||||||
#[error("An error occured while tokenizing the source code.")]
|
#[error("An error occured while tokenizing the source code.")]
|
||||||
TokenizeError(#[from] crate::lexical::token::TokenizeError),
|
TokenizeError(#[from] crate::lexical::token::TokenizeError),
|
||||||
#[error("An error occurred while parsing the source code.")]
|
#[error("An error occurred while parsing the source code.")]
|
||||||
ParseError(#[from] crate::syntax::error::Error),
|
ParseError(#[from] crate::syntax::error::Error),
|
||||||
#[error("An error occurred while transpiling the source code.")]
|
#[error("An error occurred while transpiling the source code.")]
|
||||||
TranspileError(#[from] crate::transpile::error::TranspileError),
|
TranspileError(#[from] crate::transpile::TranspileError),
|
||||||
#[error("An error occurred")]
|
#[error("An error occurred")]
|
||||||
Other(&'static str),
|
Other(&'static str),
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod error;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
|
||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
pub use diagnostic::{DummyHandler, Handler};
|
pub use diagnostic::{Handler, PrintHandler, SilentHandler, VoidHandler};
|
||||||
|
|
||||||
mod file_provider;
|
mod file_provider;
|
||||||
pub use file_provider::{FileProvider, FsProvider};
|
pub use file_provider::{FileProvider, FsProvider};
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::{borrow::Cow, collections::HashMap, fmt::Display, str::FromStr, sync::OnceLock};
|
use std::{borrow::Cow, collections::HashMap, fmt::Display, str::FromStr, sync::OnceLock};
|
||||||
|
|
||||||
use crate::base::{
|
use crate::base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, SourceIterator, Span},
|
source_file::{SourceElement, SourceIterator, Span},
|
||||||
Handler,
|
Handler,
|
||||||
};
|
};
|
||||||
|
@ -11,7 +12,7 @@ use enum_as_inner::EnumAsInner;
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
use super::{error::UnterminatedDelimitedComment, Error};
|
use super::error::{self, UnterminatedDelimitedComment};
|
||||||
|
|
||||||
/// Is an enumeration representing keywords in shulkerscript.
|
/// Is an enumeration representing keywords in shulkerscript.
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -439,7 +440,7 @@ impl Token {
|
||||||
start: usize,
|
start: usize,
|
||||||
character: char,
|
character: char,
|
||||||
prev_token: Option<&Self>,
|
prev_token: Option<&Self>,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<Self, TokenizeError> {
|
) -> Result<Self, TokenizeError> {
|
||||||
// Single line comment
|
// Single line comment
|
||||||
if let Some((_, '/')) = iter.peek() {
|
if let Some((_, '/')) = iter.peek() {
|
||||||
|
@ -503,9 +504,9 @@ impl Token {
|
||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
} else {
|
} else {
|
||||||
handler.receive(UnterminatedDelimitedComment {
|
handler.receive(error::Error::from(UnterminatedDelimitedComment {
|
||||||
span: Span::new(iter.source_file().clone(), start, start + 2).unwrap(),
|
span: Span::new(iter.source_file().clone(), start, start + 2).unwrap(),
|
||||||
});
|
}));
|
||||||
return Err(TokenizeError::FatalLexicalError);
|
return Err(TokenizeError::FatalLexicalError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,7 +577,7 @@ impl Token {
|
||||||
/// - [`TokenizeError::FatalLexicalError`] - A fatal lexical error occurred.
|
/// - [`TokenizeError::FatalLexicalError`] - A fatal lexical error occurred.
|
||||||
pub fn tokenize(
|
pub fn tokenize(
|
||||||
iter: &mut SourceIterator,
|
iter: &mut SourceIterator,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
prev_token: Option<&Self>,
|
prev_token: Option<&Self>,
|
||||||
) -> Result<Self, TokenizeError> {
|
) -> Result<Self, TokenizeError> {
|
||||||
// Gets the first character
|
// Gets the first character
|
||||||
|
|
|
@ -6,6 +6,7 @@ use derive_more::{Deref, From};
|
||||||
use enum_as_inner::EnumAsInner;
|
use enum_as_inner::EnumAsInner;
|
||||||
|
|
||||||
use crate::base::{
|
use crate::base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, SourceFile, Span},
|
source_file::{SourceElement, SourceFile, Span},
|
||||||
Handler,
|
Handler,
|
||||||
};
|
};
|
||||||
|
@ -46,7 +47,7 @@ impl TokenStream {
|
||||||
/// encountered during tokenization.
|
/// encountered during tokenization.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[tracing::instrument(level = "debug", skip_all, fields(source_file = %source_file.path().display()))]
|
#[tracing::instrument(level = "debug", skip_all, fields(source_file = %source_file.path().display()))]
|
||||||
pub fn tokenize(source_file: &Arc<SourceFile>, handler: &impl Handler<error::Error>) -> Self {
|
pub fn tokenize(source_file: &Arc<SourceFile>, handler: &impl Handler<base::Error>) -> Self {
|
||||||
// The list of token trees that will be returned.
|
// The list of token trees that will be returned.
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
let mut source_file_iterator = source_file.iter();
|
let mut source_file_iterator = source_file.iter();
|
||||||
|
@ -81,7 +82,7 @@ impl TokenStream {
|
||||||
/// Handles a token.
|
/// Handles a token.
|
||||||
fn handle_token(
|
fn handle_token(
|
||||||
tokens: &mut Vec<Token>,
|
tokens: &mut Vec<Token>,
|
||||||
handler: &impl Handler<error::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<TokenTree> {
|
) -> Option<TokenTree> {
|
||||||
tokens
|
tokens
|
||||||
.pop()
|
.pop()
|
||||||
|
@ -92,7 +93,7 @@ impl TokenStream {
|
||||||
fn handle_popped_token(
|
fn handle_popped_token(
|
||||||
tokens: &mut Vec<Token>,
|
tokens: &mut Vec<Token>,
|
||||||
popped_token: Token,
|
popped_token: Token,
|
||||||
handler: &impl Handler<error::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<TokenTree> {
|
) -> Option<TokenTree> {
|
||||||
match popped_token {
|
match popped_token {
|
||||||
Token::Punctuation(punc) if punc.punctuation == '{' => {
|
Token::Punctuation(punc) if punc.punctuation == '{' => {
|
||||||
|
@ -116,7 +117,7 @@ impl TokenStream {
|
||||||
tokens: &mut Vec<Token>,
|
tokens: &mut Vec<Token>,
|
||||||
open: Punctuation,
|
open: Punctuation,
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
handler: &impl Handler<error::Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<Delimited> {
|
) -> Option<Delimited> {
|
||||||
let mut token_trees = Vec::new();
|
let mut token_trees = Vec::new();
|
||||||
|
|
||||||
|
|
141
src/lib.rs
141
src/lib.rs
|
@ -19,56 +19,77 @@ pub mod lexical;
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
pub mod transpile;
|
pub mod transpile;
|
||||||
|
|
||||||
mod public_helpers;
|
use std::path::Path;
|
||||||
|
|
||||||
use std::{cell::Cell, fmt::Display, path::Path};
|
use base::{source_file::SourceFile, Error, FileProvider, Handler, Result};
|
||||||
|
use syntax::{parser::Parser, syntax_tree::program::ProgramFile};
|
||||||
use base::{FileProvider, Handler, Result};
|
|
||||||
use syntax::syntax_tree::program::ProgramFile;
|
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
use shulkerbox::{datapack::Datapack, virtual_fs::VFolder};
|
use shulkerbox::{datapack::Datapack, virtual_fs::VFolder};
|
||||||
|
|
||||||
use crate::lexical::token_stream::TokenStream;
|
use crate::lexical::token_stream::TokenStream;
|
||||||
|
|
||||||
/// Converts the given source code to tokens.
|
/// Converts the given source code to tokens and returns a token stream.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs while loading the [`SourceFile`].
|
||||||
pub fn tokenize<F>(file_provider: &F, path: &Path) -> Result<TokenStream>
|
pub fn tokenize(
|
||||||
where
|
handler: &impl Handler<base::Error>,
|
||||||
F: FileProvider,
|
file_provider: &impl FileProvider,
|
||||||
{
|
path: &Path,
|
||||||
let printer = Printer::new();
|
) -> Result<TokenStream> {
|
||||||
|
tracing::info!("Tokenizing the source code at path: {}", path.display());
|
||||||
|
|
||||||
public_helpers::tokenize(&printer, file_provider, path)
|
let source_file = SourceFile::load(path, file_provider)?;
|
||||||
|
|
||||||
|
Ok(TokenStream::tokenize(&source_file, handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the given source code.
|
/// Parses the given source code and returns the AST of the program.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs during [`tokenize()`].
|
||||||
/// - If an error occurs while parsing the source code.
|
/// - If an error occurs while parsing the source code.
|
||||||
pub fn parse<F>(file_provider: &F, path: &Path) -> Result<ProgramFile>
|
pub fn parse(
|
||||||
where
|
handler: &impl Handler<base::Error>,
|
||||||
F: FileProvider,
|
file_provider: &impl FileProvider,
|
||||||
{
|
path: &Path,
|
||||||
let printer = Printer::new();
|
) -> Result<ProgramFile> {
|
||||||
|
let tokens = tokenize(handler, file_provider, path)?;
|
||||||
|
|
||||||
public_helpers::parse(&printer, file_provider, path)
|
if handler.has_received() {
|
||||||
|
return Err(Error::Other(
|
||||||
|
"An error occurred while tokenizing the source code.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
||||||
///
|
///
|
||||||
/// # Parameters:
|
/// # Parameters:
|
||||||
/// - `script_paths`: A list of tuples containing the identifier of the program and the path to the script.
|
/// - `script_paths`: A list of tuples containing the identifier and the path of each script file.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs during [`parse()`]
|
||||||
/// - If an error occurs while parsing the source code.
|
|
||||||
/// - If an error occurs while transpiling the source code.
|
/// - If an error occurs while transpiling the source code.
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub fn transpile<F, P>(
|
pub fn transpile<F, P>(
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
file_provider: &F,
|
file_provider: &F,
|
||||||
pack_format: u8,
|
pack_format: u8,
|
||||||
script_paths: &[(String, P)],
|
script_paths: &[(String, P)],
|
||||||
|
@ -77,22 +98,50 @@ where
|
||||||
F: FileProvider,
|
F: FileProvider,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let printer = Printer::new();
|
use transpile::Transpiler;
|
||||||
|
|
||||||
public_helpers::transpile(&printer, file_provider, pack_format, script_paths)
|
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.",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(datapack)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compiles the given source code.
|
/// Compiles the given source code.
|
||||||
///
|
///
|
||||||
/// # Parameters:
|
/// # Parameters:
|
||||||
/// - `script_paths`: A list of tuples containing the identifier of the program and the path to the script.
|
/// - `script_paths`: A list of tuples containing the identifier and the path of each script file.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs during [`transpile()`]
|
||||||
/// - If an error occurs while parsing the source code.
|
|
||||||
/// - If an error occurs while transpiling the source code.
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub fn compile<F, P>(
|
pub fn compile<F, P>(
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
file_provider: &F,
|
file_provider: &F,
|
||||||
pack_format: u8,
|
pack_format: u8,
|
||||||
script_paths: &[(String, P)],
|
script_paths: &[(String, P)],
|
||||||
|
@ -101,35 +150,11 @@ where
|
||||||
F: FileProvider,
|
F: FileProvider,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let printer = Printer::new();
|
use shulkerbox::prelude::CompileOptions;
|
||||||
|
|
||||||
public_helpers::compile(&printer, file_provider, pack_format, script_paths)
|
let datapack = transpile(handler, file_provider, pack_format, script_paths)?;
|
||||||
}
|
|
||||||
|
|
||||||
struct Printer {
|
tracing::info!("Compiling the source code.");
|
||||||
printed: Cell<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Printer {
|
Ok(datapack.compile(&CompileOptions::default()))
|
||||||
/// Creates a new [`Printer`].
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
printed: Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_printed(&self) -> bool {
|
|
||||||
self.printed.get()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Display> Handler<T> for Printer {
|
|
||||||
fn receive<E: Into<T>>(&self, error: E) {
|
|
||||||
eprintln!("{}", error.into());
|
|
||||||
self.printed.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_received(&self) -> bool {
|
|
||||||
self.printed.get()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
base::{source_file::SourceFile, Error, FileProvider, Result},
|
|
||||||
lexical::token_stream::TokenStream,
|
|
||||||
syntax::{parser::Parser, syntax_tree::program::ProgramFile},
|
|
||||||
Printer,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
|
||||||
use crate::transpile::transpiler::Transpiler;
|
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
|
||||||
use shulkerbox::{datapack::Datapack, util::compile::CompileOptions, virtual_fs::VFolder};
|
|
||||||
|
|
||||||
/// Tokenizes the source code at the given path.
|
|
||||||
pub fn tokenize(
|
|
||||||
printer: &Printer,
|
|
||||||
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)?;
|
|
||||||
|
|
||||||
Ok(TokenStream::tokenize(&source_file, printer))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses the source code at the given path.
|
|
||||||
pub fn parse(
|
|
||||||
printer: &Printer,
|
|
||||||
file_provider: &impl FileProvider,
|
|
||||||
path: &Path,
|
|
||||||
) -> Result<ProgramFile> {
|
|
||||||
let tokens = tokenize(printer, file_provider, path)?;
|
|
||||||
|
|
||||||
if printer.has_printed() {
|
|
||||||
return Err(Error::Other(
|
|
||||||
"An error occurred while tokenizing the source code.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::info!("Parsing the source code at path: {}", path.display());
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transpiles the source code at the given paths into a shulkerbox [`Datapack`].
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
|
||||||
pub fn transpile<F, P>(
|
|
||||||
printer: &Printer,
|
|
||||||
file_provider: &F,
|
|
||||||
pack_format: u8,
|
|
||||||
script_paths: &[(String, P)],
|
|
||||||
) -> Result<Datapack>
|
|
||||||
where
|
|
||||||
F: FileProvider,
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let programs = script_paths
|
|
||||||
.iter()
|
|
||||||
.map(|(program_identifier, path)| {
|
|
||||||
let program = parse(printer, 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, printer)?;
|
|
||||||
let datapack = transpiler.into_datapack();
|
|
||||||
|
|
||||||
if printer.has_printed() {
|
|
||||||
return Err(Error::Other(
|
|
||||||
"An error occurred while transpiling the source code.",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(datapack)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compiles the source code at the given paths.
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
|
||||||
pub fn compile<F, P>(
|
|
||||||
printer: &Printer,
|
|
||||||
file_provider: &F,
|
|
||||||
pack_format: u8,
|
|
||||||
script_paths: &[(String, P)],
|
|
||||||
) -> Result<VFolder>
|
|
||||||
where
|
|
||||||
F: FileProvider,
|
|
||||||
P: AsRef<Path>,
|
|
||||||
{
|
|
||||||
let datapack = transpile(printer, file_provider, pack_format, script_paths)?;
|
|
||||||
|
|
||||||
tracing::info!("Compiling the source code.");
|
|
||||||
|
|
||||||
Ok(datapack.compile(&CompileOptions::default()))
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ use derive_more::{Deref, DerefMut};
|
||||||
use enum_as_inner::EnumAsInner;
|
use enum_as_inner::EnumAsInner;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::Handler,
|
base::{self, Handler},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, StringLiteral, Token},
|
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, StringLiteral, Token},
|
||||||
token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
|
token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
|
||||||
|
@ -42,7 +42,7 @@ impl<'a> Parser<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
f: impl FnOnce(&mut Self) -> Option<T>,
|
f: impl FnOnce(&mut Self) -> Option<T>,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<DelimitedTree<T>> {
|
) -> Option<DelimitedTree<T>> {
|
||||||
self.current_frame.stop_at_significant();
|
self.current_frame.stop_at_significant();
|
||||||
let raw_token_tree = self
|
let raw_token_tree = self
|
||||||
|
@ -363,7 +363,7 @@ impl<'a> Frame<'a> {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If the next [`Token`] is not an [`Identifier`].
|
/// If the next [`Token`] is not an [`Identifier`].
|
||||||
pub fn parse_identifier(&mut self, handler: &impl Handler<Error>) -> Option<Identifier> {
|
pub fn parse_identifier(&mut self, handler: &impl Handler<base::Error>) -> Option<Identifier> {
|
||||||
match self.next_significant_token() {
|
match self.next_significant_token() {
|
||||||
Reading::Atomic(Token::Identifier(ident)) => Some(ident),
|
Reading::Atomic(Token::Identifier(ident)) => Some(ident),
|
||||||
found => {
|
found => {
|
||||||
|
@ -397,7 +397,10 @@ impl<'a> Frame<'a> {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If the next [`Token`] is not an [`StringLiteral`].
|
/// If the next [`Token`] is not an [`StringLiteral`].
|
||||||
pub fn parse_string_literal(&mut self, handler: &impl Handler<Error>) -> Option<StringLiteral> {
|
pub fn parse_string_literal(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Option<StringLiteral> {
|
||||||
match self.next_significant_token() {
|
match self.next_significant_token() {
|
||||||
Reading::Atomic(Token::StringLiteral(literal)) => Some(literal),
|
Reading::Atomic(Token::StringLiteral(literal)) => Some(literal),
|
||||||
found => {
|
found => {
|
||||||
|
@ -417,7 +420,7 @@ impl<'a> Frame<'a> {
|
||||||
pub fn parse_keyword(
|
pub fn parse_keyword(
|
||||||
&mut self,
|
&mut self,
|
||||||
expected: KeywordKind,
|
expected: KeywordKind,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<Keyword> {
|
) -> Option<Keyword> {
|
||||||
match self.next_significant_token() {
|
match self.next_significant_token() {
|
||||||
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
|
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
|
||||||
|
@ -441,7 +444,7 @@ impl<'a> Frame<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
expected: char,
|
expected: char,
|
||||||
skip_insignificant: bool,
|
skip_insignificant: bool,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<Punctuation> {
|
) -> Option<Punctuation> {
|
||||||
match if skip_insignificant {
|
match if skip_insignificant {
|
||||||
self.next_significant_token()
|
self.next_significant_token()
|
||||||
|
|
|
@ -7,8 +7,9 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
DummyHandler, Handler,
|
VoidHandler, Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Punctuation, StringLiteral, Token},
|
token::{Punctuation, StringLiteral, Token},
|
||||||
|
@ -240,7 +241,7 @@ impl SourceElement for Condition {
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses a [`Condition`].
|
/// Parses a [`Condition`].
|
||||||
pub fn parse_condition(&mut self, handler: &impl Handler<Error>) -> Option<Condition> {
|
pub fn parse_condition(&mut self, handler: &impl Handler<base::Error>) -> Option<Condition> {
|
||||||
let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?);
|
let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?);
|
||||||
let mut expressions = VecDeque::new();
|
let mut expressions = VecDeque::new();
|
||||||
|
|
||||||
|
@ -305,7 +306,7 @@ impl<'a> Parser<'a> {
|
||||||
/// Parses a [`PrimaryCondition`].
|
/// Parses a [`PrimaryCondition`].
|
||||||
pub fn parse_primary_condition(
|
pub fn parse_primary_condition(
|
||||||
&mut self,
|
&mut self,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<PrimaryCondition> {
|
) -> Option<PrimaryCondition> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
// prefixed expression
|
// prefixed expression
|
||||||
|
@ -354,7 +355,7 @@ impl<'a> Parser<'a> {
|
||||||
/// Parses a [`ParenthesizedCondition`].
|
/// Parses a [`ParenthesizedCondition`].
|
||||||
pub fn parse_parenthesized_condition(
|
pub fn parse_parenthesized_condition(
|
||||||
&mut self,
|
&mut self,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ParenthesizedCondition> {
|
) -> Option<ParenthesizedCondition> {
|
||||||
let token_tree = self.step_into(
|
let token_tree = self.step_into(
|
||||||
Delimiter::Parenthesis,
|
Delimiter::Parenthesis,
|
||||||
|
@ -377,11 +378,11 @@ impl<'a> Parser<'a> {
|
||||||
self.try_parse(|parser| match parser.next_significant_token() {
|
self.try_parse(|parser| match parser.next_significant_token() {
|
||||||
Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation {
|
Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation {
|
||||||
'&' => {
|
'&' => {
|
||||||
let b = parser.parse_punctuation('&', false, &DummyHandler)?;
|
let b = parser.parse_punctuation('&', false, &VoidHandler)?;
|
||||||
Some(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
Some(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
||||||
}
|
}
|
||||||
'|' => {
|
'|' => {
|
||||||
let b = parser.parse_punctuation('|', false, &DummyHandler)?;
|
let b = parser.parse_punctuation('|', false, &VoidHandler)?;
|
||||||
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -6,8 +6,9 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
DummyHandler, Handler,
|
Handler, VoidHandler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Identifier, Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
token::{Identifier, Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
|
@ -226,7 +227,7 @@ impl SourceElement for Import {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub fn parse_annotation(&mut self, handler: &impl Handler<Error>) -> Option<Annotation> {
|
pub fn parse_annotation(&mut self, handler: &impl Handler<base::Error>) -> Option<Annotation> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
|
||||||
// eat the pound sign
|
// eat the pound sign
|
||||||
|
@ -280,7 +281,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub fn parse_declaration(&mut self, handler: &impl Handler<Error>) -> Option<Declaration> {
|
pub fn parse_declaration(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<base::Error>,
|
||||||
|
) -> Option<Declaration> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(function_keyword))
|
Reading::Atomic(Token::Keyword(function_keyword))
|
||||||
if function_keyword.keyword == KeywordKind::Function =>
|
if function_keyword.keyword == KeywordKind::Function =>
|
||||||
|
@ -355,7 +359,7 @@ impl<'a> Parser<'a> {
|
||||||
self.try_parse(|parser| parser
|
self.try_parse(|parser| parser
|
||||||
.parse_connected_list(
|
.parse_connected_list(
|
||||||
',',
|
',',
|
||||||
|parser| parser.parse_identifier(&DummyHandler),
|
|parser| parser.parse_identifier(&VoidHandler),
|
||||||
handler,
|
handler,
|
||||||
)
|
)
|
||||||
.map(ImportItems::Named)) // ,
|
.map(ImportItems::Named)) // ,
|
||||||
|
@ -398,7 +402,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_function(&mut self, handler: &impl Handler<Error>) -> Option<Function> {
|
pub fn parse_function(&mut self, handler: &impl Handler<base::Error>) -> Option<Function> {
|
||||||
if let Reading::Atomic(Token::Keyword(function_keyword)) = self.stop_at_significant() {
|
if let Reading::Atomic(Token::Keyword(function_keyword)) = self.stop_at_significant() {
|
||||||
// eat the function keyword
|
// eat the function keyword
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
|
@ -5,6 +5,7 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
|
@ -155,12 +156,12 @@ impl LuaCode {
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses an [`Expression`]
|
/// Parses an [`Expression`]
|
||||||
pub fn parse_expression(&mut self, handler: &impl Handler<Error>) -> Option<Expression> {
|
pub fn parse_expression(&mut self, handler: &impl Handler<base::Error>) -> Option<Expression> {
|
||||||
Some(Expression::Primary(self.parse_primary(handler)?))
|
Some(Expression::Primary(self.parse_primary(handler)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an [`Primary`]
|
/// Parses an [`Primary`]
|
||||||
pub fn parse_primary(&mut self, handler: &impl Handler<Error>) -> Option<Primary> {
|
pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> Option<Primary> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
// identifier expression
|
// identifier expression
|
||||||
Reading::Atomic(Token::Identifier(identifier)) => {
|
Reading::Atomic(Token::Identifier(identifier)) => {
|
||||||
|
|
|
@ -4,8 +4,9 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
DummyHandler, Handler,
|
Handler, VoidHandler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Punctuation, Token},
|
token::{Punctuation, Token},
|
||||||
|
@ -14,7 +15,7 @@ use crate::{
|
||||||
syntax::parser::Reading,
|
syntax::parser::Reading,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{error::Error, parser::Parser};
|
use super::parser::Parser;
|
||||||
|
|
||||||
pub mod condition;
|
pub mod condition;
|
||||||
pub mod declaration;
|
pub mod declaration;
|
||||||
|
@ -76,7 +77,7 @@ impl<'a> Parser<'a> {
|
||||||
delimiter: Delimiter,
|
delimiter: Delimiter,
|
||||||
separator: char,
|
separator: char,
|
||||||
mut f: impl FnMut(&mut Self) -> Option<T>,
|
mut f: impl FnMut(&mut Self) -> Option<T>,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<DelimitedList<T>> {
|
) -> Option<DelimitedList<T>> {
|
||||||
fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option<Punctuation> {
|
fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option<Punctuation> {
|
||||||
if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| {
|
if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| {
|
||||||
|
@ -162,14 +163,14 @@ impl<'a> Parser<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
seperator: char,
|
seperator: char,
|
||||||
mut f: impl FnMut(&mut Self) -> Option<T>,
|
mut f: impl FnMut(&mut Self) -> Option<T>,
|
||||||
_handler: &impl Handler<Error>,
|
_handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ConnectedList<T, Punctuation>> {
|
) -> Option<ConnectedList<T, Punctuation>> {
|
||||||
let first = f(self)?;
|
let first = f(self)?;
|
||||||
|
|
||||||
let mut rest = Vec::new();
|
let mut rest = Vec::new();
|
||||||
|
|
||||||
while let Some(sep) =
|
while let Some(sep) =
|
||||||
self.try_parse(|parser| parser.parse_punctuation(seperator, true, &DummyHandler))
|
self.try_parse(|parser| parser.parse_punctuation(seperator, true, &VoidHandler))
|
||||||
{
|
{
|
||||||
if let Some(element) = self.try_parse(&mut f) {
|
if let Some(element) = self.try_parse(&mut f) {
|
||||||
rest.push((sep, element));
|
rest.push((sep, element));
|
||||||
|
|
|
@ -4,12 +4,14 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
syntax::{
|
syntax::{
|
||||||
error::{Error, SyntaxKind, UnexpectedSyntax},
|
self,
|
||||||
|
error::{SyntaxKind, UnexpectedSyntax},
|
||||||
parser::{Parser, Reading},
|
parser::{Parser, Reading},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -79,7 +81,7 @@ impl Namespace {
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses a [`ProgramFile`].
|
/// Parses a [`ProgramFile`].
|
||||||
#[tracing::instrument(level = "debug", skip_all)]
|
#[tracing::instrument(level = "debug", skip_all)]
|
||||||
pub fn parse_program(&mut self, handler: &impl Handler<Error>) -> Option<ProgramFile> {
|
pub fn parse_program(&mut self, handler: &impl Handler<base::Error>) -> Option<ProgramFile> {
|
||||||
tracing::debug!("Parsing program");
|
tracing::debug!("Parsing program");
|
||||||
|
|
||||||
let namespace = match self.stop_at_significant() {
|
let namespace = match self.stop_at_significant() {
|
||||||
|
@ -102,10 +104,10 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
unexpected => {
|
unexpected => {
|
||||||
handler.receive(UnexpectedSyntax {
|
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||||
expected: SyntaxKind::Keyword(KeywordKind::Namespace),
|
expected: SyntaxKind::Keyword(KeywordKind::Namespace),
|
||||||
found: unexpected.into_token(),
|
found: unexpected.into_token(),
|
||||||
});
|
}));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
|
@ -7,6 +7,7 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
|
@ -14,10 +15,7 @@ use crate::{
|
||||||
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token},
|
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token},
|
||||||
token_stream::Delimiter,
|
token_stream::Delimiter,
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::parser::{Parser, Reading},
|
||||||
error::Error,
|
|
||||||
parser::{Parser, Reading},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::execute_block::ExecuteBlock;
|
use self::execute_block::ExecuteBlock;
|
||||||
|
@ -211,7 +209,7 @@ impl Semicolon {
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
/// Parses a [`Block`].
|
/// Parses a [`Block`].
|
||||||
pub fn parse_block(&mut self, handler: &impl Handler<Error>) -> Option<Block> {
|
pub fn parse_block(&mut self, handler: &impl Handler<base::Error>) -> Option<Block> {
|
||||||
let token_tree = self.step_into(
|
let token_tree = self.step_into(
|
||||||
Delimiter::Brace,
|
Delimiter::Brace,
|
||||||
|parser| {
|
|parser| {
|
||||||
|
@ -250,7 +248,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
/// Parses a [`Statement`].
|
/// Parses a [`Statement`].
|
||||||
#[tracing::instrument(level = "trace", skip_all)]
|
#[tracing::instrument(level = "trace", skip_all)]
|
||||||
pub fn parse_statement(&mut self, handler: &impl Handler<Error>) -> Option<Statement> {
|
pub fn parse_statement(&mut self, handler: &impl Handler<base::Error>) -> Option<Statement> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
// variable declaration
|
// variable declaration
|
||||||
Reading::Atomic(Token::CommandLiteral(command)) => {
|
Reading::Atomic(Token::CommandLiteral(command)) => {
|
||||||
|
|
|
@ -6,15 +6,17 @@ use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
self,
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
DummyHandler, Handler,
|
VoidHandler, Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
token_stream::Delimiter,
|
token_stream::Delimiter,
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::{
|
||||||
error::{Error, SyntaxKind, UnexpectedSyntax},
|
self,
|
||||||
|
error::{SyntaxKind, UnexpectedSyntax},
|
||||||
parser::{DelimitedTree, Parser, Reading},
|
parser::{DelimitedTree, Parser, Reading},
|
||||||
syntax_tree::condition::ParenthesizedCondition,
|
syntax_tree::condition::ParenthesizedCondition,
|
||||||
},
|
},
|
||||||
|
@ -711,7 +713,7 @@ impl<'a> Parser<'a> {
|
||||||
/// Parses an [`ExecuteBlock`].
|
/// Parses an [`ExecuteBlock`].
|
||||||
pub fn parse_execute_block_statement(
|
pub fn parse_execute_block_statement(
|
||||||
&mut self,
|
&mut self,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ExecuteBlock> {
|
) -> Option<ExecuteBlock> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
Reading::Atomic(Token::Keyword(if_keyword))
|
Reading::Atomic(Token::Keyword(if_keyword))
|
||||||
|
@ -728,7 +730,7 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let else_tail = self.try_parse(|parser| {
|
let else_tail = self.try_parse(|parser| {
|
||||||
let block = parser.parse_block(&DummyHandler)?;
|
let block = parser.parse_block(&VoidHandler)?;
|
||||||
let (else_keyword, else_block) = match parser.stop_at_significant() {
|
let (else_keyword, else_block) = match parser.stop_at_significant() {
|
||||||
// else statement
|
// else statement
|
||||||
Reading::Atomic(Token::Keyword(else_keyword))
|
Reading::Atomic(Token::Keyword(else_keyword))
|
||||||
|
@ -775,10 +777,10 @@ impl<'a> Parser<'a> {
|
||||||
handler,
|
handler,
|
||||||
),
|
),
|
||||||
unexpected => {
|
unexpected => {
|
||||||
handler.receive(UnexpectedSyntax {
|
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||||
expected: SyntaxKind::Punctuation('('),
|
expected: SyntaxKind::Punctuation('('),
|
||||||
found: unexpected.into_token(),
|
found: unexpected.into_token(),
|
||||||
});
|
}));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
@ -792,10 +794,10 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
// unexpected
|
// unexpected
|
||||||
unexpected => {
|
unexpected => {
|
||||||
handler.receive(UnexpectedSyntax {
|
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||||
expected: SyntaxKind::ExecuteBlock,
|
expected: SyntaxKind::ExecuteBlock,
|
||||||
found: unexpected.into_token(),
|
found: unexpected.into_token(),
|
||||||
});
|
}));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -803,7 +805,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
fn parse_execute_block_tail(
|
fn parse_execute_block_tail(
|
||||||
&mut self,
|
&mut self,
|
||||||
handler: &impl Handler<Error>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Option<ExecuteBlockTail> {
|
) -> Option<ExecuteBlockTail> {
|
||||||
match self.stop_at_significant() {
|
match self.stop_at_significant() {
|
||||||
// nested execute block
|
// nested execute block
|
||||||
|
@ -827,10 +829,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
unexpected => {
|
unexpected => {
|
||||||
handler.receive(UnexpectedSyntax {
|
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
|
||||||
expected: SyntaxKind::ExecuteBlockTail,
|
expected: SyntaxKind::ExecuteBlockTail,
|
||||||
found: unexpected.into_token(),
|
found: unexpected.into_token(),
|
||||||
});
|
}));
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ mod enabled {
|
||||||
use mlua::Lua;
|
use mlua::Lua;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{source_file::SourceElement, Handler},
|
base::{self, source_file::SourceElement, Handler},
|
||||||
syntax::syntax_tree::expression::LuaCode,
|
syntax::syntax_tree::expression::LuaCode,
|
||||||
transpile::error::{TranspileError, TranspileResult},
|
transpile::error::{TranspileError, TranspileResult},
|
||||||
};
|
};
|
||||||
|
@ -16,10 +16,7 @@ mod enabled {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If Lua code evaluation is disabled.
|
/// - If Lua code evaluation is disabled.
|
||||||
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
|
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
|
||||||
pub fn eval_string(
|
pub fn eval_string(&self, handler: &impl Handler<base::Error>) -> TranspileResult<String> {
|
||||||
&self,
|
|
||||||
handler: &impl Handler<TranspileError>,
|
|
||||||
) -> TranspileResult<String> {
|
|
||||||
tracing::debug!("Evaluating Lua code");
|
tracing::debug!("Evaluating Lua code");
|
||||||
|
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
|
@ -3,10 +3,15 @@
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub mod conversions;
|
pub mod conversions;
|
||||||
pub mod error;
|
mod error;
|
||||||
|
#[doc(inline)]
|
||||||
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
pub use error::{TranspileError, TranspileResult};
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod lua;
|
pub mod lua;
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub mod transpiler;
|
mod transpiler;
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use transpiler::Transpiler;
|
||||||
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{collections::HashMap, iter, sync::RwLock};
|
||||||
use shulkerbox::datapack::{self, Command, Datapack, Execute};
|
use shulkerbox::datapack::{self, Command, Datapack, Execute};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{source_file::SourceElement, Handler},
|
base::{self, source_file::SourceElement, Handler},
|
||||||
syntax::syntax_tree::{
|
syntax::syntax_tree::{
|
||||||
declaration::{Declaration, ImportItems},
|
declaration::{Declaration, ImportItems},
|
||||||
expression::{Expression, FunctionCall, Primary},
|
expression::{Expression, FunctionCall, Primary},
|
||||||
|
@ -64,7 +64,7 @@ impl Transpiler {
|
||||||
pub fn transpile<Ident>(
|
pub fn transpile<Ident>(
|
||||||
&mut self,
|
&mut self,
|
||||||
programs: &[(Ident, ProgramFile)],
|
programs: &[(Ident, ProgramFile)],
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> Result<(), TranspileError>
|
) -> Result<(), TranspileError>
|
||||||
where
|
where
|
||||||
Ident: AsRef<str>,
|
Ident: AsRef<str>,
|
||||||
|
@ -107,7 +107,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
program: &ProgramFile,
|
program: &ProgramFile,
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) {
|
) {
|
||||||
let namespace = program.namespace();
|
let namespace = program.namespace();
|
||||||
|
|
||||||
|
@ -117,12 +117,13 @@ impl Transpiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transpiles the given declaration.
|
/// Transpiles the given declaration.
|
||||||
|
#[allow(clippy::needless_pass_by_ref_mut)]
|
||||||
fn transpile_declaration(
|
fn transpile_declaration(
|
||||||
&mut self,
|
&mut self,
|
||||||
declaration: &Declaration,
|
declaration: &Declaration,
|
||||||
namespace: &Namespace,
|
namespace: &Namespace,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
_handler: &impl Handler<TranspileError>,
|
_handler: &impl Handler<base::Error>,
|
||||||
) {
|
) {
|
||||||
match declaration {
|
match declaration {
|
||||||
Declaration::Function(function) => {
|
Declaration::Function(function) => {
|
||||||
|
@ -184,7 +185,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<String> {
|
) -> TranspileResult<String> {
|
||||||
let program_query = (program_identifier.to_string(), name.to_string());
|
let program_query = (program_identifier.to_string(), name.to_string());
|
||||||
let alias_query = {
|
let alias_query = {
|
||||||
|
@ -289,7 +290,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
statements: &[Statement],
|
statements: &[Statement],
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Vec<Command>> {
|
) -> TranspileResult<Vec<Command>> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let commands = statements
|
let commands = statements
|
||||||
|
@ -314,7 +315,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<Command>> {
|
) -> TranspileResult<Option<Command>> {
|
||||||
match statement {
|
match statement {
|
||||||
Statement::LiteralCommand(literal_command) => {
|
Statement::LiteralCommand(literal_command) => {
|
||||||
|
@ -381,7 +382,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
func: &FunctionCall,
|
func: &FunctionCall,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Command> {
|
) -> TranspileResult<Command> {
|
||||||
let identifier = func.identifier().span();
|
let identifier = func.identifier().span();
|
||||||
let identifier_name = identifier.str();
|
let identifier_name = identifier.str();
|
||||||
|
@ -394,7 +395,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
execute: &ExecuteBlock,
|
execute: &ExecuteBlock,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<Command>> {
|
) -> TranspileResult<Option<Command>> {
|
||||||
self.transpile_execute_block_internal(execute, program_identifier, handler)
|
self.transpile_execute_block_internal(execute, program_identifier, handler)
|
||||||
.map(|ex| ex.map(Command::Execute))
|
.map(|ex| ex.map(Command::Execute))
|
||||||
|
@ -404,7 +405,7 @@ impl Transpiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
execute: &ExecuteBlock,
|
execute: &ExecuteBlock,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<Execute>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
match execute {
|
match execute {
|
||||||
ExecuteBlock::HeadTail(head, tail) => {
|
ExecuteBlock::HeadTail(head, tail) => {
|
||||||
|
@ -489,7 +490,7 @@ impl Transpiler {
|
||||||
then: Execute,
|
then: Execute,
|
||||||
el: Option<&Else>,
|
el: Option<&Else>,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<Execute>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
let (_, cond) = cond.clone().dissolve();
|
let (_, cond) = cond.clone().dissolve();
|
||||||
let (_, cond, _) = cond.dissolve();
|
let (_, cond, _) = cond.dissolve();
|
||||||
|
@ -540,7 +541,7 @@ impl Transpiler {
|
||||||
head: &ExecuteBlockHead,
|
head: &ExecuteBlockHead,
|
||||||
tail: Option<Execute>,
|
tail: Option<Execute>,
|
||||||
program_identifier: &str,
|
program_identifier: &str,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<base::Error>,
|
||||||
) -> TranspileResult<Option<Execute>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
Ok(match head {
|
Ok(match head {
|
||||||
ExecuteBlockHead::Conditional(cond) => {
|
ExecuteBlockHead::Conditional(cond) => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use shulkerbox::virtual_fs::{VFile, VFolder};
|
use shulkerbox::virtual_fs::{VFile, VFolder};
|
||||||
use shulkerscript::{
|
use shulkerscript::{
|
||||||
base::source_file::SourceElement,
|
base::{source_file::SourceElement, PrintHandler},
|
||||||
syntax::syntax_tree::{declaration::Declaration, statement::Statement},
|
syntax::syntax_tree::{declaration::Declaration, statement::Statement},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ fn parsing_test1() {
|
||||||
let mut dir = VFolder::new();
|
let mut dir = VFolder::new();
|
||||||
dir.add_file("test1.shu", VFile::Text(source.to_string()));
|
dir.add_file("test1.shu", VFile::Text(source.to_string()));
|
||||||
|
|
||||||
let parsed = shulkerscript::parse(&dir, Path::new("test1.shu")).expect("Failed to parse");
|
let parsed = shulkerscript::parse(&PrintHandler::default(), &dir, Path::new("test1.shu"))
|
||||||
|
.expect("Failed to parse");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parsed.namespace().namespace_name().str_content(),
|
parsed.namespace().namespace_name().str_content(),
|
||||||
|
@ -48,5 +49,6 @@ fn parsing_invalid() {
|
||||||
let mut dir = VFolder::new();
|
let mut dir = VFolder::new();
|
||||||
dir.add_file("invalid.shu", VFile::Text(source.to_string()));
|
dir.add_file("invalid.shu", VFile::Text(source.to_string()));
|
||||||
|
|
||||||
shulkerscript::parse(&dir, Path::new("invalid.shu")).expect_err("Expecting parsing failure");
|
shulkerscript::parse(&PrintHandler::default(), &dir, Path::new("invalid.shu"))
|
||||||
|
.expect_err("Expecting parsing failure");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use shulkerbox::{
|
||||||
datapack::{Command, Condition, Datapack, Execute},
|
datapack::{Command, Condition, Datapack, Execute},
|
||||||
virtual_fs::{VFile, VFolder},
|
virtual_fs::{VFile, VFolder},
|
||||||
};
|
};
|
||||||
|
use shulkerscript::base::PrintHandler;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transpile_test1() {
|
fn transpile_test1() {
|
||||||
|
@ -9,7 +10,12 @@ fn transpile_test1() {
|
||||||
let mut dir = VFolder::new();
|
let mut dir = VFolder::new();
|
||||||
dir.add_file("test1.shu", VFile::Text(source.to_string()));
|
dir.add_file("test1.shu", VFile::Text(source.to_string()));
|
||||||
|
|
||||||
let transpiled = shulkerscript::transpile(&dir, 48, &[("test1".to_string(), "./test1.shu")])
|
let transpiled = shulkerscript::transpile(
|
||||||
|
&PrintHandler::default(),
|
||||||
|
&dir,
|
||||||
|
48,
|
||||||
|
&[("test1".to_string(), "./test1.shu")],
|
||||||
|
)
|
||||||
.expect("Failed to transpile");
|
.expect("Failed to transpile");
|
||||||
|
|
||||||
let expected = {
|
let expected = {
|
||||||
|
|
Loading…
Reference in New Issue