diff options
-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 | ||||
-rw-r--r-- | tests/macro_processor.rs | 162 |
6 files changed, 161 insertions, 156 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 @@ + diff --git a/tests/macro_processor.rs b/tests/macro_processor.rs index 2f54961..95edfeb 100644 --- a/tests/macro_processor.rs +++ b/tests/macro_processor.rs @@ -7,21 +7,18 @@ fn run_macro_processor(input: &str) -> String { #[test] fn test_smn_empty_string() { - assert_eq!( - run_macro_processor(""), - "" - ); + assert_eq!(run_macro_processor(""), ""); } #[test] fn test_smp_non_macro_html() { assert_eq!( run_macro_processor( -"<html> + "<html> <p>This is a <em>test</em></p> -</html>"), - -"<html> +</html>" + ), + "<html> <p>This is a <em>test</em></p> </html>", ); @@ -31,10 +28,10 @@ fn test_smp_non_macro_html() { fn test_smp_define_1() { assert_eq!( run_macro_processor( -"define(TEMP, testvalue) -TEMP"), - -" + "define(TEMP, testvalue) +TEMP" + ), + " testvalue", ); } @@ -42,10 +39,8 @@ testvalue", #[test] fn test_smp_define_2() { assert_eq!( - run_macro_processor( -"define(TEMP, TEMP2)define(TEMP)ifdef(TEMP2, TEMP2_ISDEF)"), - -"TEMP2_ISDEF", + run_macro_processor("define(TEMP, TEMP2)define(TEMP)ifdef(TEMP2, TEMP2_ISDEF)"), + "TEMP2_ISDEF", ); } @@ -53,10 +48,10 @@ fn test_smp_define_2() { fn test_smp_dnl_1() { assert_eq!( run_macro_processor( -"SNNL -test"), - -"test", + "SNNL +test" + ), + "test", ); } @@ -64,10 +59,10 @@ test"), fn test_smp_dnl_2() { assert_eq!( run_macro_processor( -"SNNL -test"), - -"test", + "SNNL +test" + ), + "test", ); } @@ -75,20 +70,19 @@ test"), fn test_smp_dnl_3() { assert_eq!( run_macro_processor( -"define(MAC1, test)SNNL + "define(MAC1, test)SNNL MAC1 SNNL -test"), - -"test test", +test" + ), + "test test", ); } #[test] fn test_smp_ifdef_0() { assert_eq!( - run_macro_processor( -"define(MAC1, test)ifdef(MAC1, MAC1_ISDEF)"), -"MAC1_ISDEF", + run_macro_processor("define(MAC1, test)ifdef(MAC1, MAC1_ISDEF)"), + "MAC1_ISDEF", ); } @@ -96,54 +90,41 @@ fn test_smp_ifdef_0() { fn test_smp_ifdef_1() { assert_eq!( run_macro_processor( -"define(MAC1, test)SNNL + "define(MAC1, test)SNNL ifdef(MAC1, MAC1_ISDEF) -ifdef(MAC2, MAC2_ISDEF, MAC2_ISNDEF)"), - -"MAC1_ISDEF +ifdef(MAC2, MAC2_ISDEF, MAC2_ISNDEF)" + ), + "MAC1_ISDEF MAC2_ISNDEF", ); } - #[test] fn test_smp_ifdef_2() { - assert_eq!( - run_macro_processor( -"ifdef(MAC, MAC_ISDEF)"), - -"", - ); + assert_eq!(run_macro_processor("ifdef(MAC, MAC_ISDEF)"), "",); } #[test] fn test_smp_ifdef_3() { assert_eq!( - run_macro_processor( -"ifdef(MAC, MAC_ISDEF, MAC_ISNDEF)"), - -"MAC_ISNDEF", + run_macro_processor("ifdef(MAC, MAC_ISDEF, MAC_ISNDEF)"), + "MAC_ISNDEF", ); } #[test] fn test_smp_ifndef_1() { - assert_eq!( - run_macro_processor( -"ifndef(MAC, MAC_ISNDEF)"), - -"MAC_ISNDEF", - ); + assert_eq!(run_macro_processor("ifndef(MAC, MAC_ISNDEF)"), "MAC_ISNDEF",); } #[test] fn test_smp_ifndef_2() { assert_eq!( run_macro_processor( -"define(MAC, test)SNNL -ifndef(MAC, MAC_ISNDEF, MAC_ISDEF)"), - -"MAC_ISDEF", + "define(MAC, test)SNNL +ifndef(MAC, MAC_ISNDEF, MAC_ISDEF)" + ), + "MAC_ISDEF", ); } @@ -151,20 +132,18 @@ ifndef(MAC, MAC_ISNDEF, MAC_ISDEF)"), fn test_smp_ifndef_3() { assert_eq!( run_macro_processor( -"define(MAC, test)SNNL -ifndef(MAC, MAC_ISNDEF)"), - -"", + "define(MAC, test)SNNL +ifndef(MAC, MAC_ISNDEF)" + ), + "", ); } #[test] fn test_smp_include_1() { assert_eq!( - run_macro_processor( -"include(tests/example_include.smp)"), - -"", + run_macro_processor("include(tests/example_include.smp)"), + "", ); } @@ -172,83 +151,56 @@ fn test_smp_include_1() { fn test_smp_include_2() { assert_eq!( run_macro_processor( -"include(tests/example_include.smp)SNNL -ifdef(SMP, SMP_ISDEF, SMP_ISNDEF)"), - -"SMP_ISDEF", + "include(tests/example_include.smp)SNNL +ifdef(SMP, SMP_ISDEF, SMP_ISNDEF)" + ), + "SMP_ISDEF", ); } #[test] fn test_smp_ifeq_1() { - assert_eq!( - run_macro_processor("ifeq(a, a, true, false)"), - "true", - ); + assert_eq!(run_macro_processor("ifeq(a, a, true, false)"), "true",); } #[test] fn test_smp_ifeq_2() { - assert_eq!( - run_macro_processor("ifeq(a, b, true, false)"), - "false", - ); + assert_eq!(run_macro_processor("ifeq(a, b, true, false)"), "false",); } #[test] fn test_smp_ifeq_3() { - assert_eq!( - run_macro_processor("ifeq(a, a, true)"), - "true", - ); + assert_eq!(run_macro_processor("ifeq(a, a, true)"), "true",); } #[test] fn test_smp_ifeq_4() { - assert_eq!( - run_macro_processor("ifeq(a, b, true)"), - "", - ); + assert_eq!(run_macro_processor("ifeq(a, b, true)"), "",); } #[test] fn test_smp_ifneq_1() { - assert_eq!( - run_macro_processor("ifneq(a, a, true, false)"), - "false", - ); + assert_eq!(run_macro_processor("ifneq(a, a, true, false)"), "false",); } #[test] fn test_smp_ifneq_2() { - assert_eq!( - run_macro_processor("ifneq(a, b, true, false)"), - "true", - ); + assert_eq!(run_macro_processor("ifneq(a, b, true, false)"), "true",); } #[test] fn test_smp_ifneq_3() { - assert_eq!( - run_macro_processor("ifneq(a, a, true)"), - "", - ); + assert_eq!(run_macro_processor("ifneq(a, a, true)"), "",); } #[test] fn test_smp_ifneq_4() { - assert_eq!( - run_macro_processor("ifneq(a, b, true)"), - "true", - ); + assert_eq!(run_macro_processor("ifneq(a, b, true)"), "true",); } #[test] fn test_smp_shell_1() { - assert_eq!( - run_macro_processor("shell(printf test)"), - "test", - ); + assert_eq!(run_macro_processor("shell(printf test)"), "test",); } #[test] @@ -259,5 +211,3 @@ fn test_smp_expr_1() { ", ); } - - |