diff options
Diffstat (limited to 'src/makefile/macro.rs')
-rw-r--r-- | src/makefile/macro.rs | 173 |
1 files changed, 106 insertions, 67 deletions
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs index 139903f..27cbc14 100644 --- a/src/makefile/macro.rs +++ b/src/makefile/macro.rs @@ -1,8 +1,6 @@ -use std::cell::RefCell; use std::collections::HashMap; use std::env; use std::fmt; -use std::rc::{Rc, Weak}; use regex::Regex; @@ -21,29 +19,125 @@ pub(crate) trait LookupInternal: for<'a> Fn(&'a str) -> String {} impl<F: for<'a> Fn(&'a str) -> String> LookupInternal for F {} -fn lookup_fail(_: &str) -> String { - panic!("internal variables not available!"); +pub(crate) trait MacroSetLike: Sized { + fn lookup_internal(&self, name: &str) -> String; + fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)>; + + fn expand(&self, text: &TokenString) -> String { + let mut result = String::new(); + for token in text.tokens() { + match token { + Token::Text(t) => result.push_str(t), + Token::MacroExpansion { name, replacement } => { + let internal_macro_names = &['@', '?', '<', '*'][..]; + let internal_macro_suffices = &['D', 'F'][..]; + let just_internal = name.len() == 1 && name.starts_with(internal_macro_names); + let suffixed_internal = name.len() == 2 + && name.starts_with(internal_macro_names) + && name.ends_with(internal_macro_suffices); + let macro_value = if just_internal || suffixed_internal { + self.lookup_internal(name) + } else { + self.get(name) + .map_or_else(String::new, |(_, macro_value)| self.expand(macro_value)) + }; + let macro_value = match replacement { + Some((subst1, subst2)) => { + let subst1 = self.expand(subst1); + let subst1_suffix = regex::escape(&subst1); + let subst1_suffix = + Regex::new(&format!(r"{}\b", subst1_suffix)).unwrap(); + let subst2 = self.expand(subst2); + subst1_suffix.replace_all(¯o_value, subst2).to_string() + } + None => macro_value, + }; + result.push_str(¯o_value); + } + Token::FunctionCall { name, args } => { + result.push_str(&functions::expand_call(name, args, self)); + } + } + } + result + } + + fn with_lookup<'a, 's: 'a>( + &'s self, + lookup: &'a dyn LookupInternal, + ) -> MacroSetLikeWithLookup<'a, Self> { + MacroSetLikeWithLookup { + parent: self, + lookup, + } + } + + fn with_overlay(&self) -> MacroSetLikeWithOverlay<Self> { + MacroSetLikeWithOverlay { + parent: self, + data: HashMap::new(), + } + } +} + +pub(crate) struct MacroSetLikeWithLookup<'a, Parent: MacroSetLike> { + parent: &'a Parent, + lookup: &'a dyn LookupInternal, +} + +impl<'a, Parent: MacroSetLike> MacroSetLike for MacroSetLikeWithLookup<'a, Parent> { + fn lookup_internal(&self, name: &str) -> String { + (self.lookup)(name) + } + + fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> { + self.parent.get(name) + } +} + +pub(crate) struct MacroSetLikeWithOverlay<'a, Parent: MacroSetLike> { + parent: &'a Parent, + data: HashMap<String, (MacroSource, TokenString)>, +} + +impl<'a, Parent: MacroSetLike> MacroSetLike for MacroSetLikeWithOverlay<'a, Parent> { + fn lookup_internal(&self, name: &str) -> String { + self.parent.lookup_internal(name) + } + + fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> { + self.data.get(name).or_else(|| self.parent.get(name)) + } } -#[derive(Clone)] +impl<'a, Parent: MacroSetLike> MacroSetLikeWithOverlay<'a, Parent> { + pub(crate) fn set(&mut self, name: String, text: TokenString) { + self.data.insert(name, (MacroSource::File, text)); + } +} + +#[derive(Clone, Debug)] pub(crate) struct MacroSet { data: HashMap<String, (MacroSource, TokenString)>, - lookup_internal: RefCell<Weak<dyn LookupInternal>>, +} + +impl MacroSetLike for MacroSet { + fn lookup_internal(&self, _: &str) -> String { + panic!("can't lookup an internal macro value from a plain MacroSet"); + } + + fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> { + self.data.get(name) + } } impl MacroSet { pub(crate) fn new() -> Self { - let lookup_fail: Rc<dyn LookupInternal> = Rc::new(lookup_fail); Self { data: HashMap::new(), - lookup_internal: RefCell::new(Rc::downgrade(&lookup_fail)), } } - pub(crate) fn lookup(&self, lookup: Weak<dyn LookupInternal>) { - self.lookup_internal.replace(lookup); - } - pub(crate) fn add_builtins(&mut self) { for (k, v) in builtins() { self.data.insert(k.into(), (MacroSource::Builtin, v)); @@ -67,65 +161,10 @@ impl MacroSet { self.data.contains_key(name) } - pub(crate) fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> { - self.data.get(name) - } - // `remove` is fine, but I think for "remove-and-return" `pop` is better pub(crate) fn pop(&mut self, name: &str) -> Option<(MacroSource, TokenString)> { self.data.remove(name) } - - pub(crate) fn expand(&self, text: &TokenString) -> String { - let mut result = String::new(); - for token in text.tokens() { - match token { - Token::Text(t) => result.push_str(t), - Token::MacroExpansion { name, replacement } => { - let internal_macro_names = &['@', '?', '<', '*'][..]; - let internal_macro_suffices = &['D', 'F'][..]; - let just_internal = name.len() == 1 && name.starts_with(internal_macro_names); - let suffixed_internal = name.len() == 2 - && name.starts_with(internal_macro_names) - && name.ends_with(internal_macro_suffices); - let macro_value = if just_internal || suffixed_internal { - let lookup_internal = self.lookup_internal.borrow(); - let lookup_internal = match lookup_internal.upgrade() { - Some(f) => f, - None => Rc::new(lookup_fail), - }; - lookup_internal(name) - } else { - self.data - .get(name) - .map_or_else(String::new, |(_, macro_value)| self.expand(macro_value)) - }; - let macro_value = match replacement { - Some((subst1, subst2)) => { - let subst1 = self.expand(subst1); - let subst1_suffix = regex::escape(&subst1); - let subst1_suffix = - Regex::new(&format!(r"{}\b", subst1_suffix)).unwrap(); - let subst2 = self.expand(subst2); - subst1_suffix.replace_all(¯o_value, subst2).to_string() - } - None => macro_value, - }; - result.push_str(¯o_value); - } - Token::FunctionCall { name, args } => { - result.push_str(&functions::call(name, args, &self)); - } - } - } - result - } -} - -impl fmt::Debug for MacroSet { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", &self.data) - } } impl fmt::Display for MacroSet { |