aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-03-28 18:17:10 -0600
committerMelody Horn <melody@boringcactus.com>2021-03-28 18:17:10 -0600
commit98fa3e5246e83f8121922281a822a8d76f501750 (patch)
treeeeb82d9f9ca14043e19244e9603342a9430a1ab9
parent57e4222900de52da5ffe13933fbebe91fbd7bab9 (diff)
downloadmakers-98fa3e5246e83f8121922281a822a8d76f501750.tar.gz
makers-98fa3e5246e83f8121922281a822a8d76f501750.zip
fix the unbounded type recursion (thanks @Lucretiel!)
-rw-r--r--README.md1
-rw-r--r--src/makefile/functions.rs40
-rw-r--r--src/makefile/macro.rs171
-rw-r--r--src/makefile/mod.rs4
4 files changed, 93 insertions, 123 deletions
diff --git a/README.md b/README.md
index a1ca96a..0949478 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ that said, I test manually on complicated projects, and the current record for m
## contributors
- [zseri](https://ytrizja.de/) (code)
+- [@Lucretiel](https://github.com/Lucretiel) (code)
- you, if you'd like!
email melody@boringcactus.com if you've got something ([not just code!](https://allcontributors.org/docs/en/emoji-key)) to contribute
diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs
index 12da6c9..61b9828 100644
--- a/src/makefile/functions.rs
+++ b/src/makefile/functions.rs
@@ -1,8 +1,8 @@
use super::pattern::r#match;
-use super::r#macro::MacroSetLike;
+use super::r#macro::{MacroSet, MacroSource};
use super::token::TokenString;
-pub(crate) fn expand_call(name: &str, args: &[TokenString], macros: &impl MacroSetLike) -> String {
+pub(crate) fn expand_call(name: &str, args: &[TokenString], macros: &MacroSet) -> String {
match name {
"filter" => {
assert_eq!(args.len(), 2);
@@ -60,14 +60,10 @@ pub(crate) fn expand_call(name: &str, args: &[TokenString], macros: &impl MacroS
// Text Functions
mod text {
use super::r#match;
- use super::MacroSetLike;
+ use super::MacroSet;
use super::TokenString;
- pub(crate) fn filter(
- macros: &impl MacroSetLike,
- patterns: &TokenString,
- text: &TokenString,
- ) -> String {
+ pub(crate) fn filter(macros: &MacroSet, patterns: &TokenString, text: &TokenString) -> String {
let patterns = macros.expand(patterns);
let patterns = patterns.split_whitespace().collect::<Vec<_>>();
let text = macros.expand(text);
@@ -85,7 +81,7 @@ mod text {
}
pub(crate) fn filter_out(
- macros: &impl MacroSetLike,
+ macros: &MacroSet,
patterns: &TokenString,
text: &TokenString,
) -> String {
@@ -105,7 +101,7 @@ mod text {
result_pieces.join(" ")
}
- pub(crate) fn sort(macros: &impl MacroSetLike, words: &TokenString) -> String {
+ pub(crate) fn sort(macros: &MacroSet, words: &TokenString) -> String {
let words = macros.expand(words);
let mut words = words.split_whitespace().collect::<Vec<_>>();
words.sort_unstable();
@@ -120,10 +116,10 @@ mod file_name {
use std::ffi::OsStr;
use std::path::Path;
- use super::MacroSetLike;
+ use super::MacroSet;
use super::TokenString;
- pub(crate) fn notdir(macros: &impl MacroSetLike, words: &TokenString) -> String {
+ pub(crate) fn notdir(macros: &MacroSet, words: &TokenString) -> String {
let words = macros.expand(words);
let words = words
.split_whitespace()
@@ -137,7 +133,7 @@ mod file_name {
words.join(" ")
}
- pub(crate) fn basename(macros: &impl MacroSetLike, words: &TokenString) -> String {
+ pub(crate) fn basename(macros: &MacroSet, words: &TokenString) -> String {
let words = macros.expand(words);
let words = words
.split_whitespace()
@@ -152,7 +148,7 @@ mod file_name {
}
pub(crate) fn addprefix(
- macros: &impl MacroSetLike,
+ macros: &MacroSet,
prefix: &TokenString,
targets: &TokenString,
) -> String {
@@ -165,7 +161,7 @@ mod file_name {
results.join(" ")
}
- pub(crate) fn wildcard(macros: &impl MacroSetLike, pattern: &TokenString) -> String {
+ pub(crate) fn wildcard(macros: &MacroSet, pattern: &TokenString) -> String {
let pattern = macros.expand(pattern);
let home_dir = env::var("HOME")
.ok()
@@ -188,11 +184,12 @@ mod file_name {
// foreach
mod foreach {
- use super::MacroSetLike;
+ use super::MacroSet;
+ use super::MacroSource;
use super::TokenString;
pub(crate) fn foreach(
- macros: &impl MacroSetLike,
+ macros: &MacroSet,
var: &TokenString,
list: &TokenString,
text: &TokenString,
@@ -204,7 +201,7 @@ mod foreach {
let mut macros = macros.with_overlay();
let results = words
.map(|word| {
- macros.set(var.clone(), TokenString::text(word));
+ macros.set(var.clone(), MacroSource::File, TokenString::text(word));
macros.expand(text)
})
.collect::<Vec<_>>();
@@ -214,11 +211,12 @@ mod foreach {
// call
mod call {
- use super::MacroSetLike;
+ use super::MacroSet;
+ use super::MacroSource;
use super::TokenString;
pub(crate) fn call<'a>(
- macros: &impl MacroSetLike,
+ macros: &MacroSet,
args: impl Iterator<Item = &'a TokenString>,
) -> String {
let args = args.map(|arg| macros.expand(arg)).collect::<Vec<_>>();
@@ -226,7 +224,7 @@ mod call {
let mut macros = macros.with_overlay();
for (i, x) in args.into_iter().enumerate() {
- macros.set(i.to_string(), TokenString::text(x));
+ macros.set(i.to_string(), MacroSource::File, TokenString::text(x));
}
macros.expand(&TokenString::r#macro(function))
}
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs
index 27cbc14..79df339 100644
--- a/src/makefile/macro.rs
+++ b/src/makefile/macro.rs
@@ -19,11 +19,67 @@ pub(crate) trait LookupInternal: for<'a> Fn(&'a str) -> String {}
impl<F: for<'a> Fn(&'a str) -> String> LookupInternal for F {}
-pub(crate) trait MacroSetLike: Sized {
- fn lookup_internal(&self, name: &str) -> String;
- fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)>;
+#[derive(Clone)]
+pub(crate) struct MacroSet<'parent, 'lookup> {
+ parent: Option<&'parent MacroSet<'parent, 'lookup>>,
+ data: HashMap<String, (MacroSource, TokenString)>,
+ lookup_internal: Option<&'lookup dyn LookupInternal>,
+}
+
+impl<'parent, 'lookup> MacroSet<'parent, 'lookup> {
+ pub(crate) fn new() -> Self {
+ Self {
+ parent: None,
+ data: HashMap::new(),
+ lookup_internal: None,
+ }
+ }
+
+ 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)));
+ }
+ }
+ }
+
+ fn lookup_internal(&self, name: &str) -> String {
+ if let Some(lookup) = self.lookup_internal {
+ lookup(name)
+ } else if let Some(parent) = self.parent {
+ parent.lookup_internal(name)
+ } else {
+ panic!("no lookup possible");
+ }
+ }
+
+ pub(crate) fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> {
+ self.data
+ .get(name)
+ .or_else(|| self.parent.and_then(|parent| parent.get(name)))
+ }
+
+ 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)
+ }
+
+ // `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)
+ }
- fn expand(&self, text: &TokenString) -> String {
+ pub(crate) fn expand(&self, text: &TokenString) -> String {
let mut result = String::new();
for token in text.tokens() {
match token {
@@ -62,112 +118,27 @@ pub(crate) trait MacroSetLike: Sized {
result
}
- fn with_lookup<'a, 's: 'a>(
+ pub(crate) fn with_lookup<'l, 's: 'l>(
&'s self,
- lookup: &'a dyn LookupInternal,
- ) -> MacroSetLikeWithLookup<'a, Self> {
- MacroSetLikeWithLookup {
- parent: self,
- lookup,
- }
- }
-
- fn with_overlay(&self) -> MacroSetLikeWithOverlay<Self> {
- MacroSetLikeWithOverlay {
- parent: self,
+ lookup: &'l dyn LookupInternal,
+ ) -> MacroSet<'s, 'l> {
+ MacroSet {
+ parent: Some(self),
data: HashMap::new(),
+ lookup_internal: Some(lookup),
}
}
-}
-
-pub(crate) struct MacroSetLikeWithLookup<'a, Parent: MacroSetLike> {
- parent: &'a Parent,
- lookup: &'a dyn LookupInternal,
-}
-
-impl<'a, Parent: MacroSetLike> MacroSetLike for MacroSetLikeWithLookup<'a, Parent> {
- fn lookup_internal(&self, name: &str) -> String {
- (self.lookup)(name)
- }
-
- fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> {
- self.parent.get(name)
- }
-}
-
-pub(crate) struct MacroSetLikeWithOverlay<'a, Parent: MacroSetLike> {
- parent: &'a Parent,
- data: HashMap<String, (MacroSource, TokenString)>,
-}
-
-impl<'a, Parent: MacroSetLike> MacroSetLike for MacroSetLikeWithOverlay<'a, Parent> {
- fn lookup_internal(&self, name: &str) -> String {
- self.parent.lookup_internal(name)
- }
-
- fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> {
- self.data.get(name).or_else(|| self.parent.get(name))
- }
-}
-
-impl<'a, Parent: MacroSetLike> MacroSetLikeWithOverlay<'a, Parent> {
- pub(crate) fn set(&mut self, name: String, text: TokenString) {
- self.data.insert(name, (MacroSource::File, text));
- }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct MacroSet {
- data: HashMap<String, (MacroSource, TokenString)>,
-}
-impl MacroSetLike for MacroSet {
- fn lookup_internal(&self, _: &str) -> String {
- panic!("can't lookup an internal macro value from a plain MacroSet");
- }
-
- fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> {
- self.data.get(name)
- }
-}
-
-impl MacroSet {
- pub(crate) fn new() -> Self {
- Self {
+ pub(crate) fn with_overlay<'s>(&'s self) -> MacroSet<'s, 'lookup> {
+ MacroSet {
+ parent: Some(self),
data: HashMap::new(),
+ lookup_internal: None,
}
}
-
- 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)
- }
-
- // `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)
- }
}
-impl fmt::Display for MacroSet {
+impl fmt::Display for MacroSet<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let pieces = self
.data
diff --git a/src/makefile/mod.rs b/src/makefile/mod.rs
index f3b7efa..7c2663b 100644
--- a/src/makefile/mod.rs
+++ b/src/makefile/mod.rs
@@ -23,7 +23,7 @@ mod token;
use command_line::CommandLine;
use conditional::{ConditionalLine, ConditionalState};
use inference_rules::InferenceRule;
-use r#macro::{MacroSet, MacroSetLike, MacroSource};
+use r#macro::{MacroSet, MacroSource};
use target::Target;
use token::{tokenize, Token, TokenString};
@@ -86,7 +86,7 @@ fn inference_match<'a>(
pub(crate) struct Makefile<'a> {
inference_rules: Vec<InferenceRule>,
- macros: MacroSet,
+ macros: MacroSet<'static, 'static>,
targets: RefCell<HashMap<String, Rc<RefCell<Target>>>>,
pub(crate) first_non_special_target: Option<String>,
args: &'a Args,