summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorQrius <[email protected]>2024-09-26 00:11:05 +0200
committerQrius <[email protected]>2024-09-26 00:11:05 +0200
commit3b35f97b07d3a6b439544097fe36628619f95d4f (patch)
tree3ae0eca22cd3dcaef0d7f43efb0c6f0d3c738dac /src
parent541f983def407f1f2a3ebed859e37d9f00c83111 (diff)
downloadskaldpress-3b35f97b07d3a6b439544097fe36628619f95d4f.tar.gz
skaldpress-3b35f97b07d3a6b439544097fe36628619f95d4f.zip
Change way macros are invoked, add a bunch of tests
Diffstat (limited to 'src')
-rw-r--r--src/macro_processor/macro_processor.rs275
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(&macro_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(&macro_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(&macro_name, &mut macro_args);
output.push_str(&expanded);
state = ParserState::Normal;
- state_previous = ParserState::InMacroArgs;
macro_name.clear();
macro_args.clear();
argument.clear();