aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/functions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/makefile/functions.rs')
-rw-r--r--src/makefile/functions.rs312
1 files changed, 191 insertions, 121 deletions
diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs
index b9a6a6d..12da6c9 100644
--- a/src/makefile/functions.rs
+++ b/src/makefile/functions.rs
@@ -1,160 +1,53 @@
-use std::env;
-use std::path::Path;
-
use super::pattern::r#match;
-use super::r#macro::{MacroSet, MacroSource};
+use super::r#macro::MacroSetLike;
use super::token::TokenString;
-pub(crate) struct FunctionCall {}
-
-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);
- let text = text.split_whitespace();
- let mut result_pieces = vec![];
- for word in text {
- if patterns
- .iter()
- .any(|pattern| r#match(pattern, word).is_some())
- {
- result_pieces.push(word);
- }
- }
- result_pieces.join(" ")
-}
-
-pub(crate) fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> String {
+pub(crate) fn expand_call(name: &str, args: &[TokenString], macros: &impl MacroSetLike) -> String {
match name {
- // Text Functions
"filter" => {
assert_eq!(args.len(), 2);
- filter(macros, &args[0], &args[1])
+ text::filter(macros, &args[0], &args[1])
}
"filter-out" => {
assert_eq!(args.len(), 2);
- let patterns = macros.expand(&args[0]);
- let patterns = patterns.split_whitespace().collect::<Vec<_>>();
- let text = macros.expand(&args[1]);
- let text = text.split_whitespace();
- let mut result_pieces = vec![];
- for word in text {
- if patterns
- .iter()
- .all(|pattern| r#match(pattern, word).is_none())
- {
- result_pieces.push(word);
- }
- }
- result_pieces.join(" ")
+ text::filter_out(macros, &args[0], &args[1])
}
"sort" => {
assert_eq!(args.len(), 1);
- let words = macros.expand(&args[0]);
- let mut words = words.split_whitespace().collect::<Vec<_>>();
- words.sort();
- words.dedup();
- words.join(" ")
+ text::sort(macros, &args[0])
}
- // File Name Functions
"notdir" => {
assert_eq!(args.len(), 1);
- let words = macros.expand(&args[0]);
- let words = words
- .split_whitespace()
- .map(|word| {
- Path::new(word)
- .file_name()
- .and_then(|filename| filename.to_str())
- .unwrap_or("")
- })
- .collect::<Vec<_>>();
- words.join(" ")
+ file_name::notdir(macros, &args[0])
}
"basename" => {
assert_eq!(args.len(), 1);
- let words = macros.expand(&args[0]);
- let words = words
- .split_whitespace()
- .map(|word| {
- Path::new(word)
- .with_extension("")
- .to_str()
- .map_or_else(String::new, ToString::to_string)
- })
- .collect::<Vec<_>>();
- words.join(" ")
+ file_name::basename(macros, &args[0])
}
"addprefix" => {
assert_eq!(args.len(), 2);
- let prefix = macros.expand(&args[0]);
- let targets = macros.expand(&args[1]);
- let results = targets
- .split_whitespace()
- .map(|t| format!("{}{}", prefix, t))
- .collect::<Vec<_>>();
- results.join(" ")
+ file_name::addprefix(macros, &args[0], &args[1])
}
"wildcard" => {
assert_eq!(args.len(), 1);
- let pattern = macros.expand(&args[0]);
- let home_dir = env::var("HOME")
- .ok()
- .or(dirs::home_dir().and_then(|p| p.to_str().map(String::from)));
- let pattern = if let Some(home_dir) = home_dir {
- pattern.replace('~', &home_dir)
- } else {
- pattern
- };
- let results = glob::glob(&pattern)
- .expect("invalid glob pattern!")
- .filter_map(|path| path.ok())
- .map(|path| path.to_str().map(ToString::to_string).unwrap_or_default())
- .collect::<Vec<_>>();
- results.join(" ")
+ file_name::wildcard(macros, &args[0])
}
// foreach
"foreach" => {
assert_eq!(args.len(), 3);
- let var = macros.expand(&args[0]);
- let list = macros.expand(&args[1]);
- let text = &args[2];
- let words = list.split_whitespace();
-
- let mut macros = macros.clone();
- let results = words
- .map(|word| {
- macros.set(var.clone(), MacroSource::File, TokenString::text(word));
- macros.expand(text)
- })
- .collect::<Vec<_>>();
- results.join(" ")
+ foreach::foreach(macros, &args[0], &args[1], &args[2])
}
// call
"call" => {
- assert!(args.len() > 0);
- let args = args
- .iter()
- .map(|arg| macros.expand(arg))
- .collect::<Vec<_>>();
- let function = args[0].clone();
-
- let mut macros = macros.clone();
- for (i, x) in args.into_iter().enumerate() {
- macros.set(i.to_string(), MacroSource::File, TokenString::text(x));
- }
- macros.expand(&TokenString::r#macro(function))
+ assert!(!args.is_empty());
+ call::call(macros, args.iter())
}
// eval
- "eval" => {
- assert_eq!(args.len(), 1);
- let arg = macros.expand(&args[0]);
- todo!()
- }
+ "eval" => todo!(),
// shell
"shell" => todo!(),
@@ -164,12 +57,189 @@ pub(crate) fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> Strin
}
}
+// Text Functions
+mod text {
+ use super::r#match;
+ use super::MacroSetLike;
+ use super::TokenString;
+
+ pub(crate) fn filter(
+ macros: &impl MacroSetLike,
+ patterns: &TokenString,
+ text: &TokenString,
+ ) -> String {
+ let patterns = macros.expand(patterns);
+ let patterns = patterns.split_whitespace().collect::<Vec<_>>();
+ let text = macros.expand(text);
+ let text = text.split_whitespace();
+ let mut result_pieces = vec![];
+ for word in text {
+ if patterns
+ .iter()
+ .any(|pattern| r#match(pattern, word).is_some())
+ {
+ result_pieces.push(word);
+ }
+ }
+ result_pieces.join(" ")
+ }
+
+ pub(crate) fn filter_out(
+ macros: &impl MacroSetLike,
+ patterns: &TokenString,
+ text: &TokenString,
+ ) -> String {
+ let patterns = macros.expand(patterns);
+ let patterns = patterns.split_whitespace().collect::<Vec<_>>();
+ let text = macros.expand(text);
+ let text = text.split_whitespace();
+ let mut result_pieces = vec![];
+ for word in text {
+ if patterns
+ .iter()
+ .all(|pattern| r#match(pattern, word).is_none())
+ {
+ result_pieces.push(word);
+ }
+ }
+ result_pieces.join(" ")
+ }
+
+ pub(crate) fn sort(macros: &impl MacroSetLike, words: &TokenString) -> String {
+ let words = macros.expand(words);
+ let mut words = words.split_whitespace().collect::<Vec<_>>();
+ words.sort_unstable();
+ words.dedup();
+ words.join(" ")
+ }
+}
+
+// File Name Functions
+mod file_name {
+ use std::env;
+ use std::ffi::OsStr;
+ use std::path::Path;
+
+ use super::MacroSetLike;
+ use super::TokenString;
+
+ pub(crate) fn notdir(macros: &impl MacroSetLike, words: &TokenString) -> String {
+ let words = macros.expand(words);
+ let words = words
+ .split_whitespace()
+ .map(|word| {
+ Path::new(word)
+ .file_name()
+ .and_then(OsStr::to_str)
+ .unwrap_or("")
+ })
+ .collect::<Vec<_>>();
+ words.join(" ")
+ }
+
+ pub(crate) fn basename(macros: &impl MacroSetLike, words: &TokenString) -> String {
+ let words = macros.expand(words);
+ let words = words
+ .split_whitespace()
+ .map(|word| {
+ Path::new(word)
+ .with_extension("")
+ .to_str()
+ .map_or_else(String::new, ToString::to_string)
+ })
+ .collect::<Vec<_>>();
+ words.join(" ")
+ }
+
+ pub(crate) fn addprefix(
+ macros: &impl MacroSetLike,
+ prefix: &TokenString,
+ targets: &TokenString,
+ ) -> String {
+ let prefix = macros.expand(prefix);
+ let targets = macros.expand(targets);
+ let results = targets
+ .split_whitespace()
+ .map(|t| format!("{}{}", prefix, t))
+ .collect::<Vec<_>>();
+ results.join(" ")
+ }
+
+ pub(crate) fn wildcard(macros: &impl MacroSetLike, pattern: &TokenString) -> String {
+ let pattern = macros.expand(pattern);
+ let home_dir = env::var("HOME")
+ .ok()
+ .or_else(|| dirs::home_dir().and_then(|p| p.to_str().map(String::from)));
+ let pattern = if let Some(home_dir) = home_dir {
+ pattern.replace('~', &home_dir)
+ } else {
+ pattern
+ };
+ let results = glob::glob(&pattern)
+ .expect("invalid glob pattern!")
+ .filter_map(|path| {
+ path.ok()
+ .map(|x| x.to_str().map(ToString::to_string).unwrap_or_default())
+ })
+ .collect::<Vec<_>>();
+ results.join(" ")
+ }
+}
+
+// foreach
+mod foreach {
+ use super::MacroSetLike;
+ use super::TokenString;
+
+ pub(crate) fn foreach(
+ macros: &impl MacroSetLike,
+ var: &TokenString,
+ list: &TokenString,
+ text: &TokenString,
+ ) -> String {
+ let var = macros.expand(var);
+ let list = macros.expand(list);
+ let words = list.split_whitespace();
+
+ let mut macros = macros.with_overlay();
+ let results = words
+ .map(|word| {
+ macros.set(var.clone(), TokenString::text(word));
+ macros.expand(text)
+ })
+ .collect::<Vec<_>>();
+ results.join(" ")
+ }
+}
+
+// call
+mod call {
+ use super::MacroSetLike;
+ use super::TokenString;
+
+ pub(crate) fn call<'a>(
+ macros: &impl MacroSetLike,
+ args: impl Iterator<Item = &'a TokenString>,
+ ) -> String {
+ let args = args.map(|arg| macros.expand(arg)).collect::<Vec<_>>();
+ let function = args[0].clone();
+
+ let mut macros = macros.with_overlay();
+ for (i, x) in args.into_iter().enumerate() {
+ macros.set(i.to_string(), TokenString::text(x));
+ }
+ macros.expand(&TokenString::r#macro(function))
+ }
+}
+
#[cfg(test)]
mod test {
use super::*;
+ use crate::makefile::r#macro::{MacroSet, MacroSource};
+
fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> String {
- super::call(name, args, macros, lookup_fail)
+ super::expand_call(name, args, macros)
}
macro_rules! call {