diff options
author | Melody Horn <melody@boringcactus.com> | 2024-11-10 21:20:34 -0700 |
---|---|---|
committer | Melody Horn <melody@boringcactus.com> | 2024-11-10 21:20:34 -0700 |
commit | fca10b517b448b4023ad8c3225e59dcefd4004e4 (patch) | |
tree | 7507e959d1f690044945dd6fcf34b5fef3385b14 /src/makefile/functions.rs | |
parent | f945ff33f9312a534ae52f1c763b4150ae41dcf6 (diff) | |
download | makers-fca10b517b448b4023ad8c3225e59dcefd4004e4.tar.gz makers-fca10b517b448b4023ad8c3225e59dcefd4004e4.zip |
overhaul eval architecture
Diffstat (limited to 'src/makefile/functions.rs')
-rw-r--r-- | src/makefile/functions.rs | 284 |
1 files changed, 187 insertions, 97 deletions
diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs index 5d5e222..e76a0d2 100644 --- a/src/makefile/functions.rs +++ b/src/makefile/functions.rs @@ -1,129 +1,131 @@ -use std::cell::RefCell; use std::env; +use std::io::BufRead; use std::process::{Command, Stdio}; -use std::rc::Rc; use eyre::{bail, Result, WrapErr}; +use super::eval_context::DeferredEvalContext; use super::pattern::r#match; use super::r#macro::{Macro, Set as MacroSet}; use super::token::TokenString; use super::ItemSource; +pub const NO_EVAL: Option<&mut DeferredEvalContext<&[u8]>> = None; + #[allow(clippy::cognitive_complexity)] pub fn expand_call( name: &str, args: &[TokenString], macros: &MacroSet, - to_eval: Option<Rc<RefCell<Vec<String>>>>, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { match name { "subst" => { assert_eq!(args.len(), 3); - text::subst(macros, &args[0], &args[1], &args[2]) + text::subst(macros, &args[0], &args[1], &args[2], eval_context) } "patsubst" => { assert_eq!(args.len(), 3); - text::patsubst(macros, &args[0], &args[1], &args[2]) + text::patsubst(macros, &args[0], &args[1], &args[2], eval_context) } "strip" => { assert_eq!(args.len(), 1); - text::strip(macros, &args[0]) + text::strip(macros, &args[0], eval_context) } "findstring" => { assert_eq!(args.len(), 2); - text::findstring(macros, &args[0], &args[1]) + text::findstring(macros, &args[0], &args[1], eval_context) } "filter" => { assert_eq!(args.len(), 2); - text::filter(macros, &args[0], &args[1]) + text::filter(macros, &args[0], &args[1], eval_context) } "filter-out" => { assert_eq!(args.len(), 2); - text::filter_out(macros, &args[0], &args[1]) + text::filter_out(macros, &args[0], &args[1], eval_context) } "sort" => { assert_eq!(args.len(), 1); - text::sort(macros, &args[0]) + text::sort(macros, &args[0], eval_context) } "word" => { assert_eq!(args.len(), 2); - text::word(macros, &args[0], &args[1]) + text::word(macros, &args[0], &args[1], eval_context) } "words" => { assert_eq!(args.len(), 1); - text::words(macros, &args[0]) + text::words(macros, &args[0], eval_context) } "firstword" => { assert_eq!(args.len(), 1); - text::firstword(macros, &args[0]) + text::firstword(macros, &args[0], eval_context) } "lastword" => { assert_eq!(args.len(), 1); - text::lastword(macros, &args[0]) + text::lastword(macros, &args[0], eval_context) } "dir" => { assert_eq!(args.len(), 1); - file_name::dir(macros, &args[0]) + file_name::dir(macros, &args[0], eval_context) } "notdir" => { assert_eq!(args.len(), 1); - file_name::notdir(macros, &args[0]) + file_name::notdir(macros, &args[0], eval_context) } "basename" => { assert_eq!(args.len(), 1); - file_name::basename(macros, &args[0]) + file_name::basename(macros, &args[0], eval_context) } "addsuffix" => { assert_eq!(args.len(), 2); - file_name::addsuffix(macros, &args[0], &args[1]) + file_name::addsuffix(macros, &args[0], &args[1], eval_context) } "addprefix" => { assert_eq!(args.len(), 2); - file_name::addprefix(macros, &args[0], &args[1]) + file_name::addprefix(macros, &args[0], &args[1], eval_context) } "wildcard" => { assert_eq!(args.len(), 1); - file_name::wildcard(macros, &args[0]) + file_name::wildcard(macros, &args[0], eval_context) } "realpath" => { assert_eq!(args.len(), 1); - file_name::realpath(macros, &args[0]) + file_name::realpath(macros, &args[0], eval_context) } "abspath" => { assert_eq!(args.len(), 1); - file_name::abspath(macros, &args[0]) + file_name::abspath(macros, &args[0], eval_context) } "if" => { assert!(args.len() == 2 || args.len() == 3); - conditional::r#if(macros, &args[0], &args[1], args.get(2)) + conditional::r#if(macros, &args[0], &args[1], args.get(2), eval_context) } "or" => { assert!(!args.is_empty()); - conditional::or(macros, args.iter()) + conditional::or(macros, args.iter(), eval_context) } "and" => { assert!(!args.is_empty()); - conditional::and(macros, args.iter()) + conditional::and(macros, args.iter(), eval_context) } "foreach" => { assert_eq!(args.len(), 3); - foreach(macros, &args[0], &args[1], &args[2]) + foreach(macros, &args[0], &args[1], &args[2], eval_context) } "call" => { assert!(!args.is_empty()); - call(macros, args.iter()) + call(macros, args.iter(), eval_context) } "eval" => { assert_eq!(args.len(), 1); - let should_eval = eval(macros, &args[0])?; - if let Some(to_eval) = to_eval { - to_eval.borrow_mut().push(should_eval); + let should_eval = eval(macros, &args[0], eval_context.as_deref_mut())?; + if let Some(eval_context) = eval_context { + eval_context.eval(should_eval)?; } else { bail!("tried to eval something but no eval back-channel was available"); } @@ -132,17 +134,17 @@ pub fn expand_call( "origin" => { assert_eq!(args.len(), 1); - origin(macros, &args[0]) + origin(macros, &args[0], eval_context) } "error" => { assert_eq!(args.len(), 1); - meta::error(macros, &args[0]) + meta::error(macros, &args[0], eval_context) } "shell" => { assert_eq!(args.len(), 1); - shell(macros, &args[0]) + shell(macros, &args[0], eval_context) } // fallback @@ -159,10 +161,11 @@ mod text { from: &TokenString, to: &TokenString, text: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let from = macros.expand(from)?; - let to = macros.expand(to)?; - let text = macros.expand(text)?; + let from = macros.expand(from, eval_context.as_deref_mut())?; + let to = macros.expand(to, eval_context.as_deref_mut())?; + let text = macros.expand(text, eval_context)?; Ok(text.replace(&from, &to)) } @@ -171,10 +174,11 @@ mod text { from: &TokenString, to: &TokenString, text: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let from = macros.expand(from)?; - let to = macros.expand(to)?; - let text = macros.expand(text)?; + let from = macros.expand(from, eval_context.as_deref_mut())?; + let to = macros.expand(to, eval_context.as_deref_mut())?; + let text = macros.expand(text, eval_context)?; let words = text.split_whitespace() .map(|word| { @@ -186,8 +190,12 @@ mod text { Ok(words.join(" ")) } - pub fn strip(macros: &MacroSet, text: &TokenString) -> Result<String> { - let text = macros.expand(text)?; + pub fn strip( + macros: &MacroSet, + text: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let text = macros.expand(text, eval_context)?; // TODO don't allocate this vec let words = text.split_whitespace().collect::<Vec<_>>(); Ok(words.join(" ")) @@ -197,9 +205,10 @@ mod text { macros: &MacroSet, needle: &TokenString, haystack: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let needle = macros.expand(needle)?; - let haystack = macros.expand(haystack)?; + let needle = macros.expand(needle, eval_context.as_deref_mut())?; + let haystack = macros.expand(haystack, eval_context)?; if haystack.contains(&needle) { Ok(needle) } else { @@ -207,10 +216,15 @@ mod text { } } - pub fn filter(macros: &MacroSet, patterns: &TokenString, text: &TokenString) -> Result<String> { - let patterns = macros.expand(patterns)?; + pub fn filter( + macros: &MacroSet, + patterns: &TokenString, + text: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let patterns = macros.expand(patterns, eval_context.as_deref_mut())?; let patterns = patterns.split_whitespace().collect::<Vec<_>>(); - let text = macros.expand(text)?; + let text = macros.expand(text, eval_context)?; let text = text.split_whitespace(); let mut result_pieces = vec![]; for word in text { @@ -228,10 +242,11 @@ mod text { macros: &MacroSet, patterns: &TokenString, text: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let patterns = macros.expand(patterns)?; + let patterns = macros.expand(patterns, eval_context.as_deref_mut())?; let patterns = patterns.split_whitespace().collect::<Vec<_>>(); - let text = macros.expand(text)?; + let text = macros.expand(text, eval_context)?; let text = text.split_whitespace(); let mut result_pieces = vec![]; for word in text { @@ -245,18 +260,27 @@ mod text { Ok(result_pieces.join(" ")) } - pub fn sort(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn sort( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; let mut words = words.split_whitespace().collect::<Vec<_>>(); words.sort_unstable(); words.dedup(); Ok(words.join(" ")) } - pub fn word(macros: &MacroSet, n: &TokenString, text: &TokenString) -> Result<String> { - let n = macros.expand(n)?; + pub fn word( + macros: &MacroSet, + n: &TokenString, + text: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let n = macros.expand(n, eval_context.as_deref_mut())?; let n: usize = n.parse().wrap_err("while calling `word`")?; - let text = macros.expand(text)?; + let text = macros.expand(text, eval_context)?; Ok(text .split_whitespace() .nth(n.saturating_add(1)) @@ -264,18 +288,30 @@ mod text { .to_owned()) } - pub fn words(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn words( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; Ok(words.split_whitespace().count().to_string()) } - pub fn firstword(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn firstword( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; Ok(words.split_whitespace().next().unwrap_or("").to_owned()) } - pub fn lastword(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn lastword( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; Ok(words.split_whitespace().last().unwrap_or("").to_owned()) } } @@ -285,14 +321,19 @@ mod file_name { use std::env; use std::ffi::OsStr; use std::fs; + use std::io::BufRead; use std::path::{Path, MAIN_SEPARATOR}; - use eyre::WrapErr; - use super::*; + use crate::makefile::eval_context::DeferredEvalContext; + use eyre::WrapErr; - pub fn dir(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn dir( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; let words = words .split_whitespace() .map(|word| { @@ -307,8 +348,12 @@ mod file_name { Ok(words.join(" ")) } - pub fn notdir(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn notdir( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; let words = words .split_whitespace() .map(|word| { @@ -321,8 +366,12 @@ mod file_name { Ok(words.join(" ")) } - pub fn basename(macros: &MacroSet, words: &TokenString) -> Result<String> { - let words = macros.expand(words)?; + pub fn basename( + macros: &MacroSet, + words: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let words = macros.expand(words, eval_context)?; let words = words .split_whitespace() .map(|word| { @@ -339,9 +388,10 @@ mod file_name { macros: &MacroSet, suffix: &TokenString, targets: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let suffix = macros.expand(suffix)?; - let targets = macros.expand(targets)?; + let suffix = macros.expand(suffix, eval_context.as_deref_mut())?; + let targets = macros.expand(targets, eval_context)?; let results = targets .split_whitespace() .map(|t| format!("{}{}", t, suffix)) @@ -353,9 +403,10 @@ mod file_name { macros: &MacroSet, prefix: &TokenString, targets: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let prefix = macros.expand(prefix)?; - let targets = macros.expand(targets)?; + let prefix = macros.expand(prefix, eval_context.as_deref_mut())?; + let targets = macros.expand(targets, eval_context)?; let results = targets .split_whitespace() .map(|t| format!("{}{}", prefix, t)) @@ -363,8 +414,12 @@ mod file_name { Ok(results.join(" ")) } - pub fn wildcard(macros: &MacroSet, pattern: &TokenString) -> Result<String> { - let pattern = macros.expand(pattern)?; + pub fn wildcard( + macros: &MacroSet, + pattern: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let pattern = macros.expand(pattern, eval_context)?; let home_dir = env::var("HOME") .ok() .or_else(|| dirs::home_dir().and_then(|p| p.to_str().map(String::from))); @@ -383,8 +438,12 @@ mod file_name { Ok(results.join(" ")) } - pub fn realpath(macros: &MacroSet, targets: &TokenString) -> Result<String> { - let targets = macros.expand(targets)?; + pub fn realpath( + macros: &MacroSet, + targets: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let targets = macros.expand(targets, eval_context)?; let results = targets .split_whitespace() .map(|x| { @@ -396,9 +455,13 @@ mod file_name { Ok(results.join(" ")) } - pub fn abspath(macros: &MacroSet, targets: &TokenString) -> Result<String> { + pub fn abspath( + macros: &MacroSet, + targets: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { // TODO don't resolve symlinks - realpath(macros, targets) + realpath(macros, targets, eval_context) } } @@ -411,24 +474,29 @@ mod conditional { condition: &TokenString, if_true: &TokenString, if_false: Option<&TokenString>, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { let mut condition = condition.clone(); condition.trim_start(); condition.trim_end(); - let condition = macros.expand(&condition)?; + let condition = macros.expand(&condition, eval_context.as_deref_mut())?; if condition.is_empty() { - if_false.map_or_else(|| Ok(String::new()), |if_false| macros.expand(if_false)) + if_false.map_or_else( + || Ok(String::new()), + |if_false| macros.expand(if_false, eval_context), + ) } else { - macros.expand(if_true) + macros.expand(if_true, eval_context) } } pub fn or<'a>( macros: &MacroSet, args: impl Iterator<Item = &'a TokenString>, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { for arg in args { - let arg = macros.expand(arg)?; + let arg = macros.expand(arg, eval_context.as_deref_mut())?; if !arg.is_empty() { return Ok(arg); } @@ -439,10 +507,11 @@ mod conditional { pub fn and<'a>( macros: &MacroSet, args: impl Iterator<Item = &'a TokenString>, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { let mut last = String::new(); for arg in args { - last = macros.expand(arg)?; + last = macros.expand(arg, eval_context.as_deref_mut())?; if last.is_empty() { return Ok(String::new()); } @@ -456,9 +525,10 @@ pub fn foreach( var: &TokenString, list: &TokenString, text: &TokenString, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, ) -> Result<String> { - let var = macros.expand(var)?; - let list = macros.expand(list)?; + let var = macros.expand(var, eval_context.as_deref_mut())?; + let list = macros.expand(list, eval_context.as_deref_mut())?; let words = list.split_whitespace(); let mut macros = macros.with_overlay(); @@ -473,15 +543,19 @@ pub fn foreach( eagerly_expanded: false, }, ); - macros.expand(text) + macros.expand(text, eval_context.as_deref_mut()) }) .collect::<Result<Vec<_>, _>>()?; Ok(results.join(" ")) } -pub fn call<'a>(macros: &MacroSet, args: impl Iterator<Item = &'a TokenString>) -> Result<String> { +pub fn call<'a>( + macros: &MacroSet, + args: impl Iterator<Item = &'a TokenString>, + mut eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, +) -> Result<String> { let args = args - .map(|arg| macros.expand(arg)) + .map(|arg| macros.expand(arg, eval_context.as_deref_mut())) .collect::<Result<Vec<_>, _>>()?; let function = args[0].clone(); // TODO if function is a builtin, call the builtin instead @@ -498,31 +572,47 @@ pub fn call<'a>(macros: &MacroSet, args: impl Iterator<Item = &'a TokenString>) }, ); } - macros.expand(&TokenString::r#macro(function)) + macros.expand(&TokenString::r#macro(function), eval_context) } // TODO consider bringing eval logic in here since we put the Vec in MacroSet IIRC -pub fn eval(macros: &MacroSet, arg: &TokenString) -> Result<String> { - macros.expand(arg) +pub fn eval( + macros: &MacroSet, + arg: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, +) -> Result<String> { + macros.expand(arg, eval_context) } -pub fn origin(macros: &MacroSet, variable: &TokenString) -> Result<String> { - let variable = macros.expand(variable)?; +pub fn origin( + macros: &MacroSet, + variable: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, +) -> Result<String> { + let variable = macros.expand(variable, eval_context)?; Ok(macros.origin(&variable).to_owned()) } mod meta { use super::*; - pub fn error(macros: &MacroSet, text: &TokenString) -> Result<String> { - let text = macros.expand(text)?; + pub fn error( + macros: &MacroSet, + text: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, + ) -> Result<String> { + let text = macros.expand(text, eval_context)?; bail!("{}", text); } } -pub fn shell(macros: &MacroSet, command: &TokenString) -> Result<String> { +pub fn shell( + macros: &MacroSet, + command: &TokenString, + eval_context: Option<&mut DeferredEvalContext<impl BufRead>>, +) -> Result<String> { // TODO bring this in from command_line - let command = macros.expand(command)?; + let command = macros.expand(command, eval_context)?; let (program, args) = if cfg!(windows) { let cmd = env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".into()); let args = vec!["/c", &command]; @@ -551,7 +641,7 @@ mod test { type R = Result<()>; fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> Result<String> { - super::expand_call(name, args, macros, None) + super::expand_call(name, args, macros, NO_EVAL) } macro_rules! call { |