diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/macro_processor/macro_processor.rs | 130 | ||||
-rw-r--r-- | src/macro_processor/main.rs | 4 | ||||
-rw-r--r-- | src/skaldpress/main.rs | 18 | ||||
-rw-r--r-- | src/skaldpress/mod.rs | 1 |
5 files changed, 105 insertions, 50 deletions
@@ -1,2 +1,2 @@ -pub mod skaldpress; pub mod macro_processor; +pub mod skaldpress; diff --git a/src/macro_processor/macro_processor.rs b/src/macro_processor/macro_processor.rs index 0d931f9..4a46a62 100644 --- a/src/macro_processor/macro_processor.rs +++ b/src/macro_processor/macro_processor.rs @@ -38,15 +38,7 @@ macro_rules! highlight_debug { }; } -pub enum MacroType { - Function(fn(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String), - String(String) -} - -pub struct MacroProcessor { - macros: HashMap<String, MacroType>, -} - +/// Builtin for defining a new macro fn smp_builtin_define(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 1 { return macro_name.to_string(); @@ -61,6 +53,7 @@ fn smp_builtin_define(smp: &mut MacroProcessor, macro_name: &str, args: &mut [St String::new() } +/// If macro is defined, return second argument, else return third argument if provided fn smp_builtin_ifdef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 2 { return macro_name.to_string(); @@ -76,6 +69,7 @@ fn smp_builtin_ifdef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [Str String::new() } +/// If macro is not defined, return second argument, else return third argument if provided fn smp_builtin_ifndef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 2 { return macro_name.to_string(); @@ -91,6 +85,7 @@ fn smp_builtin_ifndef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [St String::new() } +/// If arguments are equal, return third argument, else return fourth argument if provided fn smp_builtin_ifeq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 3 { return macro_name.to_string(); @@ -106,6 +101,7 @@ fn smp_builtin_ifeq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [Stri String::new() } +/// If arguments are not equal, return third argument, else return fourth argument if provided fn smp_builtin_ifneq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 3 { return macro_name.to_string(); @@ -121,6 +117,7 @@ fn smp_builtin_ifneq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [Str return String::new(); } +/// Include a new file, and process it normally. There is no loop protection here! fn smp_builtin_include(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 1 { return macro_name.to_string(); @@ -130,18 +127,16 @@ fn smp_builtin_include(smp: &mut MacroProcessor, macro_name: &str, args: &mut [S return smp.process_input(&input_file); } +/// Simply execute argument as shell command fn smp_builtin_shell(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { if args.len() < 1 { return macro_name.to_string(); } let arg0 = smp.process_input(&args[0]); - let res = Command::new("sh") - .arg("-c") - .arg(arg0) - .output(); + let res = Command::new("sh").arg("-c").arg(arg0).output(); match res { Ok(output) => String::from_utf8(output.stdout).expect("SMP1"), - Err(_) => String::new() + Err(_) => String::new(), } } @@ -155,16 +150,38 @@ fn smp_builtin_expr(smp: &mut MacroProcessor, macro_name: &str, args: &mut [Stri *arg = smp.process_input(&arg); } - let res = Command::new("expr") - .args(args) - .output(); + let res = Command::new("expr").args(args).output(); match res { Ok(output) => String::from_utf8(output.stdout).expect("SMP1"), - Err(_) => String::new() + Err(_) => String::new(), } } +/// Types of macros, this is to make it easy to store both functions and strings +pub enum MacroType { + /// When expanded, the associated function will be expanded + Function(fn(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String), + /// Will be expanded in-place to the String + String(String), +} + +/// Possible parser states +#[derive(Debug, PartialEq)] +enum ParserState { + Normal, + InMacro, + InMacroArgs, +} + +/// Defines a MacroProcessor object, with it's associated state +/// the state mostly includes the defined macros +pub struct MacroProcessor { + /// All currently defined macros in this MacroProcessor + macros: HashMap<String, MacroType>, +} + impl MacroProcessor { + pub fn new() -> Self { let mut smp = Self { macros: HashMap::new(), @@ -173,28 +190,64 @@ impl MacroProcessor { smp } + /// Bootstrapping-function for defining all builtins, + /// the same way all other macros might be defined fn define_builtins(&mut self) { - self.define_macro_fn(String::from("define"), MacroType::Function(smp_builtin_define)); - self.define_macro_fn(String::from("ifdef"), MacroType::Function(smp_builtin_ifdef)); - self.define_macro_fn(String::from("ifndef"), MacroType::Function(smp_builtin_ifndef)); + self.define_macro_fn( + String::from("define"), + MacroType::Function(smp_builtin_define), + ); + self.define_macro_fn( + String::from("ifdef"), + MacroType::Function(smp_builtin_ifdef), + ); + self.define_macro_fn( + String::from("ifndef"), + MacroType::Function(smp_builtin_ifndef), + ); self.define_macro_fn(String::from("ifeq"), MacroType::Function(smp_builtin_ifeq)); - self.define_macro_fn(String::from("ifneq"), MacroType::Function(smp_builtin_ifneq)); - self.define_macro_fn(String::from("include"), MacroType::Function(smp_builtin_include)); - self.define_macro_fn(String::from("shell"), MacroType::Function(smp_builtin_shell)); + self.define_macro_fn( + String::from("ifneq"), + MacroType::Function(smp_builtin_ifneq), + ); + self.define_macro_fn( + String::from("include"), + MacroType::Function(smp_builtin_include), + ); + self.define_macro_fn( + String::from("shell"), + MacroType::Function(smp_builtin_shell), + ); self.define_macro_fn(String::from("expr"), MacroType::Function(smp_builtin_expr)); - // TODO // format('Result id %d', 3282) } + /// Define a new macro as a string that will be expanded in-place + /// + /// # Arguments + /// + /// * `name` - The name of the new macro + /// * `body` - The body of the new macro, this will be expanded when macro is executed pub fn define_macro(&mut self, name: String, body: String) { self.macros.insert(name, MacroType::String(body)); } + /// Define a new macro as any MacroType + /// + /// # Arguments + /// + /// * `name` - The name of the new macro + /// * `macro_expansion` - The MacroType struct to use. pub fn define_macro_fn(&mut self, name: String, macro_expansion: MacroType) { self.macros.insert(name, macro_expansion); } - + /// This expands a macro definition, and it executes builtin functions, like define + /// + /// # Arguments + /// + /// * `macro_name` - Name of macro to expand if it exists + /// * `args` - List of arguments parsed along with macro invokation (empty list if no arguments were parsed) fn expand_macro(&mut self, macro_name: &str, args: &mut [String]) -> String { let Some(macro_body) = self.macros.get(macro_name) else { return format!("{}", macro_name); @@ -203,18 +256,32 @@ impl MacroProcessor { match macro_body { MacroType::String(body) => { let mut expanded = body.clone(); + // The expanded macro, should _probably_ be expanded again + // The below is a okay _idea_, but I am not sure if I want to have this syntax for + // functions defined in normal smp code for (i, arg) in args.iter().enumerate() { let placeholder = format!("${}", i + 1); expanded = expanded.replace(&placeholder, arg); } return expanded; - }, + } MacroType::Function(func) => { return func(self, macro_name, args); - }, + } } } + /// Do macro processing of a input string + /// + /// This is the main function used for processing a input string, + /// will return the processed string. + /// Will be called recursively if needed. + /// Subsequent calls will keep the state from the previous call. + /// This includes macro definitions. + /// + /// # Arguments + /// + /// * `input` - The text to process pub fn process_input(&mut self, input: &str) -> String { let mut output = String::new(); let mut state = ParserState::Normal; @@ -296,10 +363,3 @@ impl MacroProcessor { output } } - -#[derive(Debug, PartialEq)] -enum ParserState { - Normal, - InMacro, - InMacroArgs, -} diff --git a/src/macro_processor/main.rs b/src/macro_processor/main.rs index 5575aa6..247fc85 100644 --- a/src/macro_processor/main.rs +++ b/src/macro_processor/main.rs @@ -1,11 +1,11 @@ +use skaldpress::macro_processor::MacroProcessor; use std::env; use std::fs; -use skaldpress::macro_processor::MacroProcessor; fn main() { let args: Vec<String> = env::args().collect(); let input_file = fs::read_to_string(&args[1]).expect("Failed to read input file"); - + let mut macro_processor = MacroProcessor::new(); let final_output = macro_processor.process_input(&input_file); println!("{}", final_output); diff --git a/src/skaldpress/main.rs b/src/skaldpress/main.rs index de6088f..56abf70 100644 --- a/src/skaldpress/main.rs +++ b/src/skaldpress/main.rs @@ -92,17 +92,13 @@ fn compile_file(file_path: &Path) -> Result<String, Box<dyn std::error::Error>> return Ok(file_content); }; - let template_file = format!( - "{}{}.html", - TEMPLATES_DIR, - template - ); + let template_file = format!("{}{}.html", TEMPLATES_DIR, template); //println!( // "Processing template {} for content file {:?}", // template_file, file_path //); let template = fs::read_to_string(template_file).expect("Failed to read template"); - + let mut macro_processor = MacroProcessor::new(); for (key, value) in map { macro_processor.define_macro(format!("METADATA_{}", key), value.to_string()); @@ -114,7 +110,9 @@ fn compile_file(file_path: &Path) -> Result<String, Box<dyn std::error::Error>> } fn compile_file_and_write(source_file_path: &Path) -> Result<(), Box<dyn std::error::Error>> { - let dest_file_path = Path::new(BUILD_DIR).join(source_file_path.strip_prefix(CONTENT_DIR).expect("SP14")).with_extension("html"); + let dest_file_path = Path::new(BUILD_DIR) + .join(source_file_path.strip_prefix(CONTENT_DIR).expect("SP14")) + .with_extension("html"); std::fs::create_dir_all(&dest_file_path.parent().expect("SP19")).expect("SP10"); @@ -130,16 +128,12 @@ fn compile_files_in_directory(directory: &Path) { let metadata = fs::metadata(&path).expect("SP6"); if metadata.is_file() { - println!( - "Compiling {:#?}", - path.as_path() - ); + println!("Compiling {:#?}", path.as_path()); compile_file_and_write(path.as_path()).expect("SP12"); } else if metadata.is_dir() { compile_files_in_directory(path.as_path()); } } - } fn main() { diff --git a/src/skaldpress/mod.rs b/src/skaldpress/mod.rs index e69de29..8b13789 100644 --- a/src/skaldpress/mod.rs +++ b/src/skaldpress/mod.rs @@ -0,0 +1 @@ + |