summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/macro_processor/macro_processor.rs275
-rw-r--r--tests/example_include.smp2
-rw-r--r--tests/macro_processor.rs55
3 files changed, 198 insertions, 134 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();
diff --git a/tests/example_include.smp b/tests/example_include.smp
index ce45d39..f38b21f 100644
--- a/tests/example_include.smp
+++ b/tests/example_include.smp
@@ -1 +1 @@
-define(SMP)DNL
+define(SMP)SNNL
diff --git a/tests/macro_processor.rs b/tests/macro_processor.rs
index 0c3651c..2f54961 100644
--- a/tests/macro_processor.rs
+++ b/tests/macro_processor.rs
@@ -53,7 +53,7 @@ fn test_smp_define_2() {
fn test_smp_dnl_1() {
assert_eq!(
run_macro_processor(
-"DNL
+"SNNL
test"),
"test",
@@ -64,7 +64,7 @@ test"),
fn test_smp_dnl_2() {
assert_eq!(
run_macro_processor(
-"DNL
+"SNNL
test"),
"test",
@@ -75,8 +75,8 @@ test"),
fn test_smp_dnl_3() {
assert_eq!(
run_macro_processor(
-"define(MAC1, test)DNL
-MAC1 DNL
+"define(MAC1, test)SNNL
+MAC1 SNNL
test"),
"test test",
@@ -96,7 +96,7 @@ fn test_smp_ifdef_0() {
fn test_smp_ifdef_1() {
assert_eq!(
run_macro_processor(
-"define(MAC1, test)DNL
+"define(MAC1, test)SNNL
ifdef(MAC1, MAC1_ISDEF)
ifdef(MAC2, MAC2_ISDEF, MAC2_ISNDEF)"),
@@ -140,7 +140,7 @@ fn test_smp_ifndef_1() {
fn test_smp_ifndef_2() {
assert_eq!(
run_macro_processor(
-"define(MAC, test)DNL
+"define(MAC, test)SNNL
ifndef(MAC, MAC_ISNDEF, MAC_ISDEF)"),
"MAC_ISDEF",
@@ -151,7 +151,7 @@ ifndef(MAC, MAC_ISNDEF, MAC_ISDEF)"),
fn test_smp_ifndef_3() {
assert_eq!(
run_macro_processor(
-"define(MAC, test)DNL
+"define(MAC, test)SNNL
ifndef(MAC, MAC_ISNDEF)"),
"",
@@ -159,7 +159,7 @@ ifndef(MAC, MAC_ISNDEF)"),
}
#[test]
-fn test_include_1() {
+fn test_smp_include_1() {
assert_eq!(
run_macro_processor(
"include(tests/example_include.smp)"),
@@ -169,10 +169,10 @@ fn test_include_1() {
}
#[test]
-fn test_include_2() {
+fn test_smp_include_2() {
assert_eq!(
run_macro_processor(
-"include(tests/example_include.smp)DNL
+"include(tests/example_include.smp)SNNL
ifdef(SMP, SMP_ISDEF, SMP_ISNDEF)"),
"SMP_ISDEF",
@@ -180,7 +180,7 @@ ifdef(SMP, SMP_ISDEF, SMP_ISNDEF)"),
}
#[test]
-fn test_ifeq_1() {
+fn test_smp_ifeq_1() {
assert_eq!(
run_macro_processor("ifeq(a, a, true, false)"),
"true",
@@ -188,7 +188,7 @@ fn test_ifeq_1() {
}
#[test]
-fn test_ifeq_2() {
+fn test_smp_ifeq_2() {
assert_eq!(
run_macro_processor("ifeq(a, b, true, false)"),
"false",
@@ -196,7 +196,7 @@ fn test_ifeq_2() {
}
#[test]
-fn test_ifeq_3() {
+fn test_smp_ifeq_3() {
assert_eq!(
run_macro_processor("ifeq(a, a, true)"),
"true",
@@ -204,7 +204,7 @@ fn test_ifeq_3() {
}
#[test]
-fn test_ifeq_4() {
+fn test_smp_ifeq_4() {
assert_eq!(
run_macro_processor("ifeq(a, b, true)"),
"",
@@ -212,7 +212,7 @@ fn test_ifeq_4() {
}
#[test]
-fn test_ifneq_1() {
+fn test_smp_ifneq_1() {
assert_eq!(
run_macro_processor("ifneq(a, a, true, false)"),
"false",
@@ -220,7 +220,7 @@ fn test_ifneq_1() {
}
#[test]
-fn test_ifneq_2() {
+fn test_smp_ifneq_2() {
assert_eq!(
run_macro_processor("ifneq(a, b, true, false)"),
"true",
@@ -228,7 +228,7 @@ fn test_ifneq_2() {
}
#[test]
-fn test_ifneq_3() {
+fn test_smp_ifneq_3() {
assert_eq!(
run_macro_processor("ifneq(a, a, true)"),
"",
@@ -236,9 +236,28 @@ fn test_ifneq_3() {
}
#[test]
-fn test_ifneq_4() {
+fn test_smp_ifneq_4() {
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",
+ );
+}
+
+#[test]
+fn test_smp_expr_1() {
+ assert_eq!(
+ run_macro_processor("expr(1, +, 1)"),
+ "2
+",
+ );
+}
+
+