aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/macro.rs
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-03-28 14:57:03 -0600
committerMelody Horn <melody@boringcactus.com>2021-03-28 14:57:03 -0600
commitbc5038e6c344803bce76add47b13ceaa61a5bde3 (patch)
treeeec9a8fc36562aa4413cf8be8ef22910fdc75cac /src/makefile/macro.rs
parent82278c3a5aa93204c963a63cc3cfefea1d0fb3fd (diff)
downloadmakers-bc5038e6c344803bce76add47b13ceaa61a5bde3.tar.gz
makers-bc5038e6c344803bce76add47b13ceaa61a5bde3.zip
almost implement all functions
Diffstat (limited to 'src/makefile/macro.rs')
-rw-r--r--src/makefile/macro.rs189
1 files changed, 189 insertions, 0 deletions
diff --git a/src/makefile/macro.rs b/src/makefile/macro.rs
new file mode 100644
index 0000000..139903f
--- /dev/null
+++ b/src/makefile/macro.rs
@@ -0,0 +1,189 @@
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::env;
+use std::fmt;
+use std::rc::{Rc, Weak};
+
+use regex::Regex;
+
+use super::functions;
+use super::token::{Token, TokenString};
+
+#[derive(Debug, Clone)]
+pub(crate) enum MacroSource {
+ File,
+ CommandLineOrMakeflags,
+ Environment,
+ Builtin,
+}
+
+pub(crate) trait LookupInternal: for<'a> Fn(&'a str) -> String {}
+
+impl<F: for<'a> Fn(&'a str) -> String> LookupInternal for F {}
+
+fn lookup_fail(_: &str) -> String {
+ panic!("internal variables not available!");
+}
+
+#[derive(Clone)]
+pub(crate) struct MacroSet {
+ data: HashMap<String, (MacroSource, TokenString)>,
+ lookup_internal: RefCell<Weak<dyn LookupInternal>>,
+}
+
+impl MacroSet {
+ pub(crate) fn new() -> Self {
+ let lookup_fail: Rc<dyn LookupInternal> = Rc::new(lookup_fail);
+ Self {
+ data: HashMap::new(),
+ lookup_internal: RefCell::new(Rc::downgrade(&lookup_fail)),
+ }
+ }
+
+ pub(crate) fn lookup(&self, lookup: Weak<dyn LookupInternal>) {
+ self.lookup_internal.replace(lookup);
+ }
+
+ pub(crate) fn add_builtins(&mut self) {
+ for (k, v) in builtins() {
+ self.data.insert(k.into(), (MacroSource::Builtin, v));
+ }
+ }
+
+ pub(crate) fn add_env(&mut self) {
+ for (k, v) in env::vars() {
+ if k != "MAKEFLAGS" && k != "SHELL" {
+ self.data
+ .insert(k, (MacroSource::Environment, TokenString::text(v)));
+ }
+ }
+ }
+
+ pub(crate) fn set(&mut self, name: String, source: MacroSource, text: TokenString) {
+ self.data.insert(name, (source, text));
+ }
+
+ pub(crate) fn is_defined(&self, name: &str) -> bool {
+ self.data.contains_key(name)
+ }
+
+ pub(crate) fn get(&self, name: &str) -> Option<&(MacroSource, TokenString)> {
+ self.data.get(name)
+ }
+
+ // `remove` is fine, but I think for "remove-and-return" `pop` is better
+ pub(crate) fn pop(&mut self, name: &str) -> Option<(MacroSource, TokenString)> {
+ self.data.remove(name)
+ }
+
+ pub(crate) fn expand(&self, text: &TokenString) -> String {
+ let mut result = String::new();
+ for token in text.tokens() {
+ match token {
+ Token::Text(t) => result.push_str(t),
+ Token::MacroExpansion { name, replacement } => {
+ let internal_macro_names = &['@', '?', '<', '*'][..];
+ let internal_macro_suffices = &['D', 'F'][..];
+ let just_internal = name.len() == 1 && name.starts_with(internal_macro_names);
+ let suffixed_internal = name.len() == 2
+ && name.starts_with(internal_macro_names)
+ && name.ends_with(internal_macro_suffices);
+ let macro_value = if just_internal || suffixed_internal {
+ let lookup_internal = self.lookup_internal.borrow();
+ let lookup_internal = match lookup_internal.upgrade() {
+ Some(f) => f,
+ None => Rc::new(lookup_fail),
+ };
+ lookup_internal(name)
+ } else {
+ self.data
+ .get(name)
+ .map_or_else(String::new, |(_, macro_value)| self.expand(macro_value))
+ };
+ let macro_value = match replacement {
+ Some((subst1, subst2)) => {
+ let subst1 = self.expand(subst1);
+ let subst1_suffix = regex::escape(&subst1);
+ let subst1_suffix =
+ Regex::new(&format!(r"{}\b", subst1_suffix)).unwrap();
+ let subst2 = self.expand(subst2);
+ subst1_suffix.replace_all(&macro_value, subst2).to_string()
+ }
+ None => macro_value,
+ };
+ result.push_str(&macro_value);
+ }
+ Token::FunctionCall { name, args } => {
+ result.push_str(&functions::call(name, args, &self));
+ }
+ }
+ }
+ result
+ }
+}
+
+impl fmt::Debug for MacroSet {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}", &self.data)
+ }
+}
+
+impl fmt::Display for MacroSet {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let pieces = self
+ .data
+ .iter()
+ .map(|(k, (_, v))| format!("{}={}", k, v))
+ .collect::<Vec<_>>();
+ write!(f, "{}", pieces.join("\n"))
+ }
+}
+
+fn builtins() -> Vec<(&'static str, TokenString)> {
+ // Fuck it, might as well.
+ macro_rules! handle {
+ ($value:ident) => {
+ stringify!($value)
+ };
+ ($value:literal) => {
+ $value
+ };
+ }
+ macro_rules! make {
+ ($($name:ident=$value:tt)+) => {vec![$(
+ (stringify!($name), handle!($value).parse().unwrap())
+ ),+]};
+ }
+
+ make![
+ MAKE=makers
+ AR=ar
+ YACC=yacc
+ YFLAGS=""
+ LEX=lex
+ LFLAGS=""
+ LDFLAGS=""
+
+ AS=as
+ CC=cc
+ CXX="g++"
+ CPP="$(CC) -E"
+ FC=f77
+ PC=pc
+ CO=co
+ GET=get
+ LINT=lint
+ MAKEINFO=makeinfo
+ TEX=tex
+ TEXI2DVI=texi2dvi
+ WEAVE=weave
+ CWEAVE=cweave
+ TANGLE=tangle
+ CTANGLE=ctangle
+ RM="rm -f"
+
+ ARFLAGS="rv"
+ CFLAGS=""
+ FFLAGS=""
+ ]
+}