use std::collections::HashMap;
use super::past::{MLMacroArgs, MLMacroDef, MLMacroUndef, MacroInvok, SLMacroDef, SLMacroUndef};
pub struct SLMacroMap {
map: HashMap<(u64, u8), SLMacroDef>,
}
impl Default for SLMacroMap {
fn default() -> Self {
Self::new()
}
}
impl SLMacroMap {
pub fn new() -> Self {
Self {
map: HashMap::new(),
}
}
pub fn define(&mut self, sl_macro: SLMacroDef) {
let hash = sl_macro.identifier.hash;
let args = match &sl_macro.args {
Some(args) => args.args.len() as u8,
None => 0,
};
self.map.insert((hash, args), sl_macro);
}
pub fn undefine(&mut self, sl_macro_undef: SLMacroUndef) {
let hash = sl_macro_undef.identifier.hash;
let args = sl_macro_undef.args.num;
self.map.remove(&(hash, args));
}
pub fn get(&self, invokation: &MacroInvok) -> Option<&SLMacroDef> {
let hash = invokation.identifier.hash;
let args = match &invokation.args {
Some(args) => args.args.len() as u8,
None => 0,
};
self.map.get(&(hash, args))
}
pub fn get_accepted_num_args(&self, hash: u64) -> Option<String> {
let overloaded_macros = self
.map
.values()
.filter(|entry| entry.identifier.hash == hash);
let mut arg_nums = Vec::new();
for sl_macro in overloaded_macros {
let num_args = sl_macro
.args
.as_ref()
.map(|args| args.args.len() as u8)
.unwrap_or(0);
arg_nums.push(num_args);
}
if arg_nums.is_empty() {
None
} else {
arg_nums.sort_unstable();
Some(if arg_nums.len() == 1 {
format!("{}", arg_nums.first().unwrap())
} else if arg_nums.len() == 2 {
format!(
"{} or {}",
arg_nums.first().unwrap(),
arg_nums.last().unwrap()
)
} else {
let mut s = String::new();
for num in arg_nums.iter().take(arg_nums.len() - 1) {
s.push_str(&format!("{}, ", num));
}
s.push_str(&format!("or {}", arg_nums.last().unwrap()));
s
})
}
}
pub fn contains(&self, hash: u64, num_args: u8) -> bool {
self.map.contains_key(&(hash, num_args))
}
pub fn find_by_hash(&self, hash: u64) -> Option<&SLMacroDef> {
self.map
.iter()
.find(|((entry_hash, _), _)| *entry_hash == hash)
.map(|((_, _), entry)| entry)
}
pub fn contains_hash(&self, hash: u64) -> bool {
self.map.keys().any(|key| key.0 == hash)
}
}
pub struct MLMacroMap {
macros: Vec<(u64, MLMacroDef)>,
}
impl Default for MLMacroMap {
fn default() -> Self {
Self::new()
}
}
impl MLMacroMap {
pub fn new() -> Self {
Self { macros: Vec::new() }
}
pub fn define(&mut self, ml_macro: MLMacroDef) -> bool {
let hash = ml_macro.identifier.hash;
let replace_index = self.find(hash, &ml_macro.args);
if let Some(replace_index) = replace_index {
self.macros.swap_remove(replace_index);
self.macros.push((hash, ml_macro));
true
} else {
self.macros.push((hash, ml_macro));
false
}
}
pub fn undefine(&mut self, ml_macro_undef: MLMacroUndef) {
let hash = ml_macro_undef.identifier.hash;
let index = self.find(hash, &Some(ml_macro_undef.args));
if let Some(index) = index {
self.macros.swap_remove(index);
}
}
pub fn contains(&self, hash: u64, ml_args: &Option<MLMacroArgs>) -> bool {
self.find(hash, ml_args).is_some()
}
pub fn find_by_hash(&self, hash: u64) -> Option<&MLMacroDef> {
self.macros
.iter()
.find(|entry| entry.0 == hash)
.map(|entry| &entry.1)
}
pub fn contains_hash(&self, hash: u64) -> bool {
self.macros.iter().any(|entry| entry.0 == hash)
}
pub fn get(&self, invokation: &MacroInvok) -> Option<&MLMacroDef> {
let hash = invokation.identifier.hash;
let args = match &invokation.args {
Some(args) => {
let num = args.args.len() as u8;
(num, num)
}
None => (0, 0),
};
for (macro_hash, ml_macro) in self.macros.iter() {
let macro_range = Self::get_arg_range(&ml_macro.args);
if hash == *macro_hash && Self::overlaps(args, macro_range) {
return Some(ml_macro);
}
}
None
}
fn get_arg_range(ml_macro_args: &Option<MLMacroArgs>) -> (u8, u8) {
match ml_macro_args {
Some(args) => (
args.required,
args.maximum.map(|arg| arg.get()).unwrap_or(args.required),
),
None => (0, 0),
}
}
fn find(&self, hash: u64, ml_args: &Option<MLMacroArgs>) -> Option<usize> {
let range = Self::get_arg_range(ml_args);
let mut replace_index = None;
for (index, (other_hash, other_macro)) in self.macros.iter().enumerate() {
let other_range = Self::get_arg_range(&other_macro.args);
if hash == *other_hash && Self::overlaps(range, other_range) {
replace_index = Some(index);
break;
}
}
replace_index
}
fn overlaps(range1: (u8, u8), range2: (u8, u8)) -> bool {
range1.0 <= range2.1 && range2.0 <= range1.1
}
}