diff options
Diffstat (limited to 'src/makefile/functions.rs')
-rw-r--r-- | src/makefile/functions.rs | 312 |
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 { |