implement file provider for working without file systems
This commit is contained in:
parent
b428c64f89
commit
af544ac79e
|
@ -1,11 +1,9 @@
|
||||||
use std::io;
|
|
||||||
|
|
||||||
/// An error that occurred during compilation.
|
/// An error that occurred during compilation.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("An error occurred while reading the file.")]
|
#[error("An error occurred while working with Input/Output.")]
|
||||||
IoError(#[from] io::Error),
|
IoError(String),
|
||||||
#[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.")]
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
/// A trait for providing file contents.
|
||||||
|
pub trait FileProvider {
|
||||||
|
/// Reads the contents of the file at the given path.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - If an error occurs while reading the file.
|
||||||
|
/// - If the file does not exist.
|
||||||
|
fn read_to_string<P: AsRef<Path>>(&self, path: P) -> Result<String, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides file contents from the file system.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FsProvider {
|
||||||
|
/// The root directory to base paths off of.
|
||||||
|
root: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FsProvider {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
root: PathBuf::from("."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileProvider for FsProvider {
|
||||||
|
fn read_to_string<P: AsRef<Path>>(&self, path: P) -> Result<String, Error> {
|
||||||
|
let full_path = self.root.join(path);
|
||||||
|
std::fs::read_to_string(full_path).map_err(|err| Error::IoError(err.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "shulkerbox")]
|
||||||
|
mod vfs {
|
||||||
|
use super::{Error, FileProvider, Path};
|
||||||
|
use shulkerbox::virtual_fs::{VFile, VFolder};
|
||||||
|
|
||||||
|
impl FileProvider for VFolder {
|
||||||
|
fn read_to_string<P: AsRef<Path>>(&self, path: P) -> Result<String, Error> {
|
||||||
|
normalize_path_str(path).map_or_else(
|
||||||
|
|| Err(Error::IoError("Invalid path".to_string())),
|
||||||
|
|path| {
|
||||||
|
self.get_file(&path)
|
||||||
|
.ok_or_else(|| Error::IoError("File not found".to_string()))
|
||||||
|
.and_then(|file| match file {
|
||||||
|
VFile::Text(text) => Ok(text.to_owned()),
|
||||||
|
VFile::Binary(bin) => String::from_utf8(bin.clone())
|
||||||
|
.map_err(|err| Error::IoError(err.to_string())),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_path_str<P: AsRef<Path>>(path: P) -> Option<String> {
|
||||||
|
let mut err = false;
|
||||||
|
let res = path
|
||||||
|
.as_ref()
|
||||||
|
.to_str()?
|
||||||
|
.split('/')
|
||||||
|
.fold(Vec::new(), |mut acc, el| match el {
|
||||||
|
"." | "" => acc,
|
||||||
|
".." => {
|
||||||
|
let popped = acc.pop();
|
||||||
|
if popped.is_none() {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
acc.push(el);
|
||||||
|
acc
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join("/");
|
||||||
|
|
||||||
|
if err {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_normalize_path() {
|
||||||
|
assert_eq!(normalize_path_str("a/b/c"), Some("a/b/c".to_string()));
|
||||||
|
assert_eq!(normalize_path_str("a/b/../c"), Some("a/c".to_string()));
|
||||||
|
assert_eq!(normalize_path_str("./a/b/c"), Some("a/b/c".to_string()));
|
||||||
|
assert_eq!(normalize_path_str("../a/b/c"), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,4 +9,7 @@ pub use error::{Error, Result};
|
||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
pub use diagnostic::{DummyHandler, Handler};
|
pub use diagnostic::{DummyHandler, Handler};
|
||||||
|
|
||||||
|
mod file_provider;
|
||||||
|
pub use file_provider::{FileProvider, FsProvider};
|
||||||
|
|
||||||
pub mod log;
|
pub mod log;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
fs,
|
|
||||||
iter::{Iterator, Peekable},
|
iter::{Iterator, Peekable},
|
||||||
ops::Range,
|
ops::Range,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -13,7 +12,7 @@ use std::{
|
||||||
|
|
||||||
use getset::{CopyGetters, Getters};
|
use getset::{CopyGetters, Getters};
|
||||||
|
|
||||||
use super::Error;
|
use super::{file_provider::FileProvider, Error};
|
||||||
|
|
||||||
/// Represents a source file that contains the source code.
|
/// Represents a source file that contains the source code.
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
@ -84,8 +83,8 @@ impl SourceFile {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - [`Error::IoError`]: Error occurred when reading the file contents.
|
/// - [`Error::IoError`]: Error occurred when reading the file contents.
|
||||||
pub fn load(path: &Path) -> Result<Arc<Self>, Error> {
|
pub fn load(path: &Path, provider: &impl FileProvider) -> Result<Arc<Self>, Error> {
|
||||||
let source = fs::read_to_string(path).map_err(Error::IoError)?;
|
let source = provider.read_to_string(path)?;
|
||||||
Ok(Self::new(path.to_path_buf(), source))
|
Ok(Self::new(path.to_path_buf(), source))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -23,7 +23,7 @@ mod public_helpers;
|
||||||
|
|
||||||
use std::{cell::Cell, fmt::Display, path::Path};
|
use std::{cell::Cell, fmt::Display, path::Path};
|
||||||
|
|
||||||
use base::{Handler, Result};
|
use base::{FileProvider, Handler, Result};
|
||||||
use syntax::syntax_tree::program::ProgramFile;
|
use syntax::syntax_tree::program::ProgramFile;
|
||||||
|
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
|
@ -31,14 +31,19 @@ use shulkerbox::{datapack::Datapack, virtual_fs::VFolder};
|
||||||
|
|
||||||
use crate::lexical::token_stream::TokenStream;
|
use crate::lexical::token_stream::TokenStream;
|
||||||
|
|
||||||
|
const DEFAULT_PACK_FORMAT: u8 = 48;
|
||||||
|
|
||||||
/// Converts the given source code to tokens.
|
/// Converts the given source code to tokens.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs while reading the file.
|
||||||
pub fn tokenize(path: &Path) -> Result<TokenStream> {
|
pub fn tokenize<F>(file_provider: &F, path: &Path) -> Result<TokenStream>
|
||||||
|
where
|
||||||
|
F: FileProvider,
|
||||||
|
{
|
||||||
let printer = Printer::new();
|
let printer = Printer::new();
|
||||||
|
|
||||||
public_helpers::tokenize(&printer, path)
|
public_helpers::tokenize(&printer, file_provider, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the given source code.
|
/// Parses the given source code.
|
||||||
|
@ -46,10 +51,13 @@ pub fn tokenize(path: &Path) -> Result<TokenStream> {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - If an error occurs while reading the file.
|
||||||
/// - If an error occurs while parsing the source code.
|
/// - If an error occurs while parsing the source code.
|
||||||
pub fn parse(path: &Path) -> Result<ProgramFile> {
|
pub fn parse<F>(file_provider: &F, path: &Path) -> Result<ProgramFile>
|
||||||
|
where
|
||||||
|
F: FileProvider,
|
||||||
|
{
|
||||||
let printer = Printer::new();
|
let printer = Printer::new();
|
||||||
|
|
||||||
public_helpers::parse(&printer, path)
|
public_helpers::parse(&printer, file_provider, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
/// Transpiles the given source code into a shulkerbox [`Datapack`].
|
||||||
|
@ -62,13 +70,14 @@ pub fn parse(path: &Path) -> Result<ProgramFile> {
|
||||||
/// - If an error occurs while parsing the source code.
|
/// - 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<P>(script_paths: &[(String, P)]) -> Result<Datapack>
|
pub fn transpile<F, P>(file_provider: &F, script_paths: &[(String, P)]) -> Result<Datapack>
|
||||||
where
|
where
|
||||||
|
F: FileProvider,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let printer = Printer::new();
|
let printer = Printer::new();
|
||||||
|
|
||||||
public_helpers::transpile(&printer, script_paths)
|
public_helpers::transpile(&printer, file_provider, script_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compiles the given source code.
|
/// Compiles the given source code.
|
||||||
|
@ -81,13 +90,14 @@ where
|
||||||
/// - If an error occurs while parsing the source code.
|
/// - 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 compile<P>(script_paths: &[(String, P)]) -> Result<VFolder>
|
pub fn compile<F, P>(file_provider: &F, script_paths: &[(String, P)]) -> Result<VFolder>
|
||||||
where
|
where
|
||||||
|
F: FileProvider,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let printer = Printer::new();
|
let printer = Printer::new();
|
||||||
|
|
||||||
public_helpers::compile(&printer, script_paths)
|
public_helpers::compile(&printer, file_provider, script_paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Printer {
|
struct Printer {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{source_file::SourceFile, Error, Result},
|
base::{source_file::SourceFile, Error, FileProvider, Result},
|
||||||
lexical::token_stream::TokenStream,
|
lexical::token_stream::TokenStream,
|
||||||
syntax::{parser::Parser, syntax_tree::program::ProgramFile},
|
syntax::{parser::Parser, syntax_tree::program::ProgramFile},
|
||||||
Printer,
|
Printer,
|
||||||
|
@ -14,17 +14,25 @@ use crate::transpile::transpiler::Transpiler;
|
||||||
use shulkerbox::{datapack::Datapack, util::compile::CompileOptions, virtual_fs::VFolder};
|
use shulkerbox::{datapack::Datapack, util::compile::CompileOptions, virtual_fs::VFolder};
|
||||||
|
|
||||||
/// Tokenizes the source code at the given path.
|
/// Tokenizes the source code at the given path.
|
||||||
pub fn tokenize(printer: &Printer, path: &Path) -> Result<TokenStream> {
|
pub fn tokenize(
|
||||||
|
printer: &Printer,
|
||||||
|
file_provider: &impl FileProvider,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<TokenStream> {
|
||||||
tracing::info!("Tokenizing the source code at path: {}", path.display());
|
tracing::info!("Tokenizing the source code at path: {}", path.display());
|
||||||
|
|
||||||
let source_file = SourceFile::load(path)?;
|
let source_file = SourceFile::load(path, file_provider)?;
|
||||||
|
|
||||||
Ok(TokenStream::tokenize(&source_file, printer))
|
Ok(TokenStream::tokenize(&source_file, printer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the source code at the given path.
|
/// Parses the source code at the given path.
|
||||||
pub fn parse(printer: &Printer, path: &Path) -> Result<ProgramFile> {
|
pub fn parse(
|
||||||
let tokens = tokenize(printer, path)?;
|
printer: &Printer,
|
||||||
|
file_provider: &impl FileProvider,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<ProgramFile> {
|
||||||
|
let tokens = tokenize(printer, file_provider, path)?;
|
||||||
|
|
||||||
if printer.has_printed() {
|
if printer.has_printed() {
|
||||||
return Err(Error::Other(
|
return Err(Error::Other(
|
||||||
|
@ -50,14 +58,19 @@ pub fn parse(printer: &Printer, path: &Path) -> Result<ProgramFile> {
|
||||||
|
|
||||||
/// Transpiles the source code at the given paths into a shulkerbox [`Datapack`].
|
/// Transpiles the source code at the given paths into a shulkerbox [`Datapack`].
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub fn transpile<P>(printer: &Printer, script_paths: &[(String, P)]) -> Result<Datapack>
|
pub fn transpile<F, P>(
|
||||||
|
printer: &Printer,
|
||||||
|
file_provider: &F,
|
||||||
|
script_paths: &[(String, P)],
|
||||||
|
) -> Result<Datapack>
|
||||||
where
|
where
|
||||||
|
F: FileProvider,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let programs = script_paths
|
let programs = script_paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(program_identifier, path)| {
|
.map(|(program_identifier, path)| {
|
||||||
let program = parse(printer, path.as_ref())?;
|
let program = parse(printer, file_provider, path.as_ref())?;
|
||||||
|
|
||||||
Ok((program_identifier, program))
|
Ok((program_identifier, program))
|
||||||
})
|
})
|
||||||
|
@ -73,7 +86,7 @@ where
|
||||||
|
|
||||||
tracing::info!("Transpiling the source code.");
|
tracing::info!("Transpiling the source code.");
|
||||||
|
|
||||||
let mut transpiler = Transpiler::new(27);
|
let mut transpiler = Transpiler::new(crate::DEFAULT_PACK_FORMAT);
|
||||||
transpiler.transpile(&programs, printer)?;
|
transpiler.transpile(&programs, printer)?;
|
||||||
let datapack = transpiler.into_datapack();
|
let datapack = transpiler.into_datapack();
|
||||||
|
|
||||||
|
@ -88,11 +101,16 @@ where
|
||||||
|
|
||||||
/// Compiles the source code at the given paths.
|
/// Compiles the source code at the given paths.
|
||||||
#[cfg(feature = "shulkerbox")]
|
#[cfg(feature = "shulkerbox")]
|
||||||
pub fn compile<P>(printer: &Printer, script_paths: &[(String, P)]) -> Result<VFolder>
|
pub fn compile<F, P>(
|
||||||
|
printer: &Printer,
|
||||||
|
file_provider: &F,
|
||||||
|
script_paths: &[(String, P)],
|
||||||
|
) -> Result<VFolder>
|
||||||
where
|
where
|
||||||
|
F: FileProvider,
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
let datapack = transpile(printer, script_paths)?;
|
let datapack = transpile(printer, file_provider, script_paths)?;
|
||||||
|
|
||||||
tracing::info!("Compiling the source code.");
|
tracing::info!("Compiling the source code.");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue