General cleanup
Memory allocation errors seem to be handled now.
This commit is contained in:
parent
3c86249f11
commit
23da396614
193
ell.c
193
ell.c
|
@ -160,8 +160,8 @@ new_clone (const struct item *item) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
memcpy (clone, item, size);
|
memcpy (clone, item, size);
|
||||||
if (item->type == ITEM_LIST) {
|
if (item->type == ITEM_LIST && clone->head) {
|
||||||
if (clone->head && !(clone->head = new_clone_list (clone->head))) {
|
if (!(clone->head = new_clone_list (clone->head))) {
|
||||||
free (clone);
|
free (clone);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -475,8 +475,7 @@ parser_peek (struct parser *self, jmp_buf out) {
|
||||||
!(self->error = lexer_errorf (&self->lexer, "%s", e));
|
!(self->error = lexer_errorf (&self->lexer, "%s", e));
|
||||||
longjmp (out, 1);
|
longjmp (out, 1);
|
||||||
}
|
}
|
||||||
if (self->token == T_STRING
|
if (self->token == T_STRING && self->lexer.string.memory_failure)
|
||||||
&& (self->memory_failure = self->lexer.string.memory_failure))
|
|
||||||
longjmp (out, 1);
|
longjmp (out, 1);
|
||||||
self->replace_token = false;
|
self->replace_token = false;
|
||||||
}
|
}
|
||||||
|
@ -615,11 +614,9 @@ parser_run (struct parser *self, const char **e) {
|
||||||
struct item *volatile result = NULL, *volatile *tail = &result;
|
struct item *volatile result = NULL, *volatile *tail = &result;
|
||||||
if (setjmp (err)) {
|
if (setjmp (err)) {
|
||||||
item_free_list (result);
|
item_free_list (result);
|
||||||
if (e) {
|
*e = self->error;
|
||||||
*e = self->error;
|
if (self->memory_failure || self->lexer.string.memory_failure)
|
||||||
if (self->memory_failure)
|
*e = "memory allocation failure";
|
||||||
*e = "memory allocation failure";
|
|
||||||
}
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,9 +664,6 @@ native_register (const char *name, handler_fn handler) {
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
// TODO: fill in "error_is_fatal"
|
|
||||||
// TODO: probably add new_*() methods that set "memory_failure"
|
|
||||||
|
|
||||||
struct context {
|
struct context {
|
||||||
struct item *variables; ///< List of variables
|
struct item *variables; ///< List of variables
|
||||||
|
|
||||||
|
@ -691,6 +685,11 @@ context_free (struct context *ctx) {
|
||||||
free (ctx->error);
|
free (ctx->error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check (struct context *ctx, struct item *item) {
|
||||||
|
return !(ctx->memory_failure |= !item);
|
||||||
|
}
|
||||||
|
|
||||||
static struct item *
|
static struct item *
|
||||||
get (struct context *ctx, const char *name) {
|
get (struct context *ctx, const char *name) {
|
||||||
for (struct item *iter = ctx->variables; iter; iter = iter->next)
|
for (struct item *iter = ctx->variables; iter; iter = iter->next)
|
||||||
|
@ -701,49 +700,36 @@ get (struct context *ctx, const char *name) {
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
set (struct context *ctx, const char *name, struct item *value) {
|
set (struct context *ctx, const char *name, struct item *value) {
|
||||||
struct item *iter, *key = NULL, *pair = NULL;
|
struct item *iter, *key, *pair;
|
||||||
for (iter = ctx->variables; iter; iter = iter->next)
|
for (iter = ctx->variables; iter; iter = iter->next)
|
||||||
if (!strcmp (iter->head->value, name))
|
if (!strcmp (iter->head->value, name))
|
||||||
break;
|
break;
|
||||||
if (iter) {
|
if (iter) {
|
||||||
item_free (iter->head->next);
|
item_free (iter->head->next);
|
||||||
if (!(iter->head->next = new_clone (value))) {
|
return check (ctx, (iter->head->next = new_clone (value)));
|
||||||
ctx->memory_failure = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if ((key = new_string (name, strlen (name)))
|
if (!check (ctx, (key = new_string (name, strlen (name))))
|
||||||
&& (pair = new_list (NULL))) {
|
|| !check (ctx, (pair = new_list (key))))
|
||||||
if (!((pair->head = key)->next = new_clone (value))) {
|
return false;
|
||||||
item_free (pair);
|
if (!check (ctx, (key->next = new_clone (value)))) {
|
||||||
ctx->memory_failure = true;
|
item_free (pair);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pair->next = ctx->variables;
|
|
||||||
ctx->variables = pair;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
item_free_list (key);
|
|
||||||
item_free_list (pair);
|
|
||||||
ctx->memory_failure = true;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
pair->next = ctx->variables;
|
||||||
|
ctx->variables = pair;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
set_error (struct context *ctx, const char *format, ...) {
|
set_error (struct context *ctx, const char *format, ...) {
|
||||||
free (ctx->error);
|
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start (ap, format);
|
va_start (ap, format);
|
||||||
ctx->error = vformat (format, ap);
|
free (ctx->error);
|
||||||
va_end (ap);
|
if (!(ctx->error = vformat (format, ap)))
|
||||||
|
|
||||||
if (!ctx->error)
|
|
||||||
ctx->memory_failure = true;
|
ctx->memory_failure = true;
|
||||||
|
va_end (ap);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,7 +746,7 @@ rename_arguments (struct context *ctx, struct item *names) {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (names->type != ITEM_STRING)
|
if (names->type != ITEM_STRING)
|
||||||
continue;
|
return set_error (ctx, "argument names must be strings");
|
||||||
if (!set (ctx, names->value, value))
|
if (!set (ctx, names->value, value))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -771,44 +757,33 @@ static bool execute_statement (struct context *, struct item *, struct item **);
|
||||||
static bool execute (struct context *ctx, struct item *body, struct item **);
|
static bool execute (struct context *ctx, struct item *body, struct item **);
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
execute_args_list (struct context *ctx, struct item *args, struct item **res) {
|
execute_args (struct context *ctx, struct item *args, struct item **res) {
|
||||||
for (struct item *arg = args; arg; arg = arg->next) {
|
for (; args; args = args->next) {
|
||||||
struct item *evaluated = NULL;
|
struct item *evaluated = NULL;
|
||||||
if (!execute_statement (ctx, arg, &evaluated))
|
if (!execute_statement (ctx, args, &evaluated))
|
||||||
return false;
|
return false;
|
||||||
if (evaluated) {
|
if (evaluated) {
|
||||||
item_free_list (evaluated->next);
|
item_free_list (evaluated->next);
|
||||||
evaluated->next = NULL;
|
evaluated->next = NULL;
|
||||||
*res = evaluated;
|
res = &(*res = evaluated)->next;
|
||||||
res = &evaluated->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
execute_native (struct context *ctx,
|
|
||||||
struct native_fn *fn, struct item *next, struct item **res) {
|
|
||||||
struct item *args = NULL;
|
|
||||||
bool ok = execute_args_list (ctx, next, &args)
|
|
||||||
&& fn->handler (ctx, args, res);
|
|
||||||
item_free_list (args);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we should probably maintain arguments in a separate list,
|
// TODO: we should probably maintain arguments in a separate list,
|
||||||
// either that or at least remember the count so that we can reset them
|
// either that or at least remember the count so that we can reset them
|
||||||
static bool
|
static bool
|
||||||
execute_args (struct context *ctx, struct item *next) {
|
execute_args_and_set (struct context *ctx, struct item *following) {
|
||||||
struct item *args = NULL;
|
struct item *args = NULL;
|
||||||
if (!execute_args_list (ctx, next, &args)) {
|
if (!execute_args (ctx, following, &args)) {
|
||||||
item_free_list (args);
|
item_free_list (args);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char buf[64];
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (struct item *arg = args; arg; arg = arg->next) {
|
for (struct item *arg = args; arg; arg = arg->next) {
|
||||||
|
char buf[64];
|
||||||
(void) snprintf (buf, sizeof buf, "%zu", i++);
|
(void) snprintf (buf, sizeof buf, "%zu", i++);
|
||||||
if (!set (ctx, buf, arg))
|
if (!set (ctx, buf, arg))
|
||||||
return false;
|
return false;
|
||||||
|
@ -817,15 +792,21 @@ execute_args (struct context *ctx, struct item *next) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
execute_native (struct context *ctx,
|
||||||
|
struct native_fn *fn, struct item *next, struct item **res) {
|
||||||
|
struct item *args = NULL;
|
||||||
|
bool ok = execute_args (ctx, next, &args)
|
||||||
|
&& fn->handler (ctx, args, res);
|
||||||
|
item_free_list (args);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
execute_statement
|
execute_statement
|
||||||
(struct context *ctx, struct item *statement, struct item **result) {
|
(struct context *ctx, struct item *statement, struct item **result) {
|
||||||
if (statement->type == ITEM_STRING) {
|
if (statement->type == ITEM_STRING)
|
||||||
if ((*result = new_clone (statement)))
|
return check (ctx, (*result = new_clone (statement)));
|
||||||
return true;
|
|
||||||
ctx->memory_failure = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: should this ever happen and what are the consequences?
|
// XXX: should this ever happen and what are the consequences?
|
||||||
// Shouldn't we rather clone the empty list?
|
// Shouldn't we rather clone the empty list?
|
||||||
|
@ -838,12 +819,10 @@ execute_statement
|
||||||
if (body->type == ITEM_STRING) {
|
if (body->type == ITEM_STRING) {
|
||||||
name = body->value;
|
name = body->value;
|
||||||
// TODO: these could be just regular handlers, only top priority
|
// TODO: these could be just regular handlers, only top priority
|
||||||
if (!strcmp (name, "quote")) {
|
// TODO: these should also get a stack trace the normal way
|
||||||
if (!following || (*result = new_clone_list (following)))
|
if (!strcmp (name, "quote"))
|
||||||
return true;
|
return !following
|
||||||
ctx->memory_failure = true;
|
|| check (ctx, (*result = new_clone_list (following)));
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!strcmp (name, "arg"))
|
if (!strcmp (name, "arg"))
|
||||||
return rename_arguments (ctx, following);
|
return rename_arguments (ctx, following);
|
||||||
body = get (ctx, name);
|
body = get (ctx, name);
|
||||||
|
@ -857,23 +836,21 @@ execute_statement
|
||||||
return true;
|
return true;
|
||||||
} else if (body->type == ITEM_STRING) {
|
} else if (body->type == ITEM_STRING) {
|
||||||
// Recursion could be pretty fatal, let's not do that
|
// Recursion could be pretty fatal, let's not do that
|
||||||
if ((*result = new_clone (body)))
|
if (check (ctx, (*result = new_clone (body))))
|
||||||
return true;
|
return true;
|
||||||
ctx->memory_failure = true;
|
|
||||||
} else {
|
} else {
|
||||||
if (execute_args (ctx, following)
|
if (execute_args_and_set (ctx, following)
|
||||||
&& execute (ctx, body->head, result))
|
&& execute (ctx, body->head, result))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this case, `error' is NULL
|
// In that case, `error' is NULL and there's nothing else to do anyway
|
||||||
if (ctx->memory_failure)
|
if (!ctx->memory_failure) {
|
||||||
return false;
|
// This creates some form of a stack trace
|
||||||
|
char *tmp = ctx->error;
|
||||||
// This creates some form of a stack trace
|
set_error (ctx, "%s -> %s", name, tmp);
|
||||||
char *tmp = ctx->error;
|
free (tmp);
|
||||||
set_error (ctx, "%s -> %s", name, tmp);
|
}
|
||||||
free (tmp);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -925,56 +902,59 @@ init_runtime_library_scripts (struct context *ctx) {
|
||||||
defn (fn_set) {
|
defn (fn_set) {
|
||||||
struct item *name = args;
|
struct item *name = args;
|
||||||
if (!name || name->type != ITEM_STRING)
|
if (!name || name->type != ITEM_STRING)
|
||||||
return (void *) set_error (ctx, "first argument must be string");
|
return set_error (ctx, "first argument must be string");
|
||||||
|
|
||||||
struct item *value;
|
struct item *value;
|
||||||
if ((value = name->next))
|
if ((value = name->next))
|
||||||
return set (ctx, name->value, value);
|
return set (ctx, name->value, value);
|
||||||
|
|
||||||
// FIXME: how do we represent a nil value here?
|
// FIXME: how do we represent a nil value here?
|
||||||
*result = new_clone (get (ctx, name->value));
|
return check (ctx, (*result = new_clone (get (ctx, name->value))));
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
defn (fn_list) {
|
||||||
|
struct item *values = NULL;
|
||||||
|
if (args && !check (ctx, (values = new_clone_list (args))))
|
||||||
|
return false;
|
||||||
|
return check (ctx, (*result = new_list (values)));
|
||||||
}
|
}
|
||||||
|
|
||||||
defn (fn_print) {
|
defn (fn_print) {
|
||||||
(void) result;
|
(void) result;
|
||||||
|
for (; args; args = args->next) {
|
||||||
// TODO: error on list
|
if (args->type != ITEM_STRING)
|
||||||
struct buffer buf = BUFFER_INITIALIZER;
|
// TODO: print lists as their parsable representation
|
||||||
for (; args; args = args->next)
|
return set_error (ctx, "cannot print lists");
|
||||||
buffer_append (&buf, args->value, args->len);
|
if (fwrite (args->value, 1, args->len, stdout) != args->len)
|
||||||
buffer_append_c (&buf, '\0');
|
return set_error (ctx, "write failed: %s", strerror (errno));
|
||||||
if (buf.memory_failure) {
|
|
||||||
ctx->memory_failure = true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf ("%s", buf.s);
|
|
||||||
free (buf.s);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
defn (fn_concatenate) {
|
defn (fn_concatenate) {
|
||||||
// TODO: error on list
|
|
||||||
struct buffer buf = BUFFER_INITIALIZER;
|
struct buffer buf = BUFFER_INITIALIZER;
|
||||||
for (; args; args = args->next)
|
for (; args; args = args->next) {
|
||||||
|
if (args->type != ITEM_STRING) {
|
||||||
|
free (buf.s);
|
||||||
|
return set_error (ctx, "cannot concatenate lists");
|
||||||
|
}
|
||||||
buffer_append (&buf, args->value, args->len);
|
buffer_append (&buf, args->value, args->len);
|
||||||
buffer_append_c (&buf, '\0');
|
|
||||||
if (buf.memory_failure) {
|
|
||||||
ctx->memory_failure = true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
*result = new_string (buf.s, buf.len);
|
buffer_append_c (&buf, '\0');
|
||||||
|
|
||||||
|
bool ok = !(ctx->memory_failure = buf.memory_failure)
|
||||||
|
&& check (ctx, (*result = new_string (buf.s, buf.len)));
|
||||||
free (buf.s);
|
free (buf.s);
|
||||||
return true;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
init_runtime_library (void)
|
init_runtime_library (void)
|
||||||
{
|
{
|
||||||
return native_register ("..", fn_concatenate)
|
return native_register ("set", fn_set)
|
||||||
&& native_register ("set", fn_set)
|
&& native_register ("list", fn_list)
|
||||||
&& native_register ("print", fn_print);
|
&& native_register ("print", fn_print)
|
||||||
|
&& native_register ("..", fn_concatenate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1008,7 +988,6 @@ main (int argc, char *argv[]) {
|
||||||
const char *e = NULL;
|
const char *e = NULL;
|
||||||
struct item *program = parser_run (&parser, &e);
|
struct item *program = parser_run (&parser, &e);
|
||||||
free (buf.s);
|
free (buf.s);
|
||||||
|
|
||||||
if (e) {
|
if (e) {
|
||||||
printf ("%s: %s\n", "parse error", e);
|
printf ("%s: %s\n", "parse error", e);
|
||||||
return 1;
|
return 1;
|
||||||
|
|
Loading…
Reference in New Issue