diff options
Diffstat (limited to 'src/makefile/macro.rs')
-rw-r--r-- | src/makefile/macro.rs | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs new file mode 100644 index 0000000..139903f --- /dev/null +++ b/src/makefile/macro.rs @@ -0,0 +1,189 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::env; +use std::fmt; +use std::rc::{Rc, Weak}; + +use regex::Regex; + +use super::functions; +use super::token::{Token, TokenString}; + +#[derive(Debug, Clone)] +pub(crate) enum MacroSource { + File, + CommandLineOrMakeflags, + Environment, + Builtin, +} + +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!"); +} + +#[derive(Clone)] +pub(crate) struct MacroSet { + data: HashMap<String, (MacroSource, TokenString)>, + lookup_internal: RefCell<Weak<dyn LookupInternal>>, +} + +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)); + } + } + + pub(crate) fn add_env(&mut self) { + for (k, v) in env::vars() { + if k != "MAKEFLAGS" && k != "SHELL" { + self.data + .insert(k, (MacroSource::Environment, TokenString::text(v))); + } + } + } + + pub(crate) fn set(&mut self, name: String, source: MacroSource, text: TokenString) { + self.data.insert(name, (source, text)); + } + + pub(crate) fn is_defined(&self, name: &str) -> bool { + 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 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let pieces = self + .data + .iter() + .map(|(k, (_, v))| format!("{}={}", k, v)) + .collect::<Vec<_>>(); + write!(f, "{}", pieces.join("\n")) + } +} + +fn builtins() -> Vec<(&'static str, TokenString)> { + // Fuck it, might as well. + macro_rules! handle { + ($value:ident) => { + stringify!($value) + }; + ($value:literal) => { + $value + }; + } + macro_rules! make { + ($($name:ident=$value:tt)+) => {vec![$( + (stringify!($name), handle!($value).parse().unwrap()) + ),+]}; + } + + make![ + MAKE=makers + AR=ar + YACC=yacc + YFLAGS="" + LEX=lex + LFLAGS="" + LDFLAGS="" + + AS=as + CC=cc + CXX="g++" + CPP="$(CC) -E" + FC=f77 + PC=pc + CO=co + GET=get + LINT=lint + MAKEINFO=makeinfo + TEX=tex + TEXI2DVI=texi2dvi + WEAVE=weave + CWEAVE=cweave + TANGLE=tangle + CTANGLE=ctangle + RM="rm -f" + + ARFLAGS="rv" + CFLAGS="" + FFLAGS="" + ] +} |