diff options
author | Qrius <[email protected]> | 2024-09-26 00:11:05 +0200 |
---|---|---|
committer | Qrius <[email protected]> | 2024-09-26 00:11:05 +0200 |
commit | 99205d6a5635dc8cb1a9d7429e6b6d0880518cb9 (patch) | |
tree | b6fb81830c1795cc115a0b13863123604df5bb5d /src/macro_processor | |
parent | 3b35f97b07d3a6b439544097fe36628619f95d4f (diff) | |
download | skaldpress-99205d6a5635dc8cb1a9d7429e6b6d0880518cb9.tar.gz skaldpress-99205d6a5635dc8cb1a9d7429e6b6d0880518cb9.zip |
Clean up things a bit
Diffstat (limited to 'src/macro_processor')
-rw-r--r-- | src/macro_processor/macro_processor.rs | 130 | ||||
-rw-r--r-- | src/macro_processor/main.rs | 4 |
2 files changed, 97 insertions, 37 deletions
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); |