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 | 3b35f97b07d3a6b439544097fe36628619f95d4f (patch) | |
tree | 3ae0eca22cd3dcaef0d7f43efb0c6f0d3c738dac /src/macro_processor | |
parent | 541f983def407f1f2a3ebed859e37d9f00c83111 (diff) | |
download | skaldpress-3b35f97b07d3a6b439544097fe36628619f95d4f.tar.gz skaldpress-3b35f97b07d3a6b439544097fe36628619f95d4f.zip |
Change way macros are invoked, add a bunch of tests
Diffstat (limited to 'src/macro_processor')
-rw-r--r-- | src/macro_processor/macro_processor.rs | 275 |
1 files changed, 160 insertions, 115 deletions
diff --git a/src/macro_processor/macro_processor.rs b/src/macro_processor/macro_processor.rs index 306aa04..0d931f9 100644 --- a/src/macro_processor/macro_processor.rs +++ b/src/macro_processor/macro_processor.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::fs; +use std::process::Command; // print only with debug_assertions macro_rules! dprint { @@ -37,143 +38,191 @@ macro_rules! highlight_debug { }; } -#[derive(Debug)] +pub enum MacroType { + Function(fn(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String), + String(String) +} + pub struct MacroProcessor { - macros: HashMap<String, String>, + macros: HashMap<String, MacroType>, } -impl MacroProcessor { - pub fn new() -> Self { - Self { - macros: HashMap::new(), - } +fn smp_builtin_define(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]); + if args.len() > 1 { + let arg1 = smp.process_input(&args[1]); + smp.define_macro(arg0, arg1); + } else { + smp.define_macro(arg0, String::new()); } + String::new() +} - pub fn define_macro(&mut self, name: String, body: String) { - self.macros.insert(name, body); +fn smp_builtin_ifdef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { + if args.len() < 2 { + return macro_name.to_string(); + } + // We need to expand the first argument here as well, but we need to make the parser + // support literal, and phrase strings + if smp.macros.contains_key(&args[0]) { + return smp.process_input(&args[1]); } + if args.len() > 2 { + return smp.process_input(&args[2]); + } + String::new() +} - /// This expands a macro definition, and it executes builtin functions, like define - /// Currently, you cannot overwrite builtin's. - /// This is partly by design, and I don't currently see why I would want that. - /// In the future, this may change - /// - /// (The HashMap might become a HashMap<String, Box<dyn FnMut>> or something similar. - /// Then, all builtins would also be functions, and "normal" macros, would simply - /// be closures returning the string) - fn expand_macro(&mut self, macro_name: &str, args: &mut [String]) -> String { - if macro_name == "define" { - if args.len() < 1 { - println!("Missing argument(s) to `define`, found {} but expected 1 or 2", args.len()); - return String::new(); - } - let arg0 = self.process_input(&args[0]); - if args.len() > 1 { - let arg1 = self.process_input(&args[1]); - self.define_macro(arg0, arg1); - } else { - self.define_macro(arg0, String::new()); - } - return String::new(); - } +fn smp_builtin_ifndef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { + if args.len() < 2 { + return macro_name.to_string(); + } + // We need to expand the first argument here as well, but we need to make the parser + // support literal, and phrase strings + if !smp.macros.contains_key(&args[0]) { + return smp.process_input(&args[1]); + } + if args.len() > 2 { + return smp.process_input(&args[2]); + } + String::new() +} - if macro_name == "ifdef" { - if args.len() < 2 { - println!("Missing argument(s) to `ifdef`, found {} but expected 2 or 3", args.len()); - return String::new(); - } - // We need to expand the first argument here as well, but we need to make the parser - // support literal, and phrase strings - if self.macros.contains_key(&args[0]) { - return self.process_input(&args[1]); - } - if args.len() > 2 { - return self.process_input(&args[2]); - } - return String::new(); - } +fn smp_builtin_ifeq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { + if args.len() < 3 { + return macro_name.to_string(); + } + let arg0 = smp.process_input(&args[0]); + let arg1 = smp.process_input(&args[1]); + if arg0 == arg1 { + return smp.process_input(&args[2]); + } + if args.len() > 3 { + return smp.process_input(&args[3]); + } + String::new() +} - if macro_name == "ifndef" { - if args.len() < 2 { - println!("Missing argument(s) to `ifndef`, found {} but expected 2 or 3", args.len()); - return String::new(); - } - // We need to expand the first argument here as well, but we need to make the parser - // support literal, and phrase strings - if !self.macros.contains_key(&args[0]) { - return self.process_input(&args[1]); - } - if args.len() > 2 { - return self.process_input(&args[2]); - } - return String::new(); - } +fn smp_builtin_ifneq(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { + if args.len() < 3 { + return macro_name.to_string(); + } + let arg0 = smp.process_input(&args[0]); + let arg1 = smp.process_input(&args[1]); + if arg0 != arg1 { + return smp.process_input(&args[2]); + } + if args.len() > 3 { + return smp.process_input(&args[3]); + } + return String::new(); +} - if macro_name == "ifeq" { - if args.len() < 3 { - println!("Missing argument(s) to `ifeq`, found {} but expected 3 or 4", args.len()); - return String::new(); - } - let arg0 = self.process_input(&args[0]); - let arg1 = self.process_input(&args[1]); - if arg0 == arg1 { - return self.process_input(&args[2]); - } - if args.len() > 3 { - return self.process_input(&args[3]); - } - return String::new(); - } +fn smp_builtin_include(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 input_file = fs::read_to_string(&arg0).expect("Failed to read input file"); + return smp.process_input(&input_file); +} - if macro_name == "ifneq" { - if args.len() < 3 { - println!("Missing argument(s) to `ifneq`, found {} but expected 3 or 4", args.len()); - return String::new(); - } - let arg0 = self.process_input(&args[0]); - let arg1 = self.process_input(&args[1]); - if arg0 != arg1 { - return self.process_input(&args[2]); - } - if args.len() > 3 { - return self.process_input(&args[3]); - } - return String::new(); - } - - if macro_name == "include" { - if args.len() < 1 { - println!("Missing argument(s) to `include`, found {} but expected 1", args.len()); - return String::new(); - } - let arg0 = self.process_input(&args[0]); - let input_file = fs::read_to_string(&arg0).expect("Failed to read input file"); - return self.process_input(&input_file); - } +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(); + match res { + Ok(output) => String::from_utf8(output.stdout).expect("SMP1"), + Err(_) => String::new() + } +} - //Expand macro name? somwhat unsure on how to do this safely - //let macro_name = self.process_input(macro_name); +/// Would like one that is better than this tbh +fn smp_builtin_expr(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { + if args.len() < 1 { + return macro_name.to_string(); + } + + for arg in args.iter_mut() { + *arg = smp.process_input(&arg); + } + let res = Command::new("expr") + .args(args) + .output(); + match res { + Ok(output) => String::from_utf8(output.stdout).expect("SMP1"), + Err(_) => String::new() + } +} + +impl MacroProcessor { + pub fn new() -> Self { + let mut smp = Self { + macros: HashMap::new(), + }; + smp.define_builtins(); + smp + } + + 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("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("expr"), MacroType::Function(smp_builtin_expr)); + // TODO + // format('Result id %d', 3282) + } + + pub fn define_macro(&mut self, name: String, body: String) { + self.macros.insert(name, MacroType::String(body)); + } + + 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 + 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); }; - let mut expanded = macro_body.clone(); - for (i, arg) in args.iter().enumerate() { - let placeholder = format!("${}", i + 1); - expanded = expanded.replace(&placeholder, arg); + match macro_body { + MacroType::String(body) => { + let mut expanded = body.clone(); + 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); + }, } - expanded } pub fn process_input(&mut self, input: &str) -> String { let mut output = String::new(); let mut state = ParserState::Normal; - let mut state_previous = ParserState::Normal; let mut macro_name = String::new(); let mut macro_args = Vec::new(); let mut argument = String::new(); let mut macro_name_start = 0; + let mut skip_next_line_ending = false; let mut in_quote_single = false; @@ -193,7 +242,6 @@ impl MacroProcessor { if c.is_alphanumeric() { state = ParserState::InMacro; - state_previous = ParserState::Normal; macro_name.push(c); } else { output.push(c); @@ -204,12 +252,11 @@ impl MacroProcessor { macro_name.push(c); } else if c == '(' { state = ParserState::InMacroArgs; - state_previous = ParserState::InMacro; } else { if self.macros.contains_key(¯o_name) { highlight_debug!("\x1b[32m\x1b[7m", input, (macro_name_start -> i)); } - if macro_name == "DNL" { + if macro_name == "SNNL" { skip_next_line_ending = c != '\n'; } else { let expanded = self.expand_macro(¯o_name, &mut []); @@ -218,7 +265,6 @@ impl MacroProcessor { } macro_name.clear(); state = ParserState::Normal; - state_previous = ParserState::InMacro; } } ParserState::InMacroArgs => { @@ -229,7 +275,6 @@ impl MacroProcessor { let expanded = self.expand_macro(¯o_name, &mut macro_args); output.push_str(&expanded); state = ParserState::Normal; - state_previous = ParserState::InMacroArgs; macro_name.clear(); macro_args.clear(); argument.clear(); |