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 <[email protected]>
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