aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/functions.rs
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2024-11-10 21:20:34 -0700
committerMelody Horn <melody@boringcactus.com>2024-11-10 21:20:34 -0700
commitfca10b517b448b4023ad8c3225e59dcefd4004e4 (patch)
tree7507e959d1f690044945dd6fcf34b5fef3385b14 /src/makefile/functions.rs
parentf945ff33f9312a534ae52f1c763b4150ae41dcf6 (diff)
downloadmakers-fca10b517b448b4023ad8c3225e59dcefd4004e4.tar.gz
makers-fca10b517b448b4023ad8c3225e59dcefd4004e4.zip
overhaul eval architecture
Diffstat (limited to 'src/makefile/functions.rs')
-rw-r--r--src/makefile/functions.rs284
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 {