diff options
Diffstat (limited to 'src/macro_processor')
-rw-r--r-- | src/macro_processor/error.rs | 23 | ||||
-rw-r--r-- | src/macro_processor/macro_processor.rs | 129 | ||||
-rw-r--r-- | src/macro_processor/main.rs | 14 | ||||
-rw-r--r-- | src/macro_processor/mod.rs | 1 |
4 files changed, 120 insertions, 47 deletions
diff --git a/src/macro_processor/error.rs b/src/macro_processor/error.rs new file mode 100644 index 0000000..074a8ef --- /dev/null +++ b/src/macro_processor/error.rs @@ -0,0 +1,23 @@ +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +pub enum SMPError { + IncludeError(u8, std::io::Error, String), + ShellCommandError(u8, Box<dyn Error>), +} + +impl fmt::Display for SMPError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + SMPError::IncludeError(code, _, file) => { + write!(f, "[SMP{}] Error reading file \"{}\"", code, file) + } + SMPError::ShellCommandError(code, e) => { + write!(f, "[SMP{}] Error running shell command \"{:#?}\"", code, e) + } + } + } +} + +impl std::error::Error for SMPError {} diff --git a/src/macro_processor/macro_processor.rs b/src/macro_processor/macro_processor.rs index 0f8b217..f26780e 100644 --- a/src/macro_processor/macro_processor.rs +++ b/src/macro_processor/macro_processor.rs @@ -1,3 +1,4 @@ +use crate::macro_processor::error::SMPError; use std::collections::HashMap; use std::fs; use std::process::Command; @@ -39,24 +40,32 @@ macro_rules! highlight_debug { } /// Builtin for defining a new macro -fn smp_builtin_define(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_define( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); + let arg0 = smp.process_input(&args[0])?; if args.len() > 1 { - let arg1 = smp.process_input(&args[1]); + let arg1 = smp.process_input(&args[1])?; smp.define_macro(arg0, arg1); } else { smp.define_macro(arg0, String::new()); } - String::new() + Ok(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 { +fn smp_builtin_ifdef( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 2 { - return macro_name.to_string(); + return Ok(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 @@ -66,13 +75,17 @@ fn smp_builtin_ifdef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [Str if args.len() > 2 { return smp.process_input(&args[2]); } - String::new() + Ok(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 { +fn smp_builtin_ifndef( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 2 { - return macro_name.to_string(); + return Ok(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 @@ -82,85 +95,113 @@ fn smp_builtin_ifndef(smp: &mut MacroProcessor, macro_name: &str, args: &mut [St if args.len() > 2 { return smp.process_input(&args[2]); } - String::new() + Ok(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 { +fn smp_builtin_ifeq( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 3 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); - let arg1 = smp.process_input(&args[1]); + 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() + Ok(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 { +fn smp_builtin_ifneq( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 3 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); - let arg1 = smp.process_input(&args[1]); + 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(); + return Ok(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 { +fn smp_builtin_include( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 1 { - return macro_name.to_string(); + return Ok(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"); + let arg0 = smp.process_input(&args[0])?; + let input_file = fs::read_to_string(&arg0).map_err(|e| SMPError::IncludeError(2, e, arg0))?; 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 { +fn smp_builtin_shell( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } - let arg0 = smp.process_input(&args[0]); + 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(), + Ok(output) => String::from_utf8(output.stdout) + .map_err(|e| SMPError::ShellCommandError(1, Box::new(e))), + Err(_) => Ok(String::new()), } } /// Would like one that is better than this tbh -fn smp_builtin_expr(smp: &mut MacroProcessor, macro_name: &str, args: &mut [String]) -> String { +fn smp_builtin_expr( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], +) -> Result<String, SMPError> { if args.len() < 1 { - return macro_name.to_string(); + return Ok(macro_name.to_string()); } for arg in args.iter_mut() { - *arg = smp.process_input(&arg); + *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(), + Ok(output) => String::from_utf8(output.stdout) + .map_err(|e| SMPError::ShellCommandError(1, Box::new(e))), + Err(_) => Ok(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), + Function( + fn( + smp: &mut MacroProcessor, + macro_name: &str, + args: &mut [String], + ) -> Result<String, SMPError>, + ), /// Will be expanded in-place to the String String(String), } @@ -247,9 +288,9 @@ impl MacroProcessor { /// /// * `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 { + fn expand_macro(&mut self, macro_name: &str, args: &mut [String]) -> Result<String, SMPError> { let Some(macro_body) = self.macros.get(macro_name) else { - return format!("{}", macro_name); + return Ok(format!("{}", macro_name)); }; match macro_body { @@ -262,7 +303,7 @@ impl MacroProcessor { let placeholder = format!("${}", i + 1); expanded = expanded.replace(&placeholder, arg); } - return expanded; + return Ok(expanded); } MacroType::Function(func) => { return func(self, macro_name, args); @@ -281,7 +322,7 @@ impl MacroProcessor { /// # Arguments /// /// * `input` - The text to process - pub fn process_input(&mut self, input: &str) -> String { + pub fn process_input(&mut self, input: &str) -> Result<String, SMPError> { let mut output = String::new(); let mut state = ParserState::Normal; let mut macro_name = String::new(); @@ -325,7 +366,7 @@ impl MacroProcessor { if macro_name == "SNNL" { skip_next_line_ending = c != '\n'; } else { - let expanded = self.expand_macro(¯o_name, &mut []); + let expanded = self.expand_macro(¯o_name, &mut [])?; output.push_str(&expanded); output.push(c); } @@ -338,7 +379,7 @@ impl MacroProcessor { highlight_debug!("\x1b[32m\x1b[7m", input, (macro_name_start -> i)); macro_args.push(argument.trim().to_string()); - let expanded = self.expand_macro(¯o_name, &mut macro_args); + let expanded = self.expand_macro(¯o_name, &mut macro_args)?; output.push_str(&expanded); state = ParserState::Normal; macro_name.clear(); @@ -356,9 +397,9 @@ impl MacroProcessor { // Handle cases where the text ends with a macro without arguments if !macro_name.is_empty() { - output.push_str(&self.expand_macro(¯o_name, &mut [])); + output.push_str(&self.expand_macro(¯o_name, &mut [])?); } - output + Ok(output) } } diff --git a/src/macro_processor/main.rs b/src/macro_processor/main.rs index 247fc85..2d34952 100644 --- a/src/macro_processor/main.rs +++ b/src/macro_processor/main.rs @@ -4,9 +4,17 @@ use std::fs; 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 input_file = match fs::read_to_string(&args[1]) { + Ok(x) => x, + Err(e) => { + println!("Could not read input-file \"{}\": {}", args[1], e); + std::process::exit(1); + } + }; let mut macro_processor = MacroProcessor::new(); - let final_output = macro_processor.process_input(&input_file); - println!("{}", final_output); + match macro_processor.process_input(&input_file) { + Ok(out) => println!("{}", out), + Err(e) => println!("Error {}", e), + } } diff --git a/src/macro_processor/mod.rs b/src/macro_processor/mod.rs index 2fccbda..ffc2144 100644 --- a/src/macro_processor/mod.rs +++ b/src/macro_processor/mod.rs @@ -1,2 +1,3 @@ +pub mod error; pub mod macro_processor; pub use macro_processor::MacroProcessor; |