Howdy all. I propose adding a "letrec" builtin to complement the "let" builtin. The "let" builtin does not allow one to define inner functions or mutually recursive definitions.
Here is an example Makefile using the new function: print-var = $(info $$($1) = '$($(1))') $(letrec \ a,foo, \ b,bar, \ c,$(call double,~hello~), \ double,$(1)$(1), \ $(call print-var,a) \ $(call print-var,b) \ $(call print-var,c) \ $(info 2 x $$(a) = '$(call double,$(a))') \ ) .PHONY: all all: @: Variable names and their values are provided pairwise. "Letrec" strips any leading whitespace from the expansion of each variable name argument. Here is a patch implementing the functionality: >From 57727f01bbb88e57b13eaed7d4693f8c18a8aa58 Mon Sep 17 00:00:00 2001 From: Pete Dietl <petedi...@gmail.com> Date: Wed, 25 Dec 2024 22:24:26 -0800 Subject: [PATCH] Add letrec builtin --- src/function.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/function.c b/src/function.c index b4c38052..ed574b6e 100644 --- a/src/function.c +++ b/src/function.c @@ -952,6 +952,46 @@ func_let (char *o, char **argv, const char *funcname UNUSED) return o + strlen (o); } +static char * +func_letrec (char *o, char **argv, const char *funcname) +{ + size_t num_args = 0; + + while (argv[num_args]) + num_args++; + + if ((num_args % 2) != 1) + { + /* is not a multiple of 2 plus one */ + OS (fatal, *expanding_var, "%s: Wrong number of arguments", funcname); + } + + push_new_variable_scope (); + + for (size_t i = 0; i < (num_args - 1); i += 2) + { + size_t vartok_len; + char *varnamearg = expand_argument (argv[i], NULL); + const char *varname = varnamearg; + char *value = argv[i + 1]; + char *vartok = find_next_token (&varname, &vartok_len); + + if (*vartok == '\0') + OS (fatal, *expanding_var, "%s: Empty variable name", funcname); + define_variable (vartok, vartok_len, value, o_automatic, 1); + free (varnamearg); + } + + /* Expand the body in the context of the arguments, adding the result to + the variable buffer. */ + + o = expand_string_buf (o, argv[num_args - 1], SIZE_MAX); + + pop_variable_scope (); + + return o + strlen (o); +} + struct a_word { struct a_word *chain; @@ -2416,6 +2456,7 @@ static const struct function_table_entry function_table_init[] = FT_ENTRY ("join", 2, 2, 1, func_join), FT_ENTRY ("lastword", 0, 1, 1, func_lastword), FT_ENTRY ("let", 3, 3, 0, func_let), + FT_ENTRY ("letrec", 3, 0, 0, func_letrec), FT_ENTRY ("notdir", 0, 1, 1, func_notdir_suffix), FT_ENTRY ("or", 1, 0, 0, func_or), FT_ENTRY ("origin", 0, 1, 1, func_origin), -- 2.43.0 Please let me know your thoughts and suggestions. Thanks, Pete