[RFC PATCH 28/28] cli: lil: Load procs from the environment

Sean Anderson
2021-07-01T06:21:02Z

When we start up the LIL interpreter, go through every variable and see if
it looks like a new procedure. If it does, try and parse it. For the return
trip, every time that we create a new procedure, create a new global
variable containing that procedure.

The end result of this is that procedures should now be saved to the
environment in the same way that variables are. So you can do

=> proc foo {args} { ... }
=> env save

and foo will be there after you reboot.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

common/cli.c | 54 ++++++++++++++++++++++++++++-------
common/cli_lil.c | 71 ++++++++++++++++++++++++++++++++++-------------
include/cli_lil.h | 17 ++++++++++++
3 files changed, 113 insertions(+), 29 deletions(-)

diff --git a/common/cli.c b/common/cli.c
index 391fee0ec7..c71f75e684 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -16,9 +16,11 @@
#include <command.h>
#include <console.h>
#include <env.h>
+#include <env_internal.h>
#include <fdtdec.h>
#include <hang.h>
#include <malloc.h>
+#include <search.h>
#include <asm/global_data.h>

DECLARE_GLOBAL_DATA_PTR;
@@ -26,6 +28,21 @@ DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_LIL
static struct lil *lil;

+static enum lil_error print_lil_err(struct lil *lil)
+{
+ enum lil_error ret;
+ const char *err_msg;
+
+ ret = lil_error(lil, &err_msg);
+ if (ret) {
+ if (err_msg)
+ printf("error: %s\n", err_msg);
+ else
+ printf("error: %d\n", ret);
+ }
+ return ret;
+}
+
static int env_setvar(struct lil *lil, const char *name,
struct lil_value **value)
{
@@ -41,16 +58,40 @@ static int env_getvar(struct lil *lil, const char *name,
return 1;
}

+static int env_register_proc(struct env_entry *entry, void *priv)
+{
+ struct lil *lil = priv;
+ struct lil_value *name;
+ const char *name_str, prefix[] = "proc";
+
+ /* Skip variables which are obviously not procedures */
+ if (strncmp(entry->data, prefix, sizeof(prefix) - 1))
+ return 0;
+
+ name = lil_parse_eval(lil, entry->data, 0, true);
+ name_str = lil_to_string(name);
+ if (strcmp(entry->key, name_str))
+ log_debug("proc %s created by variable %s\n",
+ name_str, entry->key);
+ lil_free_value(name);
+ return print_lil_err(lil);
+}
+
+static int env_initprocs(struct lil *lil)
+{
+ return hwalk_r(&env_htab, env_register_proc, lil);
+}
+
static const struct lil_callbacks env_callbacks = {
.setvar = env_setvar,
.getvar = env_getvar,
+ .initprocs = env_initprocs,
};

static int lil_run(const char *cmd)
{
- int err;
struct lil_value *result = lil_parse_eval(lil, cmd, 0, true);
- const char *err_msg, *strres = lil_to_string(result);
+ const char *strres = lil_to_string(result);

/* The result may be very big, so use puts */
if (strres && strres[0]) {
@@ -59,14 +100,7 @@ static int lil_run(const char *cmd)
}
lil_free_value(result);

- err = lil_error(lil, &err_msg);
- if (err) {
- if (err_msg)
- printf("error: %s\n", err_msg);
- else
- printf("error: %d\n", err);
- }
- return !!err;
+ return !!print_lil_err(lil);
}
#endif

diff --git a/common/cli_lil.c b/common/cli_lil.c
index 2a8600ffb6..7b4a56dbd0 100644
--- a/common/cli_lil.c
+++ b/common/cli_lil.c
@@ -3218,9 +3218,11 @@ static struct lil_value *fnc_reflect(struct lil *lil, size_t argc,
static struct lil_value *fnc_proc(struct lil *lil, size_t argc,
struct lil_value **argv)
{
+ static const char fmt[] = "proc %s {%s} %s";
+ size_t n;
struct lil_func *cmd;
- struct lil_list *args;
- struct lil_value *name, *code;
+ struct lil_list *fargs;
+ struct lil_value *name, *args, *code, *val;

if (argc != 3) {
lil_set_error_argc(lil, 3);
@@ -3228,34 +3230,62 @@ static struct lil_value *fnc_proc(struct lil *lil, size_t argc,
}

name = lil_clone_value(argv[0]);
- if (!name) {
- lil_set_error_oom(lil);
- return NULL;
- }
-
- args = lil_subst_to_list(lil, argv[1]);
- if (!args)
- goto err_args;
-
code = lil_clone_value(argv[2]);
- if (!code) {
+ if (!name || !code) {
lil_set_error_oom(lil);
- goto err_code;
+ goto err_name_fargs_code;
}

+ fargs = lil_subst_to_list(lil, argv[1]);
+ if (!fargs)
+ goto err_name_fargs_code;
+
cmd = add_func(lil, lil_to_string(name));
if (!cmd)
goto err_func;
- cmd->argnames = args;
+ cmd->argnames = fargs;
cmd->code = code;

+ args = lil_list_to_value(fargs);
+ code = lil_quote_value(code);
+ if (!args || !code) {
+ lil_set_error_oom(lil);
+ goto err_quote_val;
+ }
+
+ n = snprintf(NULL, 0, fmt, lil_to_string(name), lil_to_string(args),
+ lil_to_string(code));
+ val = alloc_value_len(NULL, n);
+ if (!val) {
+ lil_set_error_oom(lil);
+ goto err_quote_val;
+ }
+
+ snprintf(val->d, n + 1, fmt, lil_to_string(name), lil_to_string(args),
+ lil_to_string(code));
+ val->l = n;
+ if (!lil_set_var(lil, lil_to_string(name), val, LIL_SETVAR_GLOBAL))
+ goto err_set;
+
+ lil_free_value(val);
+ lil_free_value(code);
+ lil_free_value(args);
+
return name;

-err_func:
+err_set:
+ lil_free_value(val);
+err_quote_val:
+ del_func(lil, cmd);
+ lil_free_value(code);
+ lil_free_value(args);
+ lil_free_value(name);
+ return NULL;
+
+err_func:
+ lil_free_list(fargs);
+err_name_fargs_code:
lil_free_value(code);
-err_code:
- lil_free_list(args);
-err_args:
lil_free_value(name);
return NULL;
}
@@ -4235,12 +4265,15 @@ static void register_stdcmds(struct lil *lil)
struct cmd_tbl *cmdtp, *start = ll_entry_start(struct cmd_tbl, cmd);
const int len = ll_entry_count(struct cmd_tbl, cmd);

+ lil_register(lil, "proc", fnc_proc);
+ if (lil->callbacks.initprocs)
+ lil->callbacks.initprocs(lil);
+
lil_register(lil, "decr", fnc_decr);
lil_register(lil, "eval", fnc_eval);
lil_register(lil, "expr", fnc_expr);
lil_register(lil, "for", fnc_for);
lil_register(lil, "foreach", fnc_foreach);
- lil_register(lil, "proc", fnc_proc);
lil_register(lil, "if", fnc_if);
lil_register(lil, "incr", fnc_incr);
lil_register(lil, "local", fnc_local);
diff --git a/include/cli_lil.h b/include/cli_lil.h
index 6fbc270f1b..47a2eeefc9 100644
--- a/include/cli_lil.h
+++ b/include/cli_lil.h
@@ -94,6 +94,23 @@ struct lil_callbacks {
*/
int (*getvar)(struct lil *lil, const char *name,
struct lil_value **value);
+
+ /**
+ * @initprocs: Called when procedures should be created
+ *
+ * @lil: The LIL interpreter
+ *
+ * This is called once in lil_new() when user functions should be added.
+ * When this is called, the only function registered is "proc". You can
+ * register functions by calling lil_parse_eval() with code which
+ * initializes a procedure.
+ *
+ * In practice, this is expected to be used to initialize procedures
+ * from environmental variables.
+ *
+ * @Return: 0 if ok, or non-zero on error.
+ */
+ int (*initprocs)(struct lil *lil);
};

/**
--
2.32.0