From ceb2ee16c46515dbd46267c2c93f5cb8f8d7ea47 Mon Sep 17 00:00:00 2001
From: Melody Horn <melody@boringcactus.com>
Date: Wed, 14 Apr 2021 15:45:45 -0600
Subject: use full pattern matching for $(x:y%z=z%a) macro substitution

---
 src/makefile/macro.rs | 60 +++++++++++++++++++++++++++++++++++++++++++--------
 src/makefile/mod.rs   |  5 ++---
 2 files changed, 53 insertions(+), 12 deletions(-)

(limited to 'src')

diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs
index af7e354..9b52206 100644
--- a/src/makefile/macro.rs
+++ b/src/makefile/macro.rs
@@ -6,6 +6,7 @@ use std::fmt;
 use std::rc::Rc;
 
 use eyre::{bail, Result, WrapErr};
+#[cfg(not(feature = "full"))]
 use regex::Regex;
 
 #[cfg(feature = "full")]
@@ -238,15 +239,40 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> {
                     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"{}(\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()
+                            #[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,
+                                    Some(Rc::clone(&self.to_eval)),
+                                )?
+                            }
+                            #[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,
                     };
@@ -421,4 +447,20 @@ mod test {
         assert_eq!(macros.expand(&"$(oof:;=?)".parse()?)?, "bruh? swag? yeet?");
         Ok(())
     }
+
+    #[test]
+    #[cfg(feature = "full")]
+    fn hell_subst() -> R {
+        let mut macros = Set::new();
+        macros.set(
+            "m".to_owned(),
+            Macro {
+                source: ItemSource::FunctionCall,
+                text: TokenString::text("conf"),
+                eagerly_expanded: false,
+            },
+        );
+        assert_eq!(macros.expand(&"$(m:%=%-objs)".parse()?)?, "conf-objs");
+        Ok(())
+    }
 }
diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index 8c50f41..1d4873e 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -203,7 +203,7 @@ impl<'a> Makefile<'a> {
             .filter(|rule| rule.matches(name).unwrap_or(false));
         for rule in inference_rule_candidates {
             log::trace!(
-                "{:>58} considering rule to build {:?} from {:?}",
+                "{} considering rule to build {:?} from {:?}",
                 name,
                 &rule.products,
                 &rule.prerequisites
@@ -211,7 +211,6 @@ impl<'a> Makefile<'a> {
             // whose prerequisite file ($*.s2) exists.
             let prereq_paths = rule
                 .prereqs(name)?
-                .inspect(|x| log::trace!("{:>58} prereq {}", name, x))
                 .map(|prereq_path_name| {
                     if name == prereq_path_name || banned_names.contains(&&*prereq_path_name) {
                         // we can't build this based on itself! fuck outta here
@@ -250,7 +249,7 @@ impl<'a> Makefile<'a> {
                 })
                 .collect::<Option<Vec<String>>>();
             if let Some(prereqs) = prereq_paths {
-                log::trace!("oh {} is a {:#?}", name, rule);
+                log::trace!("oh {} is a {}", name, rule);
                 new_target = Some(Target {
                     name: name.into(),
                     prerequisites: prereqs,
-- 
cgit v1.2.3