diff --git a/.idea/src.iml b/.idea/src.iml index c8a0e01..c4fe321 100644 --- a/.idea/src.iml +++ b/.idea/src.iml @@ -2,6 +2,7 @@ + diff --git a/Cargo.lock b/Cargo.lock index 12dbd01..11d51fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,14 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +[[package]] +name = "librashader-preprocess" +version = "0.1.0" +dependencies = [ + "librashader-presets", + "thiserror", +] + [[package]] name = "librashader-presets" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 83ac058..49ffb09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ - "librashader-presets" + "librashader-presets", + "librashader-preprocess" ] \ No newline at end of file diff --git a/librashader-preprocess/Cargo.toml b/librashader-preprocess/Cargo.toml new file mode 100644 index 0000000..a950ed4 --- /dev/null +++ b/librashader-preprocess/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "librashader-preprocess" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +thiserror = "1.0.37" +librashader-presets = { path = "../librashader-presets" } \ No newline at end of file diff --git a/librashader-preprocess/src/error.rs b/librashader-preprocess/src/error.rs new file mode 100644 index 0000000..5808e6f --- /dev/null +++ b/librashader-preprocess/src/error.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum PreprocessError { + #[error("the version header was missing")] + MissingVersionHeader, + #[error("the file was not found during resolution")] + IOError(PathBuf, std::io::Error), + #[error("unexpected end of file")] + UnexpectedEof, + #[error("unexpected end of line")] + UnexpectedEol(usize), +} diff --git a/librashader-preprocess/src/include.rs b/librashader-preprocess/src/include.rs new file mode 100644 index 0000000..1bf57a2 --- /dev/null +++ b/librashader-preprocess/src/include.rs @@ -0,0 +1,90 @@ +use std::fs::File; +use std::io::Read; +use std::str::Lines; +use std::path::Path; +use crate::PreprocessError; + +const GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE: &'static str = "#extension GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE : require"; + +trait PushLine { + fn push_line(&mut self, str: &str); +} + +impl PushLine for String { + fn push_line(&mut self, str: &str) { + self.push_str(str); + self.push('\n'); + } +} + +fn read_file(path: impl AsRef) -> Result { + let path = path.as_ref(); + let mut source = String::new(); + File::open(path).and_then(|mut f| f.read_to_string(&mut source)) + .map_err(|e| PreprocessError::IOError(path.to_path_buf(), e))?; + Ok(source) +} + +pub fn read_source(path: impl AsRef) -> Result { + let path = path.as_ref(); + let source = read_file(path)?; + let mut output = String::new(); + + let source = source.trim(); + let mut lines = source.lines(); + + if let Some(header) = lines.next() { + if !header.starts_with("#version ") { + return Err(PreprocessError::MissingVersionHeader) + } + output.push_line(header); + } else { + return Err(PreprocessError::UnexpectedEof) + } + + output.push_line(GL_GOOGLE_CPP_STYLE_LINE_DIRECTIVE); + mark_line(2, path.file_name().and_then(|f| f.to_str()).unwrap_or(""), &mut output); + preprocess(lines, path, &mut output)?; + + Ok(output) +} + +fn mark_line(line_no: usize, comment: &str, output: &mut String) { + output.push_line(&format!("#line {} \"{}\"", line_no, comment)) +} + +fn preprocess(lines: Lines, file_name: impl AsRef, output: &mut String) -> Result<(), PreprocessError> { + let file_name = file_name.as_ref(); + let include_path = file_name.parent().unwrap(); + let file_name = file_name.file_name().and_then(|f| f.to_str()).unwrap_or(""); + + for (line_no, line) in lines.enumerate() { + if line.starts_with("#include ") { + let include_file = line["#include ".len()..].trim().trim_matches('"'); + if include_file.is_empty() { + return Err(PreprocessError::UnexpectedEol(line_no)) + } + + let mut include_path = include_path.to_path_buf(); + include_path.push(include_file); + + let source = read_file(&include_path)?; + let source = source.trim(); + let lines = source.lines(); + + let include_file = include_path.file_name().and_then(|f| f.to_str()).unwrap_or(""); + mark_line(1, include_file, output); + preprocess(lines, include_path, output)?; + mark_line(line_no + 1, file_name, output); + continue; + } + if line.starts_with("#endif") || line.starts_with("#pragma") { + output.push_line(line); + mark_line(line_no + 2, file_name, output); + continue; + } + + output.push_line(line) + } + Ok(()) +} \ No newline at end of file diff --git a/librashader-preprocess/src/lib.rs b/librashader-preprocess/src/lib.rs new file mode 100644 index 0000000..f067149 --- /dev/null +++ b/librashader-preprocess/src/lib.rs @@ -0,0 +1,15 @@ +mod error; +mod include; + +pub use error::*; + + +#[cfg(test)] +mod test { + use crate::include::read_source; + #[test] + pub fn preprocess_file() { + let result = read_source("../test/slang-shaders/blurs/shaders/royale/blur3x3-last-pass.slang").unwrap(); + eprintln!("{result}") + } +} \ No newline at end of file