diff options
Diffstat (limited to 'src/macro_processor')
| -rw-r--r-- | src/macro_processor/macro_processor.rs | 188 | 
1 files changed, 177 insertions, 11 deletions
diff --git a/src/macro_processor/macro_processor.rs b/src/macro_processor/macro_processor.rs index 7839963..ee7e269 100644 --- a/src/macro_processor/macro_processor.rs +++ b/src/macro_processor/macro_processor.rs @@ -56,6 +56,52 @@ fn smp_builtin_define(      Ok(String::new())  } +/// Builtin for undefining a macro +fn smp_builtin_undefine( +    smp: &mut MacroProcessor, +    macro_name: &str, +    args: &mut [String], +) -> Result<String, SMPError> { +    if args.len() < 1 { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("Wrong number of arguments, expected at least 1"), +            )); +        return Ok(macro_name.to_string()); +    } +    if let None = smp.macros.remove(&args[0]) { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("Macro already not defined"), +            )); +    } +    Ok(String::new()) +} + +/// Builtin for defining a new macro +fn smp_builtin_define_array( +    smp: &mut MacroProcessor, +    macro_name: &str, +    args: &mut [String], +) -> Result<String, SMPError> { +    if args.len() < 1 { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("Wrong number of arguments, expected at least 1"), +            )); +        return Ok(macro_name.to_string()); +    } +    let arg0 = smp.process_input(&args[0])?; +    smp.define_macro(arg0, MacroType::Array(Vec::new())); +    Ok(String::new()) +} +  /// If macro is defined, return second argument, else return third argument if provided  fn smp_builtin_ifdef(      smp: &mut MacroProcessor, @@ -295,19 +341,77 @@ fn smp_builtin_array_push(      macro_name: &str,      args: &mut [String],  ) -> Result<String, SMPError> { -    for arg in args.iter() { -        if let Err(e) = smp.array_push(macro_name, MacroType::String(arg.to_string())) { -            smp.warnings -                .push(MacroProcessorWarning::from_macro_invocation( -                    macro_name, -                    args, -                    format!("Error executing array_push ({:?})", e), -                )); -        } +    let mut args_iter = args.iter(); +    let Some(array_name) = args_iter.next() else { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("Invalid arguments to array_push"), +            )); +        return Ok(String::new()); +    }; +    let mut def = Vec::new(); +    while let Some(arg) = args_iter.next() { +        def.push(MacroType::String(smp.process_input(arg)?)); +    } +    if let Err(e) = smp.array_push(array_name, MacroType::Array(def)) { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("Error executing array_push ({:?})", e), +            ));      }      Ok(String::new())  } +/// Push any arguments to array macro +/// Process each element in a array as a macro-invokation on the second argument +/// Not the best way to do this, it is not sensibly recursive. +fn smp_builtin_array_each( +    smp: &mut MacroProcessor, +    macro_name: &str, +    args: &mut [String], +) -> Result<String, SMPError> { +    let Some(macro_body) = smp.macros.get(&args[0]) else { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("{:?} is not a macro", args[0]), +            )); +        return Ok(String::new()); +    }; +    let MacroType::Array(array) = macro_body else { +        smp.warnings +            .push(MacroProcessorWarning::from_macro_invocation( +                macro_name, +                args, +                format!("{:?} is not a macro of type array", args[0]), +            )); +        return Ok(String::new()); +    }; +    let mut out = String::new(); +    for el in array.clone() { +        let exp = match el { +            MacroType::String(s) => smp.expand_macro(&args[1], &mut [s])?, +            MacroType::Array(a) => { +                let mut out = Vec::new(); +                for _el in a { +                    out.push(_el.to_string()); +                } +                let expanded = smp.expand_macro(&args[1], &mut out)?; +                smp.process_input(&expanded)? +            } +            _ => String::new(), +        }; +        out.push_str(&exp); +    } + +    Ok(out) +} +  #[cfg(feature = "time")]  fn smp_builtin_format_time(      smp: &mut MacroProcessor, @@ -354,7 +458,7 @@ fn macro_name_clean<'a>(macro_name: &'a str) -> &'a str {  }  /// Types of macros, this is to make it easy to store both functions and strings -#[derive(Clone)] +#[derive(Clone, Debug)]  pub enum MacroType {      /// When expanded, the associated function will be expanded      Function( @@ -369,10 +473,31 @@ pub enum MacroType {      Array(Vec<MacroType>),  } +use std::string::ToString; +impl ToString for MacroType { +    fn to_string(&self) -> String { +        match self { +            MacroType::String(s) => s.to_string(), +            MacroType::Array(a) => { +                let mut out = String::from("["); +                for (i, el) in a.iter().enumerate() { +                    out.push_str(&el.to_string()); +                    if i < (a.len() - 1) { +                        out.push_str(", "); +                    } +                } +                out +            } +            MacroType::Function(_a) => String::from("Function()"), +        } +    } +} +  /// Possible parser states  #[derive(Debug, PartialEq)]  enum ParserState {      Normal, +    InQuotes,      InMacro,      InMacroArgs,      DNL, @@ -432,6 +557,14 @@ impl MacroProcessor {              MacroType::Function(smp_builtin_define),          );          self.define_macro( +            String::from("define_array"), +            MacroType::Function(smp_builtin_define_array), +        ); +        self.define_macro( +            String::from("undefine"), +            MacroType::Function(smp_builtin_undefine), +        ); +        self.define_macro(              String::from("ifdef"),              MacroType::Function(smp_builtin_ifdef),          ); @@ -465,11 +598,21 @@ impl MacroProcessor {              String::from("array_push"),              MacroType::Function(smp_builtin_array_push),          ); +        self.define_macro( +            String::from("array_each"), +            MacroType::Function(smp_builtin_array_each), +        );          #[cfg(feature = "time")]          self.define_macro(              String::from("format_time"),              MacroType::Function(smp_builtin_format_time),          ); +        #[cfg(feature = "webring")] +        self.define_macro( +            String::from("webring_rss"), +            MacroType::Function(smp_builtin_webring_rss), +        ); +          // format('Result id %d', 3282)      } @@ -548,7 +691,7 @@ impl MacroProcessor {                  // 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); +                    let placeholder = format!("${}", i);                      expanded = expanded.replace(&placeholder, arg);                  }                  self.process_input(&expanded) @@ -584,6 +727,9 @@ impl MacroProcessor {          let mut in_quote_single = false;          let mut in_quote_double = false; +        const QUOTE_START: char = '`'; +        const QUOTE_END: char = '\''; +        let mut quote_level = 0;          let mut parens_level = 0;          for (i, c) in input.char_indices() { @@ -606,10 +752,30 @@ impl MacroProcessor {                      if c.is_alphanumeric() {                          state = ParserState::InMacro;                          macro_name.push(c); +                    } else if c == QUOTE_START { +                        state = ParserState::InQuotes; +                        quote_level += 1;                      } else {                          output.push(c);                      }                  } +                ParserState::InQuotes => match c { +                    QUOTE_START => { +                        quote_level += 1; +                        output.push(c); +                    } +                    QUOTE_END => { +                        quote_level -= 1; +                        if quote_level == 0 { +                            state = ParserState::Normal; +                        } else { +                            output.push(c); +                        } +                    } +                    _ => { +                        output.push(c); +                    } +                },                  ParserState::InMacro => {                      if c.is_alphanumeric() || c == '_' {                          macro_name.push(c);  | 
