diff options
author | Melody Horn <melody@boringcactus.com> | 2024-11-11 00:07:33 -0700 |
---|---|---|
committer | Melody Horn <melody@boringcactus.com> | 2024-11-11 00:07:33 -0700 |
commit | ec2c18171cd316a8b1f69baf92f67980820dfc9a (patch) | |
tree | 007add1cd59aec07098134b018efededee463d97 /src/makefile/macro.rs | |
parent | 825ff799a2154ffb94ef21bbc14e2afe2afa2006 (diff) | |
download | makers-ec2c18171cd316a8b1f69baf92f67980820dfc9a.tar.gz makers-ec2c18171cd316a8b1f69baf92f67980820dfc9a.zip |
overhaul macro inheritance
i was really proud of the lifetimes i had going on in the previous code. i should not have been.
Diffstat (limited to 'src/makefile/macro.rs')
-rw-r--r-- | src/makefile/macro.rs | 252 |
1 files changed, 16 insertions, 236 deletions
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs index 4a82fe9..fb0367b 100644 --- a/src/makefile/macro.rs +++ b/src/makefile/macro.rs @@ -1,20 +1,16 @@ -use std::cell::RefCell; use std::collections::HashMap; use std::collections::HashSet; use std::env; use std::fmt; #[cfg(feature = "full")] use std::io::BufRead; -use std::rc::Rc; + #[cfg(feature = "full")] -use super::functions; -use super::token::{Token, TokenString}; +use super::eval_context::DeferredEvalContext; use super::ItemSource; -use super::LookupInternal; -#[cfg(feature = "full")] -use crate::makefile::eval_context::DeferredEvalContext; -use eyre::{bail, Result, WrapErr}; +use super::{MacroScopeStack, TokenString}; +use eyre::Result; #[cfg(not(feature = "full"))] use regex::Regex; @@ -69,13 +65,6 @@ impl ExportConfig { } } - fn same_type(&self) -> Self { - match self { - Self::Only(_) => Self::only(), - Self::AllBut(_) => Self::all_but(), - } - } - fn should_export(&self, x: &str) -> bool { match self { Self::Only(exported) => exported.contains(x), @@ -84,25 +73,19 @@ impl ExportConfig { } } -#[derive(Clone)] -pub struct Set<'parent, 'lookup> { - parent: Option<&'parent Set<'parent, 'lookup>>, +#[derive(Clone, Debug)] +pub struct Set { pub data: HashMap<String, Macro>, - lookup_internal: Option<LookupInternal<'lookup>>, #[cfg(feature = "full")] pub exported: ExportConfig, - warnings: Rc<RefCell<HashSet<String>>>, } -impl<'parent, 'lookup> Set<'parent, 'lookup> { +impl Set { pub fn new() -> Self { Self { - parent: None, data: HashMap::new(), - lookup_internal: None, #[cfg(feature = "full")] exported: ExportConfig::only(), - warnings: Default::default(), } } @@ -136,23 +119,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { } } - fn lookup_internal(&self, name: &str) -> Result<String> { - if let Some(lookup) = self.lookup_internal.as_ref() { - lookup.lookup(name) - } else if let Some(parent) = self.parent { - parent.lookup_internal(name) - } else { - bail!( - "tried to lookup {:?} but no lookup function is available", - name - ) - } - } - pub fn get(&self, name: &str) -> Option<&Macro> { - self.data - .get(name) - .or_else(|| self.parent.and_then(|parent| parent.get(name))) + self.data.get(name) } pub fn set(&mut self, name: String, r#macro: Macro) { @@ -167,9 +135,7 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { // `remove` is fine, but I think for "remove-and-return" `pop` is better pub fn pop(&mut self, name: &str) -> Option<Macro> { // TODO figure out a better way to handle inheritance - self.data - .remove(name) - .or_else(|| self.parent.and_then(|p| p.get(name).cloned())) + self.data.remove(name) } pub fn extend( @@ -195,171 +161,6 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { self.data.extend(other); } - fn warn(&self, text: String) { - if !self.warnings.borrow().contains(&text) { - log::warn!("{}", &text); - self.warnings.borrow_mut().insert(text); - } - } - - pub fn expand<#[cfg(feature = "full")] R: BufRead>( - &self, - text: &TokenString, - #[cfg(feature = "full")] mut eval_context: Option<&mut DeferredEvalContext<R>>, - ) -> Result<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 name = self - .expand( - name, - #[cfg(feature = "full")] - eval_context.as_deref_mut(), - ) - .wrap_err_with(|| format!("while expanding \"{}\"", name))?; - 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) - .wrap_err_with(|| format!("while expanding $\"{}\"", name))? - } else { - self.get(&name).map_or_else( - || { - self.warn(format!("undefined macro {}", name)); - Ok(String::new()) - }, - |x| { - self.expand( - &x.text, - #[cfg(feature = "full")] - eval_context.as_deref_mut(), - ) - .wrap_err_with(|| format!("while expanding \"{}\"", &x.text)) - }, - )? - }; - let macro_value = match replacement { - Some((subst1, subst2)) => { - let subst1 = self.expand( - subst1, - #[cfg(feature = "full")] - eval_context.as_deref_mut(), - )?; - #[cfg(feature = "full")] - { - let (subst1, subst2) = if subst1.contains('%') { - (subst1, subst2.clone()) - } else { - let mut real_subst2 = TokenString::text("%"); - real_subst2.extend(subst2.clone()); - (format!("%{}", subst1), real_subst2) - }; - let args = [ - TokenString::text(subst1), - subst2, - TokenString::text(macro_value), - ]; - functions::expand_call( - "patsubst", - &args, - self, - eval_context.as_deref_mut(), - )? - } - #[cfg(not(feature = "full"))] - { - let subst1_suffix = regex::escape(&subst1); - let subst1_suffix = - Regex::new(&format!(r"{}(\s|$)", subst1_suffix)) - .context("formed invalid regex somehow")?; - let subst2 = self.expand(subst2)?; - subst1_suffix - .replace_all(¯o_value, |c: ®ex::Captures| { - format!("{}{}", subst2, c.get(1).unwrap().as_str()) - }) - .to_string() - } - } - None => macro_value, - }; - log::trace!( - "expanded {} (from {:?}) into \"{}\"", - token, - self.get(&name).map(|x| &x.source), - ¯o_value - ); - result.push_str(¯o_value); - } - #[cfg(feature = "full")] - Token::FunctionCall { name, args } => { - let name = self.expand(name, eval_context.as_deref_mut())?; - let fn_result = - functions::expand_call(&name, args, self, eval_context.as_deref_mut())?; - log::trace!("expanded {} into \"{}\"", token, &fn_result); - result.push_str(&fn_result); - } - } - } - Ok(result) - } - - #[cfg(feature = "full")] - pub fn origin(&self, name: &str) -> &'static str { - match self.data.get(name) { - None => self.parent.map_or("undefined", |p| p.origin(name)), - Some(Macro { - source: ItemSource::Builtin, - .. - }) => "default", - Some(Macro { - source: ItemSource::Environment, - .. - }) => "environment", - // TODO figure out when to return "environment override" - Some(Macro { - source: ItemSource::File { .. }, - .. - }) => "file", - Some(Macro { - source: ItemSource::CommandLineOrMakeflags, - .. - }) => "command line", - // TODO handle override - Some(Macro { - source: ItemSource::FunctionCall, - .. - }) => "automatic", - } - } - - pub fn with_lookup<'l, 's: 'l>(&'s self, lookup: LookupInternal<'l>) -> Set<'s, 'l> { - Set { - parent: Some(self), - data: HashMap::new(), - lookup_internal: Some(lookup), - #[cfg(feature = "full")] - exported: self.exported.same_type(), - warnings: Rc::clone(&self.warnings), - } - } - - pub fn with_overlay<'s>(&'s self) -> Set<'s, 'lookup> { - Set { - parent: Some(self), - data: HashMap::new(), - lookup_internal: None, - #[cfg(feature = "full")] - exported: self.exported.same_type(), - warnings: Rc::clone(&self.warnings), - } - } - #[cfg(feature = "full")] pub fn resolve_exports<R: BufRead>( &self, @@ -370,37 +171,16 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { .iter() .filter(|(name, _)| self.exported.should_export(name)) .map(|(name, value)| { - self.expand(&value.text, eval_context.as_deref_mut()) + MacroScopeStack::from_scope(self) + .expand(&value.text, eval_context.as_deref_mut()) .map(|text| (name.as_ref(), text)) }) .collect::<Result<Vec<_>>>()?; - Ok(if let Some(parent) = self.parent { - let mut parent_exports = parent.resolve_exports(eval_context)?; - parent_exports.extend(own_exports); - parent_exports - } else { - own_exports - }) - } -} - -impl fmt::Debug for Set<'_, '_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut r#struct = f.debug_struct("Set"); - r#struct.field("parent", &self.parent); - r#struct.field("data", &self.data); - r#struct.field( - "lookup_internal", - &self.lookup_internal.as_ref().map(|_| ()), - ); - #[cfg(feature = "full")] - r#struct.field("exported", &self.exported); - r#struct.field("warnings", &self.warnings); - r#struct.finish() + Ok(own_exports) } } -impl fmt::Display for Set<'_, '_> { +impl fmt::Display for Set { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let pieces = self .data @@ -411,7 +191,7 @@ impl fmt::Display for Set<'_, '_> { } } -impl Default for Set<'_, '_> { +impl Default for Set { fn default() -> Self { Self::new() } @@ -489,7 +269,7 @@ mod test { }, ); assert_eq!( - macros.expand( + MacroScopeStack::from_scope(¯os).expand( &"$(oof:;=?)".parse()?, #[cfg(feature = "full")] NO_EVAL @@ -512,7 +292,7 @@ mod test { }, ); assert_eq!( - macros.expand(&"$(m:%=%-objs)".parse()?, NO_EVAL)?, + MacroScopeStack::from_scope(¯os).expand(&"$(m:%=%-objs)".parse()?, NO_EVAL)?, "conf-objs" ); Ok(()) |