From 89a87698ed39188fee792cc3488bffed2ff525cf Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Sat, 3 Apr 2021 12:26:33 -0600 Subject: implement `eval` --- src/makefile/functions.rs | 31 ++++++++++++++++++++++++++++--- src/makefile/input.rs | 28 ++++++++++++++++++++-------- src/makefile/macro.rs | 24 ++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs index 43ed44b..a22ec3b 100644 --- a/src/makefile/functions.rs +++ b/src/makefile/functions.rs @@ -1,10 +1,18 @@ +use std::cell::RefCell; +use std::rc::Rc; + use eyre::{bail, Result}; use super::pattern::r#match; use super::r#macro::{Set as MacroSet, Source as MacroSource}; use super::token::TokenString; -pub fn expand_call(name: &str, args: &[TokenString], macros: &MacroSet) -> Result { +pub fn expand_call( + name: &str, + args: &[TokenString], + macros: &MacroSet, + to_eval: Option>>>, +) -> Result { match name { "filter" => { assert_eq!(args.len(), 2); @@ -54,7 +62,16 @@ pub fn expand_call(name: &str, args: &[TokenString], macros: &MacroSet) -> Resul } // eval - "eval" => todo!(), + "eval" => { + assert_eq!(args.len(), 1); + let should_eval = eval::eval(macros, &args[0])?; + if let Some(to_eval) = to_eval { + to_eval.borrow_mut().push(should_eval); + } else { + bail!("tried to eval something but no eval back-channel was available"); + } + return Ok(String::new()); + } "origin" => { assert_eq!(args.len(), 1); @@ -268,6 +285,14 @@ mod call { } } +mod eval { + use super::*; + + pub fn eval(macros: &MacroSet, arg: &TokenString) -> Result { + macros.expand(arg) + } +} + mod origin { use super::*; @@ -317,7 +342,7 @@ mod test { type R = Result<()>; fn call(name: &str, args: &[TokenString], macros: &MacroSet) -> Result { - super::expand_call(name, args, macros) + super::expand_call(name, args, macros, None) } macro_rules! call { diff --git a/src/makefile/input.rs b/src/makefile/input.rs index 918ce0a..f269fa4 100644 --- a/src/makefile/input.rs +++ b/src/makefile/input.rs @@ -2,7 +2,7 @@ use std::cell::Cell; use std::collections::HashMap; use std::error::Error as StdError; use std::fs::File; -use std::io::{BufRead, BufReader, Error as IoError, Lines}; +use std::io::{BufRead, BufReader, Cursor, Error as IoError, Lines}; use std::iter::Peekable; use std::path::Path; @@ -181,7 +181,10 @@ impl<'a, R: BufRead> MakefileReader<'a, R> { // POSIX says we only have to handle a single filename, but GNU make // handles arbitrarily many filenames, and it's not like that's more work for field in fields { - self.extend(MakefileReader::read_file(self.args, field)?); + self.extend( + MakefileReader::read_file(self.args, field) + .with_context(|| format!("while including {}", field))?, + ); } continue; } @@ -202,7 +205,18 @@ impl<'a, R: BufRead> MakefileReader<'a, R> { // before we actually test it, see if it's only visible after expanding macros let (line_tokens, line_type) = if let LineType::Unknown = line_type { - let line_tokens = TokenString::text(self.expand_macros(&line_tokens)?); + let line_tokens = TokenString::text(self.expand_macros(&line_tokens)?.trim()); + // and let's eval whatever bullshit needs evaling + #[cfg(feature = "full")] + { + let eval = self.macros.to_eval.take(); + for eval in eval { + self.extend( + MakefileReader::read(self.args, Cursor::new(eval)) + .context("while evaling")?, + ); + } + } let line_type = LineType::of(&line_tokens); (line_tokens, line_type) } else { @@ -499,8 +513,6 @@ impl<'a, R: BufRead> MakefileReader<'a, R> { #[cfg(test)] mod test { - use std::io::Cursor; - use super::*; type R = Result<()>; @@ -545,7 +557,7 @@ endef } #[test] - #[ignore = "I still haven't implemented `eval` or %-based macro substitution."] + #[cfg(feature = "full")] fn eval() -> R { // This, for the record, is a terrible misfeature. // If you need this, you probably shouldn't be using Make. @@ -574,10 +586,10 @@ endef $(foreach prog,$(PROGRAMS),$(eval $(call PROGRAM_template,$(prog)))) $(PROGRAMS): - $(LINK.o) $^ $(LDLIBS) -o $@ +\t$(LINK.o) $^ $(LDLIBS) -o $@ clean: - rm -f $(ALL_OBJS) $(PROGRAMS) +\trm -f $(ALL_OBJS) $(PROGRAMS) "; let args = Args::empty(); diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs index 7fbb4a6..a1f2378 100644 --- a/src/makefile/macro.rs +++ b/src/makefile/macro.rs @@ -1,6 +1,10 @@ +#[cfg(feature = "full")] +use std::cell::RefCell; use std::collections::HashMap; use std::env; use std::fmt; +#[cfg(feature = "full")] +use std::rc::Rc; use eyre::{bail, Result, WrapErr}; use regex::Regex; @@ -26,6 +30,8 @@ pub struct Set<'parent, 'lookup> { parent: Option<&'parent Set<'parent, 'lookup>>, data: HashMap, lookup_internal: Option<&'lookup dyn LookupInternal>, + #[cfg(feature = "full")] + pub to_eval: Rc>>, } impl<'parent, 'lookup> Set<'parent, 'lookup> { @@ -34,6 +40,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { parent: None, data: HashMap::new(), lookup_internal: None, + #[cfg(feature = "full")] + to_eval: Rc::new(RefCell::new(Vec::new())), } } @@ -58,7 +66,10 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { } else if let Some(parent) = self.parent { parent.lookup_internal(name) } else { - bail!("no lookup possible") + bail!( + "tried to lookup {:?} but no lookup function is available", + name + ) } } @@ -121,7 +132,12 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { } #[cfg(feature = "full")] Token::FunctionCall { name, args } => { - result.push_str(&functions::expand_call(name, args, self)?); + result.push_str(&functions::expand_call( + name, + args, + self, + Some(Rc::clone(&self.to_eval)), + )?); } } } @@ -146,6 +162,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { parent: Some(self), data: HashMap::new(), lookup_internal: Some(lookup), + #[cfg(feature = "full")] + to_eval: Default::default(), } } @@ -155,6 +173,8 @@ impl<'parent, 'lookup> Set<'parent, 'lookup> { parent: Some(self), data: HashMap::new(), lookup_internal: None, + #[cfg(feature = "full")] + to_eval: Default::default(), } } } -- cgit v1.2.3