[RFC] Scoped variables, supercharged

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

[RFC] Scoped variables, supercharged

Jouke Witteveen
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 | Threaded
Open this post in threaded view
|

[RFC] Scoped variables, supercharged

Gnu - Make - Bugs mailing list
Jouke Witteveen writes:
 > 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)))

 Your proposal has the potential to create variables that would have
 scope local to a single invocation of a user-defined function, but it
 wouldn't provide scoping to Make-proper.  For that reason alone, I
 would suggest narrowing down the naming of the feature.  Perhaps
 something like:

   Function local variables

 Have you considered how this might affect target- and
 pattern-specific variables?

 What would the affect be of a local variable overriding the name of a
 global variable?

 Finally, have you taken a look at the so-called Gnu Make Standard
 Library (GMSL)?  The implementation of dictionaries in that piece of
 software reduces the need to introduce changes to Gnu Make to support
 variable scoping -- but I accept that might be a controversial view.



Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Jouke Witteveen
On Thu, Dec 26, 2019 at 6:13 PM <[hidden email]> wrote:

> Jouke Witteveen writes:
>  > 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)))
>
>  Your proposal has the potential to create variables that would have
>  scope local to a single invocation of a user-defined function, but it
>  wouldn't provide scoping to Make-proper.  For that reason alone, I
>  would suggest narrowing down the naming of the feature.  Perhaps
>  something like:

What other meaning of 'scoping to Make-proper' do you have in mind? I
fail to see a scoping scenario that is not covered by my $(let)
proposal.

>    Function local variables

Except that it has nothing to do with functions. Instead of the call
statements in my example, you can think of any long statement that
uses the same substatement (that has no side-effects) multiple times.

>  Have you considered how this might affect target- and
>  pattern-specific variables?
>
>  What would the affect be of a local variable overriding the name of a
>  global variable?

Global variables are shielded in the same way that $(call ...) shields
$0, $1, $2, ... in nested invocations.

>  Finally, have you taken a look at the so-called Gnu Make Standard
>  Library (GMSL)?  The implementation of dictionaries in that piece of
>  software reduces the need to introduce changes to Gnu Make to support
>  variable scoping -- but I accept that might be a controversial view.

I am aware of that library. I'd say it would benefit greatly from the
addition of my $(let) built-in to make.

Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Paul Smith-20
On Thu, 2019-12-26 at 21:24 +0100, Jouke Witteveen wrote:
> >   Your proposal has the potential to create variables that would have
> >   scope local to a single invocation of a user-defined function, but it
> >   wouldn't provide scoping to Make-proper.  For that reason alone, I
> >   would suggest narrowing down the naming of the feature.  Perhaps
> >   something like:
>
> What other meaning of 'scoping to Make-proper' do you have in mind? I
> fail to see a scoping scenario that is not covered by my $(let)
> proposal.

I believe thutt is thinking of creating new scopes in makefiles themselves,
not within a variable/function context, for example within included
makefiles or even within subsections of a particular makefile.

For example something where you could say:

   FOO = bar

   push scope
   local FOO = baz

   $(FOO): blah
   pop scope

   $(FOO): yuck

Of course if you wanted to write a bunch of define/endif stuff and use eval
you could use $(let ...) to do something like this but it can get pretty
gross.

That is a separate feature that wouldn't help you do what you want to do,
but that's probably why thutt suggests finding a more specific term.  In
Lisp, "let" creates "local bindings" not a new scope.  I'm not sure if that
is more or less clear for people not familiar with Lisp.


Here's another question: will this "do what you expect" if you invoke an
include within an eval within a let?  For example:

  $(let FOO BAR,,$(eval include myfile.mk))

will ensure that any "global" settings of FOO or BAR that are made inside
of myfile.mk are forgotten about afterwards?

What other sorts of tricks or issues could we get into using eval within
let, I wonder...


Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Gnu - Make - Bugs mailing list
In reply to this post by Jouke Witteveen
Jouke Witteveen writes:
 > On Thu, Dec 26, 2019 at 6:13 PM <[hidden email]> wrote:
 > > Jouke Witteveen writes:
 > >  > 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)))
 > >
 > >  Your proposal has the potential to create variables that would have
 > >  scope local to a single invocation of a user-defined function, but it
 > >  wouldn't provide scoping to Make-proper.  For that reason alone, I
 > >  would suggest narrowing down the naming of the feature.  Perhaps
 > >  something like:
 >
 > What other meaning of 'scoping to Make-proper' do you have in mind? I
 > fail to see a scoping scenario that is not covered by my $(let)
 > proposal.
 >
 > >    Function local variables

 As Paul pointed out, regular Makefile variables have a single global
 scope.  For example, the canonical 'CC' variable is global.  Changing
 it affects everything in the entire Makefile (include-ed files, too).

 The flat namespace of Make gets crowded pretty quickly.  Consider if
 you want to have certain flags set in CFLAGS for a set of files, and
 other mutually exclusive flags set for other files.  As a
 hypothetical example, think about compiling some files for x86-64
 architecture, and others for Arm32.  Without using recursive
 invocation of Make.

 > Except that it has nothing to do with functions. Instead of the call
 > statements in my example, you can think of any long statement that
 > uses the same substatement (that has no side-effects) multiple times.

I see.  If you write Makefiles with a lot of Gnu Make extensions, this
would be handy.  I use I don't put a lot of But, I generally write Makefiles to look as regular
as possible.  So, expansion of

 >
 > >  Have you considered how this might affect target- and
 > >  pattern-specific variables?
 > >
 > >  What would the affect be of a local variable overriding the name of a
 > >  global variable?
 >
 > Global variables are shielded in the same way that $(call ...) shields
 > $0, $1, $2, ... in nested invocations.

 Are you sure?  If I set the value of CC to '/usr/bin/python' with
 $(eval), what will happen?


--
My New Years Resolution is 4K.


Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Jouke Witteveen
In reply to this post by Paul Smith-20
On Thu, Dec 26, 2019 at 10:52 PM Paul Smith <[hidden email]> wrote:

>
> On Thu, 2019-12-26 at 21:24 +0100, Jouke Witteveen wrote:
> > >   Your proposal has the potential to create variables that would have
> > >   scope local to a single invocation of a user-defined function, but it
> > >   wouldn't provide scoping to Make-proper.  For that reason alone, I
> > >   would suggest narrowing down the naming of the feature.  Perhaps
> > >   something like:
> >
> > What other meaning of 'scoping to Make-proper' do you have in mind? I
> > fail to see a scoping scenario that is not covered by my $(let)
> > proposal.
>
> I believe thutt is thinking of creating new scopes in makefiles themselves,
> not within a variable/function context, for example within included
> makefiles or even within subsections of a particular makefile.

Ah, now I understand. With $(let), the scope is determined by the
parentheses, but theoretically, a file-scope could be a thing too.

> For example something where you could say:
>
>    FOO = bar
>
>    push scope
>    local FOO = baz
>
>    $(FOO): blah
>    pop scope
>
>    $(FOO): yuck
>
> Of course if you wanted to write a bunch of define/endif stuff and use eval
> you could use $(let ...) to do something like this but it can get pretty
> gross.

Let's run a little test. I have a Makefile containing:
----
X=global
$(let X,local,$(eval include inc.mk))
$(info $X)
----
And a file callend inc.mk containing:
----
$(info $X)
X=included
$(info $X)
----
We can see what happens even without recompiling make by replacing the
instance of $(let) by $(foreach). The resulting behavior is the same
(because 'local' is only one word). We get the following:
----
$ make
local
local
included
----

To me, it would make more sense if the second line read 'included' and
the last line read 'global', so maybe there is a bug in how make
currently deals with variable scopes.

> That is a separate feature that wouldn't help you do what you want to do,
> but that's probably why thutt suggests finding a more specific term.  In
> Lisp, "let" creates "local bindings" not a new scope.  I'm not sure if that
> is more or less clear for people not familiar with Lisp.

I modeled $(let) after let expressions of Haskell. I am sure many
other (especially functional) languages have similar let constructs.
There is even a Wikipedia page on the 'Let expression', which mainly
deals with its use in mathematics, but is still very much applicable
to my $(let) proposal. For familiarity in the greater programming
language ecosystem, I think $(let) bindings with a scope defined by
the parentheses is a sensible choice of naming and behavior.

> Here's another question: will this "do what you expect" if you invoke an
> include within an eval within a let?  For example:
>
>   $(let FOO BAR,,$(eval include myfile.mk))
>
> will ensure that any "global" settings of FOO or BAR that are made inside
> of myfile.mk are forgotten about afterwards?
>
> What other sorts of tricks or issues could we get into using eval within
> let, I wonder...

Whatever breaks with the above expression probably also breaks with

  $(foreach FOO,_,$(foreach BAR,_,$(eval include myfile.mk)))

One way to think of why $(let) would be a reasonable addition to make
is precisely because it does not deviate from established behavior too
much. Yet, it provides a clear value. Outside of the scoping, it also
enables basic list unpacking similar to `read` in the shell. My
example with reverse demonstrates this aspect too.

Regards,
- Jouke

Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Gnu - Make - Bugs mailing list
Jouke Witteveen writes:
 > On Thu, Dec 26, 2019 at 10:52 PM Paul Smith <[hidden email]> wrote:
 > >

 <snip>

 > > I believe thutt is thinking of creating new scopes in makefiles themselves,
 > > not within a variable/function context, for example within included
 > > makefiles or even within subsections of a particular makefile.

 Yes.

 > Ah, now I understand. With $(let), the scope is determined by the
 > parentheses, but theoretically, a file-scope could be a thing too.
 >
 > > For example something where you could say:
 > >
 > >    FOO = bar
 > >
 > >    push scope
 > >    local FOO = baz
 > >
 > >    $(FOO): blah
 > >    pop scope
 > >
 > >    $(FOO): yuck
 > >
 > > Of course if you wanted to write a bunch of define/endif stuff and use eval
 > > you could use $(let ...) to do something like this but it can get pretty
 > > gross.
 >
 > Let's run a little test. I have a Makefile containing:
 > ----
 > X=global
 > $(let X,local,$(eval include inc.mk))
 > $(info $X)
 > ----
 > And a file callend inc.mk containing:
 > ----
 > $(info $X)
 > X=included
 > $(info $X)
 > ----
 > We can see what happens even without recompiling make by replacing the
 > instance of $(let) by $(foreach). The resulting behavior is the same
 > (because 'local' is only one word). We get the following:
 > ----
 > $ make
 > local
 > local
 > included
 > ----

 Interesting.  I'd like to raise a few points.

 o I would find it objectionable to have to $(eval) the include of
   files to gain scoping.  At the very least, this would adversely
   affect the readability of Makefiles.

 o I don't think this meachanism correctly captures scoping.  The
   state of $(X) can be set by the master Makefile, or by an include
   file.  Furthermore, it can be changed by another included file.

   But, the actual use of $(X) in a recipe can be deferred to much
   later.  If there are several locations that set $(X) to a different
   value, what value will be used when the recipe is expanded?

 <snip>

 > One way to think of why $(let) would be a reasonable addition to make
 > is precisely because it does not deviate from established behavior too
 > much. Yet, it provides a clear value. Outside of the scoping, it also
 > enables basic list unpacking similar to `read` in the shell. My
 > example with reverse demonstrates this aspect too.

 I agree that there is a clear value.  But I still caution that it
 should not be confused with full semantic scoping in a whole build
 system implemented in Make.

--
My New Years Resolution is 4K.


Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Jouke Witteveen
On Fri, Dec 27, 2019 at 4:29 PM <[hidden email]> wrote:

>
> Jouke Witteveen writes:
>  > On Thu, Dec 26, 2019 at 10:52 PM Paul Smith <[hidden email]> wrote:
>  > >
>
>  <snip>
>
>  > > I believe thutt is thinking of creating new scopes in makefiles themselves,
>  > > not within a variable/function context, for example within included
>  > > makefiles or even within subsections of a particular makefile.
>
>  Yes.

Well, this request is not that ;-). Maybe it can help with it and make
something like it possible through some clever construct, but it was
designed purely for lexical scoping.

>  > Ah, now I understand. With $(let), the scope is determined by the
>  > parentheses, but theoretically, a file-scope could be a thing too.
>  >
>  > > For example something where you could say:
>  > >
>  > >    FOO = bar
>  > >
>  > >    push scope
>  > >    local FOO = baz
>  > >
>  > >    $(FOO): blah
>  > >    pop scope
>  > >
>  > >    $(FOO): yuck
>  > >
>  > > Of course if you wanted to write a bunch of define/endif stuff and use eval
>  > > you could use $(let ...) to do something like this but it can get pretty
>  > > gross.
>  >
>  > Let's run a little test. I have a Makefile containing:
>  > ----
>  > X=global
>  > $(let X,local,$(eval include inc.mk))
>  > $(info $X)
>  > ----
>  > And a file callend inc.mk containing:
>  > ----
>  > $(info $X)
>  > X=included
>  > $(info $X)
>  > ----
>  > We can see what happens even without recompiling make by replacing the
>  > instance of $(let) by $(foreach). The resulting behavior is the same
>  > (because 'local' is only one word). We get the following:
>  > ----
>  > $ make
>  > local
>  > local
>  > included
>  > ----
>
>  Interesting.  I'd like to raise a few points.
>
>  o I would find it objectionable to have to $(eval) the include of
>    files to gain scoping.  At the very least, this would adversely
>    affect the readability of Makefiles.

These experiments were meant to test $(let), not to show any advisable
use of it. At least, that was my understanding of the suggestions by
Paul Smith. The points I was trying to make are that
1) the scope stack can act differently from what you expect, and
2) the obvious shortcomings of $(let) are also shortcomings of $(foreach).

>  o I don't think this meachanism correctly captures scoping.  The
>    state of $(X) can be set by the master Makefile, or by an include
>    file.  Furthermore, it can be changed by another included file.

$(let) provides lexical scoping in line with the use of let
expressions in other languages. The binding occurs at the time of
expansion. After expansion, the expression is fully expanded and the
scoped variables have completely disappeared.

>    But, the actual use of $(X) in a recipe can be deferred to much
>    later.  If there are several locations that set $(X) to a different
>    value, what value will be used when the recipe is expanded?
>
>  <snip>
>
>  > One way to think of why $(let) would be a reasonable addition to make
>  > is precisely because it does not deviate from established behavior too
>  > much. Yet, it provides a clear value. Outside of the scoping, it also
>  > enables basic list unpacking similar to `read` in the shell. My
>  > example with reverse demonstrates this aspect too.
>
>  I agree that there is a clear value.  But I still caution that it
>  should not be confused with full semantic scoping in a whole build
>  system implemented in Make.

I am not sure what you mean by 'full semantic scoping', but I do feel
that you try to make of $(let) something that it is not. I am sure you
are aware that you can give variables target-specific values. This may
be of use when one build system is used for targets on multiple
platforms. Also, make is very liberal in its variable names, so with
proper hygiene and discipline, you can append some namespace-like
prefix to all the variables used in a file.

Regards,
- Jouke

Reply | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Jouke Witteveen
In reply to this post by Jouke Witteveen
On Thu, Dec 26, 2019 at 2:03 PM Jouke Witteveen <[hidden email]> wrote:
> 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.

Was this proposal ever rejected? I think it may have gotten lost a bit
when dealing with the release of GNU Make 4.3.
Also, the reviews of my proposal seemed to have judged it based on
something it is not and does not try to be. All it does is a little
pattern matching and lexical scoping, basically reordering existing
$(foreach) implementation code.

As an extra example of how useful let expressions can be, consider the
following staple of functional programming:

# Right Fold
#  $1: Macro name
#  $2: List
Fold = $(let first rest,$2, \
    $(if $(rest), \
        $(call $1,$(first),$(call $0,$1,$(rest))), \
        $(first)))

Regards,
- Jouke

> 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 | Threaded
Open this post in threaded view
|

Re: [RFC] Scoped variables, supercharged

Paul Smith-20
On Fri, 2020-03-20 at 20:46 +0100, Jouke Witteveen wrote:
> On Thu, Dec 26, 2019 at 2:03 PM Jouke Witteveen <
> [hidden email]> wrote:
> > 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.
>
> Was this proposal ever rejected?

I think this would be a useful addition, personally.

I expect that something like this would require paperwork to assign
copyright to the FSF.  Is that something you'd be willing to do?  If so
you can reply to me privately and I'll set you up.  This can be done
completely electronically from many countries these days.