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 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, lookup_internal: RefCell>, } impl MacroSet { pub(crate) fn new() -> Self { let lookup_fail: Rc = Rc::new(lookup_fail); Self { data: HashMap::new(), lookup_internal: RefCell::new(Rc::downgrade(&lookup_fail)), } } pub(crate) fn lookup(&self, lookup: Weak) { 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::>(); 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="" ] }