[RFC PATCH 19/28] cli: lil: Add "symbol" structure

Sean Anderson
2021-07-01T06:20:04Z

We need a generic structure to hold symbols parsed by the parser. We would
also like to re-use existing code as much as possible without rewriting
everything. To do this, we hijack the allocators for lil_list and lil_value
and have them allocate enough space for a lil_symbol.

While we're at it, we can make lil_list hold lil_symbols instead of
lil_values. To keep all the old users sane, we just cast back to lil_value
before retrieving the value (with an assert to make sure we're not sending
back something else). Unfortunately, many functions were accessing the
list vector directly, so convert them.

This commit also fixes pools not behaving properly when running out of
memory. This should likely be refactored in the future so everything uses
one set of allocator/free routines to avoid code duplication.

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

common/cli_lil.c | 317 +++++++++++++++++++++++++++++-----------------
include/cli_lil.h | 8 +-
2 files changed, 206 insertions(+), 119 deletions(-)

diff --git a/common/cli_lil.c b/common/cli_lil.c
index 5875fbd46b..06fd37c383 100644
--- a/common/cli_lil.c
+++ b/common/cli_lil.c
@@ -106,18 +106,45 @@ struct lil_env {
int breakrun;
};

+struct lil_symbol;
+
/**
- * struct list - A list of values
- * @v: A list of pointers to &struct lil_value
- * @c: The number of values in this list
+ * struct list - A list of symbols
+ * @v: A list of pointers to symbols
+ * @c: The number of symbols in this list
* @cap: The space allocated for @v
*/
struct lil_list {
- struct lil_value **v;
+ struct lil_symbol **v;
size_t c;
size_t cap;
};

+/**
+ * struct lil_symbol - A symbol parsed by the parser
+ * @LIL_SYMBOL_VALUE: A plain old string and length
+ * @LIL_SYMBOL_VARIABLE: A name of a variable to be substituted
+ * @LIL_SYMBOL_LIST: A list of symbols
+ * @value: A literal value or name of variable
+ * @list: A list of commands in the script
+ * @word: Another word to be evaluated
+ * @type: The type of word
+ */
+struct lil_symbol {
+ union {
+ struct lil_value value;
+ struct lil_symbol *symbol;
+ struct lil_list list;
+ };
+ enum {
+ LIL_SYMBOL_VALUE = 0,
+ LIL_SYMBOL_LIST,
+ LIL_SYMBOL_VARIABLE,
+ LIL_SYMBOL_COMMAND,
+ LIL_SYMBOL_SCRIPT,
+ } type;
+};
+
/**
* struct lil_func - A function which may be evaluated with a list of arguments
* @name: The name of the function
@@ -287,24 +314,50 @@ static int hm_has(struct hashmap *hm, const char *key)
return 0;
}

-#ifdef CONFIG_LIL_POOLS
+static void lil_free_symbol(struct lil_symbol *sym)
+{
+ switch (sym->type) {
+ case LIL_SYMBOL_VALUE:
+ lil_free_value(&sym->value);
+ return;
+ case LIL_SYMBOL_VARIABLE:
+ lil_free_symbol(sym->symbol);
+ free(sym);
+ return;
+ case LIL_SYMBOL_LIST:
+ case LIL_SYMBOL_COMMAND:
+ case LIL_SYMBOL_SCRIPT:
+ lil_free_list(&sym->list);
+ return;
+ }
+ log_debug("unknown type %d\n", sym->type);
+ assert(0);
+}
+
+#if IS_ENABLED(CONFIG_LIL_POOLS)
static struct lil_value *alloc_from_pool(void)
{
- if (poolsize > 0) {
- poolsize--;
- return pool[poolsize];
- } else {
- struct lil_value *val = calloc(1, sizeof(struct lil_value));
-
- return val;
- }
+ if (poolsize > 0)
+ return pool[--poolsize];
+ else
+ return calloc(1, sizeof(struct lil_symbol));
}

static void release_to_pool(struct lil_value *val)
{
if (poolsize == poolcap) {
- poolcap = poolcap ? (poolcap + poolcap / 2) : 64;
- pool = realloc(pool, sizeof(struct lil_value *) * poolcap);
+ size_t npoolcap = poolcap ? (poolcap + poolcap / 2) : 64;
+ struct lil_value **npool =
+ realloc(pool, sizeof(struct lil_symbol *) * npoolcap);
+
+ if (!npool) {
+ free(val->d);
+ free(val);
+ return;
+ }
+
+ poolcap = npoolcap;
+ pool = npool;
}
pool[poolsize++] = val;
}
@@ -319,45 +372,48 @@ static void ensure_capacity(struct lil_value *val, size_t cap)
#else
static struct lil_value *alloc_from_pool(void)
{
- return NULL;
+ return calloc(1, sizeof(struct lil_symbol));
}

-static void release_to_pool(struct lil_value *val) { }
-static void ensure_capacity(struct lil_value *val, size_t cap) { }
+static void release_to_pool(struct lil_value *val)
+{
+ free(val->d);
+ free(val);
+}
+static void ensure_capacity(struct lil_value *val, size_t cap)
+{
+ val->d = realloc(val->d, cap);
+}
#endif

+static struct lil_symbol *value_to_symbol(struct lil_value *val)
+{
+ return container_of(val, struct lil_symbol, value);
+}
+
static struct lil_value *alloc_value_len(const char *str, size_t len)
{
struct lil_value *val;

- if (IS_ENABLED(CONFIG_LIL_POOLS))
- val = alloc_from_pool();
- else
- val = calloc(1, sizeof(struct lil_value));
+ val = alloc_from_pool();
if (!val)
return NULL;
+ value_to_symbol(val)->type = LIL_SYMBOL_VALUE;

if (str) {
val->l = len;
- if (IS_ENABLED(CONFIG_LIL_POOLS)) {
- ensure_capacity(val, len + 1);
- } else {
- val->d = malloc(len + 1);
- if (!val->d) {
- free(val);
- return NULL;
- }
+ ensure_capacity(val, len + 1);
+ if (!val->d) {
+ release_to_pool(val);
+ return NULL;
}
memcpy(val->d, str, len);
val->d[len] = 0;
} else {
val->l = 0;
- if (IS_ENABLED(CONFIG_LIL_POOLS)) {
- ensure_capacity(val, 1);
+ ensure_capacity(val, 1);
+ if (val->d)
val->d[0] = '\0';
- } else {
- val->d = NULL;
- }
}
return val;
}
@@ -372,53 +428,41 @@ struct lil_value *lil_clone_value(struct lil_value *src)
return alloc_value_len(src->d, src->l);
}

-int lil_append_char(struct lil_value *val, char ch)
+enum lil_error lil_append_char(struct lil_value *val, char ch)
{
- if (IS_ENABLED(CONFIG_LIL_POOLS)) {
- ensure_capacity(val, val->l + 2);
- val->d[val->l++] = ch;
- val->d[val->l] = '\0';
- } else {
- char *new = realloc(val->d, val->l + 2);
+ ensure_capacity(val, val->l + 2);
+ if (!val->d)
+ return LIL_ERR_OOM;

- if (!new)
- return 0;
-
- new[val->l++] = ch;
- new[val->l] = 0;
- val->d = new;
- }
- return 1;
+ val->d[val->l++] = ch;
+ val->d[val->l] = '\0';
+ return LIL_ERR_NONE;
}

-int lil_append_string_len(struct lil_value *val, const char *s, size_t len)
+static enum lil_error lil_append_string_len(struct lil_value *val,
+ const char *s, size_t len)
{
if (!s || !s[0])
- return 1;
+ return LIL_ERR_NONE;

- if (IS_ENABLED(CONFIG_LIL_POOLS)) {
- ensure_capacity(val, val->l + len + 1);
- memcpy(val->d + val->l, s, len + 1);
- } else {
- char *new = realloc(val->d, val->l + len + 1);
-
- if (!new)
- return 0;
- memcpy(new + val->l, s, len + 1);
- val->d = new;
- }
+ ensure_capacity(val, val->l + len + 1);
+ if (!val->d)
+ return LIL_ERR_OOM;
+ memcpy(val->d + val->l, s, len + 1);
val->l += len;
- return 1;
+ return LIL_ERR_NONE;
}

-int lil_append_string(struct lil_value *val, const char *s)
+enum lil_error lil_append_string(struct lil_value *val, const char *s)
{
return lil_append_string_len(val, s, strlen(s));
}

-int lil_append_val(struct lil_value *val, struct lil_value *v)
+enum lil_error lil_append_val(struct lil_value *val, struct lil_value *v)
{
- return lil_append_string_len(val, v->d, v->l);
+ if (v)
+ return lil_append_string_len(val, v->d, v->l);
+ return LIL_ERR_NONE;
}

void lil_free_value(struct lil_value *val)
@@ -426,23 +470,28 @@ void lil_free_value(struct lil_value *val)
if (!val)
return;

- if (IS_ENABLED(CONFIG_LIL_POOLS)) {
- release_to_pool(val);
- } else {
- free(val->d);
- free(val);
- }
+ release_to_pool(val);
+}
+
+static struct lil_symbol *list_to_symbol(struct lil_list *list)
+{
+ return container_of(list, struct lil_symbol, list);
}

struct lil_list *lil_alloc_list(void)
{
struct lil_list *list;

- if (IS_ENABLED(CONFIG_LIL_POOLS) && listpoolsize > 0)
- return listpool[--listpoolsize];
+ if (IS_ENABLED(CONFIG_LIL_POOLS) && listpoolsize > 0) {
+ list = listpool[--listpoolsize];
+ } else {
+ list = calloc(1, sizeof(struct lil_symbol));
+ if (!list)
+ return list;
+ list->v = NULL;
+ }

- list = calloc(1, sizeof(struct lil_list));
- list->v = NULL;
+ list_to_symbol(list)->type = LIL_SYMBOL_LIST;
return list;
}

@@ -454,49 +503,61 @@ void lil_free_list(struct lil_list *list)
return;

for (i = 0; i < list->c; i++)
- lil_free_value(list->v[i]);
+ lil_free_symbol(list->v[i]);

if (IS_ENABLED(CONFIG_LIL_POOLS)) {
list->c = 0;
if (listpoolsize == listpoolcap) {
+ int ncap;
+ struct lil_list **npool;
+
if (listpoolcap)
- listpoolcap += listpoolcap / 2;
+ ncap += listpoolcap / 2;
else
- listpoolcap = 32;
- listpool = realloc(listpool,
- sizeof(*listpool) * listpoolcap);
+ ncap = 32;
+ npool = realloc(listpool, sizeof(*npool) * ncap);
+ if (!npool)
+ goto free;
+
+ listpoolcap = ncap;
+ listpool = npool;
}
listpool[listpoolsize++] = list;
} else {
+free:
free(list->v);
free(list);
}
}

-void lil_list_append(struct lil_list *list, struct lil_value *val)
+int lil_list_append(struct lil_list *list, void *item)
{
if (list->c == list->cap) {
size_t cap = list->cap ? (list->cap + list->cap / 2) : 32;
- struct lil_value **nv =
- realloc(list->v, sizeof(struct lil_value *) * cap);
+ struct lil_symbol **nv = realloc(list->v, sizeof(void *) * cap);

if (!nv)
- return;
+ return -ENOMEM;

list->cap = cap;
list->v = nv;
}
- list->v[list->c++] = val;
+ list->v[list->c++] = item;
+ return 0;
}

-size_t lil_list_size(struct lil_list *list)
+static struct lil_symbol *lil_list_gets(struct lil_list *list, size_t index)
{
- return list->c;
+ assert(index < list->c);
+ return list->v[index];
}

struct lil_value *lil_list_get(struct lil_list *list, size_t index)
{
- return index >= list->c ? NULL : list->v[index];
+ struct lil_symbol *sym = lil_list_gets(list, index);
+
+ assert(sym->type == LIL_SYMBOL_VALUE);
+ return &sym->value;
}

static int needs_escape(const char *str)
@@ -519,25 +580,26 @@ struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape)
size_t i, j;

for (i = 0; i < list->c; i++) {
+ struct lil_value *item = lil_list_get(list, i);
int escape =
- do_escape ? needs_escape(lil_to_string(list->v[i])) : 0;
+ do_escape ? needs_escape(lil_to_string(item)) : 0;

if (i)
lil_append_char(val, ' ');

if (escape) {
lil_append_char(val, '{');
- for (j = 0; j < list->v[i]->l; j++) {
- if (list->v[i]->d[j] == '{')
+ for (j = 0; j < item->l; j++) {
+ if (item->d[j] == '{')
lil_append_string(val, "}\"\\o\"{");
- else if (list->v[i]->d[j] == '}')
+ else if (item->d[j] == '}')
lil_append_string(val, "}\"\\c\"{");
else
- lil_append_char(val, list->v[i]->d[j]);
+ lil_append_char(val, item->d[j]);
}
lil_append_char(val, '}');
} else {
- lil_append_val(val, list->v[i]);
+ lil_append_val(val, item);
}
}
return val;
@@ -588,15 +650,23 @@ void lil_free_env(struct lil_env *env)
free(env->var);

if (envpoolsize == envpoolcap) {
+ int ncap;
+ struct lil_env **npool;
+
if (envpoolcap)
envpoolcap += envpoolcap / 2;
else
envpoolcap = 64;
- envpool = realloc(envpool,
- sizeof(*envpool) * envpoolcap);
+ npool = realloc(envpool, sizeof(*npool) * ncap);
+ if (!npool)
+ goto free;
+
+ envpoolcap = ncap;
+ envpool = npool;
}
envpool[envpoolsize++] = env;
} else {
+free:
hm_destroy(&env->varmap);
for (i = 0; i < env->vars; i++) {
free(env->var[i]->n);
@@ -660,6 +730,8 @@ static struct lil_func *add_func(struct lil *lil, const char *name)
}

cmd = calloc(1, sizeof(struct lil_func));
+ if (!cmd)
+ return NULL;
cmd->name = strdup(name);

ncmd = realloc(lil->cmd, sizeof(struct lil_func *) * (lil->cmds + 1));
@@ -1093,15 +1165,17 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd,
struct lil_value *r;

if (cmd->proc) {
- lil->env->proc = words->v[0]->d;
- r = cmd->proc(lil, words->c - 1, words->v + 1);
+ lil->env->proc = lil_to_string(lil_list_get(words, 0));
+ r = cmd->proc(lil, words->c - 1,
+ (struct lil_value **)words->v + 1);
lil->env->proc = NULL;
} else {
lil_push_env(lil);
lil->env->func = cmd;

if (cmd->argnames->c == 1 &&
- !strcmp(lil_to_string(cmd->argnames->v[0]), "args")) {
+ !strcmp(lil_to_string(lil_list_get(cmd->argnames, 0)),
+ "args")) {
struct lil_value *args = lil_list_to_value(words, 1);

lil_set_var(lil, "args", args, LIL_SETVAR_LOCAL_NEW);
@@ -1111,14 +1185,15 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd,

for (i = 0; i < cmd->argnames->c; i++) {
struct lil_value *val;
+ struct lil_value *name =
+ lil_list_get(cmd->argnames, i);

if (i < words->c - 1)
- val = words->v[i + 1];
+ val = lil_list_get(words, i + 1);
else
val = lil->empty;

- lil_set_var(lil,
- lil_to_string(cmd->argnames->v[i]),
+ lil_set_var(lil, lil_to_string(name),
val, LIL_SETVAR_LOCAL_NEW);
}
}
@@ -1176,13 +1251,13 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen,
goto cleanup;

if (words->c) {
- struct lil_func *cmd =
- lil_find_cmd(lil, lil_to_string(words->v[0]));
+ const char *cmdname =
+ lil_to_string(lil_list_get(words, 0));
+ struct lil_func *cmd = lil_find_cmd(lil, cmdname);

if (!cmd) {
- if (words->v[0]->l) {
- lil_set_error_nocmd(lil,
- words->v[0]->d);
+ if (cmdname[0]) {
+ lil_set_error_nocmd(lil, cmdname);
goto cleanup;
}
} else {
@@ -2042,6 +2117,9 @@ static struct lil_value *fnc_proc(struct lil *lil, size_t argc,
name = lil_clone_value(argv[0]);
fargs = lil_subst_to_list(lil, argv[1]);
cmd = add_func(lil, lil_to_string(argv[0]));
+ if (!cmd)
+ return NULL;
+
cmd->argnames = fargs;
cmd->code = lil_clone_value(argv[2]);
} else {
@@ -2052,11 +2130,17 @@ static struct lil_value *fnc_proc(struct lil *lil, size_t argc,
fargs = lil_subst_to_list(lil, tmp);
lil_free_value(tmp);
cmd = add_func(lil, lil_to_string(name));
+ if (!cmd)
+ return NULL;
+
cmd->argnames = fargs;
cmd->code = lil_clone_value(argv[0]);
} else {
fargs = lil_subst_to_list(lil, argv[0]);
cmd = add_func(lil, lil_to_string(name));
+ if (!cmd)
+ return NULL;
+
cmd->argnames = fargs;
cmd->code = lil_clone_value(argv[1]);
}
@@ -2278,7 +2362,7 @@ static struct lil_value *fnc_index(struct lil *lil, size_t argc,
if (index >= list->c)
r = NULL;
else
- r = lil_clone_value(list->v[index]);
+ r = lil_clone_value(lil_list_get(list, index));
lil_free_list(list);
return r;
}
@@ -2295,7 +2379,7 @@ static struct lil_value *fnc_indexof(struct lil *lil, size_t argc,

list = lil_subst_to_list(lil, argv[0]);
for (index = 0; index < list->c; index++) {
- if (!strcmp(lil_to_string(list->v[index]),
+ if (!strcmp(lil_to_string(lil_list_get(list, index)),
lil_to_string(argv[1]))) {
r = lil_alloc_integer(index);
break;
@@ -2364,7 +2448,7 @@ static struct lil_value *fnc_slice(struct lil *lil, size_t argc,

slice = lil_alloc_list();
for (i = (size_t)from; i < (size_t)to; i++)
- lil_list_append(slice, lil_clone_value(list->v[i]));
+ lil_list_append(slice, lil_clone_value(lil_list_get(list, i)));
lil_free_list(list);

r = lil_list_to_value(slice, 1);
@@ -2395,10 +2479,12 @@ static struct lil_value *fnc_filter(struct lil *lil, size_t argc,
list = lil_subst_to_list(lil, argv[base]);
filtered = lil_alloc_list();
for (i = 0; i < list->c && !lil->env->breakrun; i++) {
- lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY);
+ lil_set_var(lil, varname, lil_list_get(list, i),
+ LIL_SETVAR_LOCAL_ONLY);
r = lil_eval_expr(lil, argv[base + 1]);
if (lil_to_boolean(r))
- lil_list_append(filtered, lil_clone_value(list->v[i]));
+ lil_list_append(filtered,
+ lil_clone_value(lil_list_get(list, i)));
lil_free_value(r);
}
lil_free_list(list);
@@ -2475,7 +2561,8 @@ static struct lil_value *fnc_foreach(struct lil *lil, size_t argc,
for (i = 0; i < list->c; i++) {
struct lil_value *rv;

- lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY);
+ lil_set_var(lil, varname, lil_list_get(list, i),
+ LIL_SETVAR_LOCAL_ONLY);
rv = lil_parse_value(lil, argv[codeidx], 0);
if (rv->l)
lil_list_append(rlist, rv);
diff --git a/include/cli_lil.h b/include/cli_lil.h
index 91e79c12f4..40c822401e 100644
--- a/include/cli_lil.h
+++ b/include/cli_lil.h
@@ -149,13 +149,13 @@ struct lil_value *lil_alloc_integer(ssize_t num);
void lil_free_value(struct lil_value *val);

struct lil_value *lil_clone_value(struct lil_value *src);
-int lil_append_char(struct lil_value *val, char ch);
-int lil_append_string(struct lil_value *val, const char *s);
-int lil_append_val(struct lil_value *val, struct lil_value *v);
+enum lil_error lil_append_char(struct lil_value *val, char ch);
+enum lil_error lil_append_string(struct lil_value *val, const char *s);
+enum lil_error lil_append_val(struct lil_value *val, struct lil_value *v);

struct lil_list *lil_alloc_list(void);
void lil_free_list(struct lil_list *list);
-void lil_list_append(struct lil_list *list, struct lil_value *val);
+int lil_list_append(struct lil_list *list, void *item);
size_t lil_list_size(struct lil_list *list);
struct lil_value *lil_list_get(struct lil_list *list, size_t index);
struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape);
--
2.32.0