use crate::tokenizer::Token; use regex::{Captures, Match, Regex}; use toml::{Table, Value}; // MetaRules // Struct containing all meta rules. pub struct MetaRules { replacement_rules: Vec<(String, (String, String))>, interpolation_rules: Vec<(String, (String, String))>, token_rules: Vec<(String, String)>, pub special_tokens: Vec<(String, Token)>, } // Implementation of MetaRules // Trait implementation impl MetaRules { // @name new // @return MetaRules // @brief Create a new rule struct by reading from a configuration file. // @param configuration_filename: &str pub fn new(configuration_filename: &str) -> MetaRules { let configuration_content: String = std::fs::read_to_string(configuration_filename) .expect("[ERROR] Could not open configuration file!"); let mut replacements: Vec<(String, (String, String))> = vec![]; let mut interpolation: Vec<(String, (String, String))> = vec![]; let mut meta_token_rules: Vec<(String, String)> = vec![]; let meta_tokens: Vec<(String, Token)> = vec![]; let configuration = gtoml::parse(configuration_content.as_str()) .expect("[ERROR] TOML invalid in preprocessor!"); let configuration_unpacked: Table = Table::try_from(configuration).unwrap(); let meta_configuration: Table = match configuration_unpacked.get("meta") { Some(config) => config.as_table().unwrap().clone(), None => Table::new(), }; if !meta_configuration.is_empty() { if meta_configuration.contains_key("replacements") { println!("[INFO] Found replacement rules."); let replacement_rules: Table = meta_configuration .get("replacements") .unwrap() .as_table() .unwrap() .clone(); for key in replacement_rules.keys() { let value: Vec = replacement_rules .get(key) .unwrap() .as_array() .unwrap() .clone(); let name: String = key.clone(); let pattern: String = value[0].as_str().unwrap().to_owned(); let replacement: String = value[1].as_str().unwrap().to_owned(); replacements.push((name, (pattern, replacement))); } } if meta_configuration.contains_key("interpolation") { println!("[INFO] Found interpolation rules."); let interpolation_rules: Table = meta_configuration .get("interpolation") .unwrap() .as_table() .unwrap() .clone(); for key in interpolation_rules.keys() { let value: Vec = interpolation_rules .get(key) .unwrap() .as_array() .unwrap() .clone(); let name: String = key.clone(); let pattern: String = value[0].as_str().unwrap().to_owned(); let cmd: &str = value[1].as_str().unwrap(); interpolation.push((name, (pattern, String::from(cmd)))); } } if meta_configuration.contains_key("token") { println!("[INFO] Found token rules."); let token_rules: Table = meta_configuration .get("token") .unwrap() .as_table() .unwrap() .clone(); for rule in token_rules.keys() { let pattern: String = token_rules.get(rule).unwrap().as_str().unwrap().to_owned(); meta_token_rules.push((rule.clone(), pattern)); } } } else { println!("[WARNING] No meta configuration, skipping preprocessor."); } MetaRules { replacement_rules: replacements, interpolation_rules: interpolation, token_rules: meta_token_rules, special_tokens: meta_tokens, } } // @name process // @return String // @brief Run preprocessor on raw code. // @param rule_set: MetaRules, raw_code: String pub fn process(&mut self, raw_code: String) -> String { let mut processed_code: String = raw_code.clone(); // replacement rules for rule in self.replacement_rules.iter() { println!("[INFO] Applying rule {}", rule.0); let base_pattern: Regex = Regex::new((rule.1 .0).as_str()).unwrap(); processed_code = base_pattern .replace_all(processed_code.as_str(), rule.1 .1.as_str()) .to_string(); } // interpolation rules for rule in self.interpolation_rules.iter() { println!("[INFO] Applying rule {}", rule.0); let base_pattern: Regex = Regex::new((rule.1 .0).as_str()).unwrap(); let processed_code_replacement = processed_code.clone(); let captures: Option = base_pattern.captures(processed_code_replacement.as_str()); let directive: String; match captures { Some(n) => directive = n.get(0).map_or("", |m| m.as_str()).to_string(), None => continue, }; let command: &str = &base_pattern.replace(directive.as_str(), rule.1 .1.as_str()); let subprocess = std::process::Command::new("/bin/bash") .arg("-c") .arg(String::from("echo \"$(") + command + ")\"") .output() .expect((String::from("") + "Failed to run command " + command + "!").as_str()); processed_code = base_pattern .replace( processed_code.as_str(), String::from_utf8(subprocess.stdout).unwrap(), ) .to_string(); } for token_style in self.token_rules.iter() { println!("[INFO] Searching meta tokens of style {}", token_style.0); // Search all occurrences let token_pattern: Regex = Regex::new(token_style.1.as_str()).expect("Could not assign pattern."); let match_list: Match; match_list = match token_pattern.find(processed_code.as_str()) { Some(n) => n, None => continue, }; // Create id for each occurrence let meta_id: String = String::from("meta_token_") + match_list.start().to_string().as_str() + "__" + match_list.end().to_string().as_str(); // Replace token by id let meta_value: String = match_list.as_str().to_string(); let value_regex: Regex = Regex::new(meta_value.as_str()).expect("Could not create pattern."); processed_code = value_regex .replace(processed_code.as_str(), meta_id.as_str()) .to_string(); println!("Replace {} with {}.", meta_value, meta_id); // Safe id and token self.special_tokens.push(( meta_id, Token { token: meta_value, token_type: crate::TokenType::IDENTIFIER, }, )); } return processed_code; } }