From 98fa3e5246e83f8121922281a822a8d76f501750 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sun, 28 Mar 2021 18:17:10 -0600 Subject: fix the unbounded type recursion (thanks @Lucretiel!) --- README.md | 1 + src/makefile/functions.rs | 40 ++++++----- src/makefile/macro.rs | 171 +++++++++++++++++++--------------------------- src/makefile/mod.rs | 4 +- 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::>(); 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::>(); 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::>(); @@ -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, ) -> String { let args = args.map(|arg| macros.expand(arg)).collect::>(); @@ -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 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, + 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 { - 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, -} - -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, -} -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, - macros: MacroSet, + macros: MacroSet<'static, 'static>, targets: RefCell>>>, pub(crate) first_non_special_target: Option, args: &'a Args, -- cgit v1.2.3