bug-make
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC] Scoped variables, supercharged


From: Jouke Witteveen
Subject: [RFC] Scoped variables, supercharged
Date: Thu, 26 Dec 2019 14:03:19 +0100

Hi,

I would like make to have scoped variables. Here, I will propose an
implementation of them. This implementation is currently without tests and
documentation. Hopefully, the proposal is acceptable and I can add the
tests and documentation.

Consider a situation in which we have macros F and G, and some variable X,
and our makefile includes:

  $(call F,$(call G,$(X)),$(call G,$(X)))

Here, we duplicate the call to G. To make that more transparent (and
assuming G does not introduce side-effects), we could write the above as:

  Y := $(call G,$(X))
  $(call F,$(Y),$(Y))
  undefine Y

However, this would interfere with any existing variable Y.
Alternatively, we could try:

  $(foreach Y,$(call G,$(X)), \
    $(call F,$(Y),$(Y)))

but that would not work if $(call G,$(X)) yields a list.

A solution would be a new function, 'let', which allows us to write

  $(let Y,$(call G,$(X)), \
    $(call F,$(Y),$(Y)))

This function can be implemented easily. It can even be given superpowers.
If the first argument to the new let-function is a single name, it is
assigned the full second argument. If it is multiple names, say n, we can
assign the first n-1 names to the first n-1 words of the second argument
and the final name to the remainder of the arguments (adding empty words
as necessary).

This also solves http://savannah.gnu.org/bugs/?51286 and makes something
like

  reverse = $(let first rest,$1,$(if $(rest),$(call reverse,$(rest)) )$(first))

possible.

I have included an implementation bbelow, borrowing from the
implementation of foreach and call. Let me know if I can go forward with
this idea and prepare a patch including tests and documentation.

Regards,
- Jouke

---
 src/function.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/src/function.c b/src/function.c
index 4ebff16..1c1c38b 100644
--- a/src/function.c
+++ b/src/function.c
@@ -908,6 +908,53 @@ func_foreach (char *o, char **argv, const char *funcname 
UNUSED)
   return o;
 }
 
+static char *
+func_let (char *o, char **argv, const char *funcname UNUSED)
+{
+  /* expand only the first two.  */
+  char *varnames = expand_argument (argv[0], NULL);
+  char *list = expand_argument (argv[1], NULL);
+  const char *body = argv[2];
+
+  const char *list_iterator = list;
+  char *p;
+  size_t len;
+  size_t vlen;
+  const char *vp_next = varnames;
+  const char *vp = find_next_token (&vp_next, &vlen);
+
+  push_new_variable_scope ();
+
+  /* loop through LIST for all but the last VARNAME */
+  NEXT_TOKEN (vp_next);
+  while (*vp_next != '\0')
+    {
+      p = find_next_token (&list_iterator, &len);
+      if (*list_iterator != '\0')
+        {
+          ++list_iterator;
+          p[len] = '\0';
+        }
+      define_variable (vp, vlen, p ? p : "", o_automatic, 0);
+
+      vp = find_next_token (&vp_next, &vlen);
+      NEXT_TOKEN (vp_next);
+    }
+  if (vp)
+    define_variable (vp, vlen, next_token (list_iterator), o_automatic, 0);
+
+  /* Expand the body in the context of the arguments, adding the result to
+     the variable buffer.  */
+
+  o = variable_expand_string (o, body, SIZE_MAX);
+
+  pop_variable_scope ();
+  free (varnames);
+  free (list);
+
+  return o + strlen (o);
+}
+
 struct a_word
 {
   struct a_word *next;
@@ -2337,7 +2384,8 @@ func_abspath (char *o, char **argv, const char *funcname 
UNUSED)
    comma-separated values are treated as arguments.
 
    EXPAND_ARGS means that all arguments should be expanded before invocation.
-   Functions that do namespace tricks (foreach) don't automatically expand.  */
+   Functions that do namespace tricks (foreach, let) don't automatically
+   expand.  */
 
 static char *func_call (char *o, char **argv, const char *funcname);
 
@@ -2373,6 +2421,7 @@ static struct function_table_entry function_table_init[] =
   FT_ENTRY ("words",         0,  1,  1,  func_words),
   FT_ENTRY ("origin",        0,  1,  1,  func_origin),
   FT_ENTRY ("foreach",       3,  3,  0,  func_foreach),
+  FT_ENTRY ("let",           3,  3,  0,  func_let),
   FT_ENTRY ("call",          1,  0,  1,  func_call),
   FT_ENTRY ("info",          0,  1,  1,  func_error),
   FT_ENTRY ("error",         0,  1,  1,  func_error),
-- 
2.24.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]