aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/macro.rs
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2024-11-11 00:07:33 -0700
committerMelody Horn <melody@boringcactus.com>2024-11-11 00:07:33 -0700
commitec2c18171cd316a8b1f69baf92f67980820dfc9a (patch)
tree007add1cd59aec07098134b018efededee463d97 /src/makefile/macro.rs
parent825ff799a2154ffb94ef21bbc14e2afe2afa2006 (diff)
downloadmakers-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.rs252
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(&macro_value, |c: &regex::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),
- &macro_value
- );
- result.push_str(&macro_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(&macros).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(&macros).expand(&"$(m:%=%-objs)".parse()?, NO_EVAL)?,
"conf-objs"
);
Ok(())