allow custom handlers instead of only printer

This commit is contained in:
Moritz Hölting 2024-08-23 00:06:58 +02:00
parent bc25da6f2c
commit 83d5f329f9
20 changed files with 273 additions and 263 deletions

View File

@ -1,3 +1,5 @@
use std::{cell::Cell, fmt::Display};
/// Represents a trait responsible for handling diagnostics in the interpreter.
pub trait Handler<T> {
/// Receive an error and handles it.
@ -6,13 +8,89 @@ pub trait Handler<T> {
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)]
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 has_received(&self) -> bool {
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));
}
}

View File

@ -4,12 +4,14 @@
pub enum Error {
#[error("An error occurred while working with Input/Output.")]
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.")]
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 transpiling the source code.")]
TranspileError(#[from] crate::transpile::error::TranspileError),
TranspileError(#[from] crate::transpile::TranspileError),
#[error("An error occurred")]
Other(&'static str),
}

View File

@ -7,7 +7,7 @@ mod error;
pub use error::{Error, Result};
mod diagnostic;
pub use diagnostic::{DummyHandler, Handler};
pub use diagnostic::{Handler, PrintHandler, SilentHandler, VoidHandler};
mod file_provider;
pub use file_provider::{FileProvider, FsProvider};

View File

@ -3,6 +3,7 @@
use std::{borrow::Cow, collections::HashMap, fmt::Display, str::FromStr, sync::OnceLock};
use crate::base::{
self,
source_file::{SourceElement, SourceIterator, Span},
Handler,
};
@ -11,7 +12,7 @@ use enum_as_inner::EnumAsInner;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use super::{error::UnterminatedDelimitedComment, Error};
use super::error::{self, UnterminatedDelimitedComment};
/// Is an enumeration representing keywords in shulkerscript.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -439,7 +440,7 @@ impl Token {
start: usize,
character: char,
prev_token: Option<&Self>,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Result<Self, TokenizeError> {
// Single line comment
if let Some((_, '/')) = iter.peek() {
@ -503,9 +504,9 @@ impl Token {
}
.into())
} else {
handler.receive(UnterminatedDelimitedComment {
handler.receive(error::Error::from(UnterminatedDelimitedComment {
span: Span::new(iter.source_file().clone(), start, start + 2).unwrap(),
});
}));
return Err(TokenizeError::FatalLexicalError);
}
}
@ -576,7 +577,7 @@ impl Token {
/// - [`TokenizeError::FatalLexicalError`] - A fatal lexical error occurred.
pub fn tokenize(
iter: &mut SourceIterator,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
prev_token: Option<&Self>,
) -> Result<Self, TokenizeError> {
// Gets the first character

View File

@ -6,6 +6,7 @@ use derive_more::{Deref, From};
use enum_as_inner::EnumAsInner;
use crate::base::{
self,
source_file::{SourceElement, SourceFile, Span},
Handler,
};
@ -46,7 +47,7 @@ impl TokenStream {
/// encountered during tokenization.
#[must_use]
#[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.
let mut tokens = Vec::new();
let mut source_file_iterator = source_file.iter();
@ -81,7 +82,7 @@ impl TokenStream {
/// Handles a token.
fn handle_token(
tokens: &mut Vec<Token>,
handler: &impl Handler<error::Error>,
handler: &impl Handler<base::Error>,
) -> Option<TokenTree> {
tokens
.pop()
@ -92,7 +93,7 @@ impl TokenStream {
fn handle_popped_token(
tokens: &mut Vec<Token>,
popped_token: Token,
handler: &impl Handler<error::Error>,
handler: &impl Handler<base::Error>,
) -> Option<TokenTree> {
match popped_token {
Token::Punctuation(punc) if punc.punctuation == '{' => {
@ -116,7 +117,7 @@ impl TokenStream {
tokens: &mut Vec<Token>,
open: Punctuation,
delimiter: Delimiter,
handler: &impl Handler<error::Error>,
handler: &impl Handler<base::Error>,
) -> Option<Delimited> {
let mut token_trees = Vec::new();

View File

@ -19,56 +19,77 @@ pub mod lexical;
pub mod syntax;
pub mod transpile;
mod public_helpers;
use std::path::Path;
use std::{cell::Cell, fmt::Display, path::Path};
use base::{FileProvider, Handler, Result};
use syntax::syntax_tree::program::ProgramFile;
use base::{source_file::SourceFile, Error, FileProvider, Handler, Result};
use syntax::{parser::Parser, syntax_tree::program::ProgramFile};
#[cfg(feature = "shulkerbox")]
use shulkerbox::{datapack::Datapack, virtual_fs::VFolder};
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
/// - If an error occurs while reading the file.
pub fn tokenize<F>(file_provider: &F, path: &Path) -> Result<TokenStream>
where
F: FileProvider,
{
let printer = Printer::new();
/// - 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());
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
/// - If an error occurs while reading the file.
/// - If an error occurs during [`tokenize()`].
/// - If an error occurs while parsing the source code.
pub fn parse<F>(file_provider: &F, path: &Path) -> Result<ProgramFile>
where
F: FileProvider,
{
let printer = Printer::new();
pub fn parse(
handler: &impl Handler<base::Error>,
file_provider: &impl FileProvider,
path: &Path,
) -> 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`].
///
/// # 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
/// - If an error occurs while reading the file.
/// - If an error occurs while parsing the source code.
/// - If an error occurs during [`parse()`]
/// - If an error occurs while transpiling the source code.
#[cfg(feature = "shulkerbox")]
pub fn transpile<F, P>(
handler: &impl Handler<base::Error>,
file_provider: &F,
pack_format: u8,
script_paths: &[(String, P)],
@ -77,22 +98,50 @@ where
F: FileProvider,
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.
///
/// # 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
/// - If an error occurs while reading the file.
/// - If an error occurs while parsing the source code.
/// - If an error occurs while transpiling the source code.
/// - If an error occurs during [`transpile()`]
#[cfg(feature = "shulkerbox")]
pub fn compile<F, P>(
handler: &impl Handler<base::Error>,
file_provider: &F,
pack_format: u8,
script_paths: &[(String, P)],
@ -101,35 +150,11 @@ where
F: FileProvider,
P: AsRef<Path>,
{
let printer = Printer::new();
use shulkerbox::prelude::CompileOptions;
public_helpers::compile(&printer, file_provider, pack_format, script_paths)
}
struct Printer {
printed: Cell<bool>,
}
impl Printer {
/// 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()
}
let datapack = transpile(handler, file_provider, pack_format, script_paths)?;
tracing::info!("Compiling the source code.");
Ok(datapack.compile(&CompileOptions::default()))
}

View File

@ -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()))
}

View File

@ -4,7 +4,7 @@ use derive_more::{Deref, DerefMut};
use enum_as_inner::EnumAsInner;
use crate::{
base::Handler,
base::{self, Handler},
lexical::{
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, StringLiteral, Token},
token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
@ -42,7 +42,7 @@ impl<'a> Parser<'a> {
&mut self,
delimiter: Delimiter,
f: impl FnOnce(&mut Self) -> Option<T>,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<DelimitedTree<T>> {
self.current_frame.stop_at_significant();
let raw_token_tree = self
@ -363,7 +363,7 @@ impl<'a> Frame<'a> {
///
/// # Errors
/// 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() {
Reading::Atomic(Token::Identifier(ident)) => Some(ident),
found => {
@ -397,7 +397,10 @@ impl<'a> Frame<'a> {
///
/// # Errors
/// 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() {
Reading::Atomic(Token::StringLiteral(literal)) => Some(literal),
found => {
@ -417,7 +420,7 @@ impl<'a> Frame<'a> {
pub fn parse_keyword(
&mut self,
expected: KeywordKind,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<Keyword> {
match self.next_significant_token() {
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
@ -441,7 +444,7 @@ impl<'a> Frame<'a> {
&mut self,
expected: char,
skip_insignificant: bool,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<Punctuation> {
match if skip_insignificant {
self.next_significant_token()

View File

@ -7,8 +7,9 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
DummyHandler, Handler,
VoidHandler, Handler,
},
lexical::{
token::{Punctuation, StringLiteral, Token},
@ -240,7 +241,7 @@ impl SourceElement for Condition {
impl<'a> Parser<'a> {
/// 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 expressions = VecDeque::new();
@ -305,7 +306,7 @@ impl<'a> Parser<'a> {
/// Parses a [`PrimaryCondition`].
pub fn parse_primary_condition(
&mut self,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<PrimaryCondition> {
match self.stop_at_significant() {
// prefixed expression
@ -354,7 +355,7 @@ impl<'a> Parser<'a> {
/// Parses a [`ParenthesizedCondition`].
pub fn parse_parenthesized_condition(
&mut self,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<ParenthesizedCondition> {
let token_tree = self.step_into(
Delimiter::Parenthesis,
@ -377,11 +378,11 @@ impl<'a> Parser<'a> {
self.try_parse(|parser| match parser.next_significant_token() {
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))
}
'|' => {
let b = parser.parse_punctuation('|', false, &DummyHandler)?;
let b = parser.parse_punctuation('|', false, &VoidHandler)?;
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
}
_ => None,

View File

@ -6,8 +6,9 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
DummyHandler, Handler,
Handler, VoidHandler,
},
lexical::{
token::{Identifier, Keyword, KeywordKind, Punctuation, StringLiteral, Token},
@ -226,7 +227,7 @@ impl SourceElement for Import {
}
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() {
Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => {
// eat the pound sign
@ -280,7 +281,10 @@ impl<'a> Parser<'a> {
}
#[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() {
Reading::Atomic(Token::Keyword(function_keyword))
if function_keyword.keyword == KeywordKind::Function =>
@ -355,7 +359,7 @@ impl<'a> Parser<'a> {
self.try_parse(|parser| parser
.parse_connected_list(
',',
|parser| parser.parse_identifier(&DummyHandler),
|parser| parser.parse_identifier(&VoidHandler),
handler,
)
.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() {
// eat the function keyword
self.forward();

View File

@ -5,6 +5,7 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
Handler,
},
@ -155,12 +156,12 @@ impl LuaCode {
impl<'a> Parser<'a> {
/// 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)?))
}
/// 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() {
// identifier expression
Reading::Atomic(Token::Identifier(identifier)) => {

View File

@ -4,8 +4,9 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
DummyHandler, Handler,
Handler, VoidHandler,
},
lexical::{
token::{Punctuation, Token},
@ -14,7 +15,7 @@ use crate::{
syntax::parser::Reading,
};
use super::{error::Error, parser::Parser};
use super::parser::Parser;
pub mod condition;
pub mod declaration;
@ -76,7 +77,7 @@ impl<'a> Parser<'a> {
delimiter: Delimiter,
separator: char,
mut f: impl FnMut(&mut Self) -> Option<T>,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<DelimitedList<T>> {
fn skip_to_next_separator(this: &mut Parser, separator: char) -> Option<Punctuation> {
if let Reading::Atomic(Token::Punctuation(punc)) = this.stop_at(|token| {
@ -162,14 +163,14 @@ impl<'a> Parser<'a> {
&mut self,
seperator: char,
mut f: impl FnMut(&mut Self) -> Option<T>,
_handler: &impl Handler<Error>,
_handler: &impl Handler<base::Error>,
) -> Option<ConnectedList<T, Punctuation>> {
let first = f(self)?;
let mut rest = Vec::new();
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) {
rest.push((sep, element));

View File

@ -4,12 +4,14 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
Handler,
},
lexical::token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax},
self,
error::{SyntaxKind, UnexpectedSyntax},
parser::{Parser, Reading},
},
};
@ -79,7 +81,7 @@ impl Namespace {
impl<'a> Parser<'a> {
/// Parses a [`ProgramFile`].
#[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");
let namespace = match self.stop_at_significant() {
@ -102,10 +104,10 @@ impl<'a> Parser<'a> {
})
}
unexpected => {
handler.receive(UnexpectedSyntax {
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
expected: SyntaxKind::Keyword(KeywordKind::Namespace),
found: unexpected.into_token(),
});
}));
None
}
}?;

View File

@ -7,6 +7,7 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
Handler,
},
@ -14,10 +15,7 @@ use crate::{
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token},
token_stream::Delimiter,
},
syntax::{
error::Error,
parser::{Parser, Reading},
},
syntax::parser::{Parser, Reading},
};
use self::execute_block::ExecuteBlock;
@ -211,7 +209,7 @@ impl Semicolon {
impl<'a> Parser<'a> {
/// 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(
Delimiter::Brace,
|parser| {
@ -250,7 +248,7 @@ impl<'a> Parser<'a> {
/// Parses a [`Statement`].
#[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() {
// variable declaration
Reading::Atomic(Token::CommandLiteral(command)) => {

View File

@ -6,15 +6,17 @@ use getset::Getters;
use crate::{
base::{
self,
source_file::{SourceElement, Span},
DummyHandler, Handler,
VoidHandler, Handler,
},
lexical::{
token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
token_stream::Delimiter,
},
syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax},
self,
error::{SyntaxKind, UnexpectedSyntax},
parser::{DelimitedTree, Parser, Reading},
syntax_tree::condition::ParenthesizedCondition,
},
@ -711,7 +713,7 @@ impl<'a> Parser<'a> {
/// Parses an [`ExecuteBlock`].
pub fn parse_execute_block_statement(
&mut self,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<ExecuteBlock> {
match self.stop_at_significant() {
Reading::Atomic(Token::Keyword(if_keyword))
@ -728,7 +730,7 @@ impl<'a> Parser<'a> {
};
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() {
// else statement
Reading::Atomic(Token::Keyword(else_keyword))
@ -775,10 +777,10 @@ impl<'a> Parser<'a> {
handler,
),
unexpected => {
handler.receive(UnexpectedSyntax {
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
expected: SyntaxKind::Punctuation('('),
found: unexpected.into_token(),
});
}));
None
}
}?;
@ -792,10 +794,10 @@ impl<'a> Parser<'a> {
// unexpected
unexpected => {
handler.receive(UnexpectedSyntax {
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
expected: SyntaxKind::ExecuteBlock,
found: unexpected.into_token(),
});
}));
None
}
}
@ -803,7 +805,7 @@ impl<'a> Parser<'a> {
fn parse_execute_block_tail(
&mut self,
handler: &impl Handler<Error>,
handler: &impl Handler<base::Error>,
) -> Option<ExecuteBlockTail> {
match self.stop_at_significant() {
// nested execute block
@ -827,10 +829,10 @@ impl<'a> Parser<'a> {
}
unexpected => {
handler.receive(UnexpectedSyntax {
handler.receive(syntax::error::Error::from(UnexpectedSyntax {
expected: SyntaxKind::ExecuteBlockTail,
found: unexpected.into_token(),
});
}));
None
}
}

View File

@ -5,7 +5,7 @@ mod enabled {
use mlua::Lua;
use crate::{
base::{source_file::SourceElement, Handler},
base::{self, source_file::SourceElement, Handler},
syntax::syntax_tree::expression::LuaCode,
transpile::error::{TranspileError, TranspileResult},
};
@ -16,10 +16,7 @@ mod enabled {
/// # Errors
/// - If Lua code evaluation is disabled.
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
pub fn eval_string(
&self,
handler: &impl Handler<TranspileError>,
) -> TranspileResult<String> {
pub fn eval_string(&self, handler: &impl Handler<base::Error>) -> TranspileResult<String> {
tracing::debug!("Evaluating Lua code");
let lua = Lua::new();

View File

@ -3,10 +3,15 @@
#[doc(hidden)]
#[cfg(feature = "shulkerbox")]
pub mod conversions;
pub mod error;
mod error;
#[doc(inline)]
#[allow(clippy::module_name_repetitions)]
pub use error::{TranspileError, TranspileResult};
#[doc(hidden)]
pub mod lua;
#[cfg(feature = "shulkerbox")]
pub mod transpiler;
mod transpiler;
#[doc(inline)]
pub use transpiler::Transpiler;
mod util;

View File

@ -6,7 +6,7 @@ use std::{collections::HashMap, iter, sync::RwLock};
use shulkerbox::datapack::{self, Command, Datapack, Execute};
use crate::{
base::{source_file::SourceElement, Handler},
base::{self, source_file::SourceElement, Handler},
syntax::syntax_tree::{
declaration::{Declaration, ImportItems},
expression::{Expression, FunctionCall, Primary},
@ -64,7 +64,7 @@ impl Transpiler {
pub fn transpile<Ident>(
&mut self,
programs: &[(Ident, ProgramFile)],
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> Result<(), TranspileError>
where
Ident: AsRef<str>,
@ -107,7 +107,7 @@ impl Transpiler {
&mut self,
program: &ProgramFile,
identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) {
let namespace = program.namespace();
@ -117,12 +117,13 @@ impl Transpiler {
}
/// Transpiles the given declaration.
#[allow(clippy::needless_pass_by_ref_mut)]
fn transpile_declaration(
&mut self,
declaration: &Declaration,
namespace: &Namespace,
program_identifier: &str,
_handler: &impl Handler<TranspileError>,
_handler: &impl Handler<base::Error>,
) {
match declaration {
Declaration::Function(function) => {
@ -184,7 +185,7 @@ impl Transpiler {
&mut self,
name: &str,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<String> {
let program_query = (program_identifier.to_string(), name.to_string());
let alias_query = {
@ -289,7 +290,7 @@ impl Transpiler {
&mut self,
statements: &[Statement],
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Vec<Command>> {
let mut errors = Vec::new();
let commands = statements
@ -314,7 +315,7 @@ impl Transpiler {
&mut self,
statement: &Statement,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<Command>> {
match statement {
Statement::LiteralCommand(literal_command) => {
@ -381,7 +382,7 @@ impl Transpiler {
&mut self,
func: &FunctionCall,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Command> {
let identifier = func.identifier().span();
let identifier_name = identifier.str();
@ -394,7 +395,7 @@ impl Transpiler {
&mut self,
execute: &ExecuteBlock,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<Command>> {
self.transpile_execute_block_internal(execute, program_identifier, handler)
.map(|ex| ex.map(Command::Execute))
@ -404,7 +405,7 @@ impl Transpiler {
&mut self,
execute: &ExecuteBlock,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<Execute>> {
match execute {
ExecuteBlock::HeadTail(head, tail) => {
@ -489,7 +490,7 @@ impl Transpiler {
then: Execute,
el: Option<&Else>,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<Execute>> {
let (_, cond) = cond.clone().dissolve();
let (_, cond, _) = cond.dissolve();
@ -540,7 +541,7 @@ impl Transpiler {
head: &ExecuteBlockHead,
tail: Option<Execute>,
program_identifier: &str,
handler: &impl Handler<TranspileError>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<Execute>> {
Ok(match head {
ExecuteBlockHead::Conditional(cond) => {

View File

@ -2,7 +2,7 @@ use std::path::Path;
use shulkerbox::virtual_fs::{VFile, VFolder};
use shulkerscript::{
base::source_file::SourceElement,
base::{source_file::SourceElement, PrintHandler},
syntax::syntax_tree::{declaration::Declaration, statement::Statement},
};
@ -12,7 +12,8 @@ fn parsing_test1() {
let mut dir = VFolder::new();
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!(
parsed.namespace().namespace_name().str_content(),
@ -48,5 +49,6 @@ fn parsing_invalid() {
let mut dir = VFolder::new();
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");
}

View File

@ -2,6 +2,7 @@ use shulkerbox::{
datapack::{Command, Condition, Datapack, Execute},
virtual_fs::{VFile, VFolder},
};
use shulkerscript::base::PrintHandler;
#[test]
fn transpile_test1() {
@ -9,8 +10,13 @@ fn transpile_test1() {
let mut dir = VFolder::new();
dir.add_file("test1.shu", VFile::Text(source.to_string()));
let transpiled = shulkerscript::transpile(&dir, 48, &[("test1".to_string(), "./test1.shu")])
.expect("Failed to transpile");
let transpiled = shulkerscript::transpile(
&PrintHandler::default(),
&dir,
48,
&[("test1".to_string(), "./test1.shu")],
)
.expect("Failed to transpile");
let expected = {
let mut dp = Datapack::new(48);