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