From 329f60420465161dcb2b8dfd85211129cb20ff4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 18 May 2017 18:01:57 +0200 Subject: [PATCH] Split native functions from non-native --- ell.c | 218 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 115 insertions(+), 103 deletions(-) diff --git a/ell.c b/ell.c index 59f59ac..cb30ebf 100755 --- a/ell.c +++ b/ell.c @@ -643,6 +643,38 @@ parse (const char *s, size_t len, char **e) { // --- Runtime ----------------------------------------------------------------- +struct context; +typedef bool (*handler_fn) (struct context *); + +struct native_fn { + struct native_fn *next; ///< The next link in the chain + handler_fn handler; ///< Internal C handler, or NULL + char name[]; ///< The name of the function +}; + +struct native_fn *g_native; ///< Maps words to functions + +static bool +register_native (const char *name, handler_fn handler) { + struct native_fn *fn = NULL; + for (fn = g_native; fn; fn = fn->next) + if (!strcmp (fn->name, name)) + break; + + if (!fn) { + if (!(fn = calloc (1, sizeof *fn + strlen (name) + 1))) + return false; + strcpy (fn->name, name); + fn->next = g_native; + g_native = fn; + } + + fn->handler = handler; + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + struct context { struct item *variables; ///< List of variables @@ -653,19 +685,6 @@ struct context { void *user_data; ///< User data }; -/// Internal handler for a function -typedef bool (*handler_fn) (struct context *); - -struct fn { - struct fn *next; ///< The next link in the chain - - handler_fn handler; ///< Internal C handler, or NULL - struct item *script; ///< Alternatively runtime code - char name[]; ///< The name of the function -}; - -struct fn *g_functions; ///< Maps words to functions - static void context_init (struct context *ctx) { memset (ctx, 0, sizeof *ctx); @@ -691,20 +710,46 @@ set_error (struct context *ctx, const char *format, ...) { return false; } -static bool execute (struct context *, struct item *); +static struct item * +var (struct context *ctx, const char *name) { + for (struct item *iter = ctx->variables; iter; iter = iter->next) + if (!strcmp (iter->head->value, name)) + return iter->head->next; + return NULL; +} + +static void +set (struct context *ctx, const char *name, struct item *value) { + for (struct item *iter = ctx->variables; iter; iter = iter->next) + if (!strcmp (iter->head->value, name)) { + item_free (iter->head->next); + iter->head->next = value; + return; + } + struct item *key = new_string (name, strlen (name)); + key->next = value; + struct item *pair = new_list (key); + pair->next = ctx->variables; + ctx->variables = pair; +} + +static struct item *execute (struct context *, struct item *); static bool call_function (struct context *ctx, const char *name) { - struct fn *iter; - for (iter = g_functions; iter; iter = iter->next) - if (!strcmp (name, iter->name)) - goto found; - return set_error (ctx, "unknown function: %s", name); - -found: - if (iter->handler - ? iter->handler (ctx) - : execute (ctx, iter->script)) + struct item *body = var (ctx, name); + if (!body) { + struct native_fn *fn; + for (fn = g_native; fn; fn = fn->next) + if (!strcmp (name, fn->name)) + break; + if (!fn) + return set_error (ctx, "unknown function: %s", name); + if (fn->handler (ctx)) + return true; + } else if (body->type == ITEM_STRING) { + return set_error (ctx, "strings aren't callable: %s", name); + } else if (execute (ctx, body)) return true; // In this case, `error' is NULL @@ -719,64 +764,38 @@ found: return false; } -static void -free_function (struct fn *fn) { - item_free_list (fn->script); - free (fn); -} +static struct item *execute (struct context *ctx, struct item *script); -static void -unregister_function (const char *name) { - for (struct fn **iter = &g_functions; *iter; iter = &(*iter)->next) - if (!strcmp ((*iter)->name, name)) { - struct fn *tmp = *iter; - *iter = tmp->next; - free_function (tmp); - break; - } -} - -static struct fn * -prepend_new_fn (const char *name) { - struct fn *fn = calloc (1, sizeof *fn + strlen (name) + 1); - if (!fn) +static struct item * +execute_one (struct context *ctx, struct item *statement) { + if (!statement->head) return NULL; - strcpy (fn->name, name); - fn->next = g_functions; - return g_functions = fn; -} - -static bool -register_handler (const char *name, handler_fn handler) { - unregister_function (name); - struct fn *fn = prepend_new_fn (name); - if (!fn) - return false; - fn->handler = handler; - return true; -} - -static bool -register_script (const char *name, struct item *script) { - unregister_function (name); - struct fn *fn = prepend_new_fn (name); - if (!fn) - return false; - fn->script = script; - return true; -} - -static bool -execute (struct context *ctx, struct item *script) { - for (; script; script = script->next) { - // TODO: this should be a list - // -> if the first item is a STRING, resolve it - // -> but handle special forms - // -> assign the rest of the items to variables - // -> recurse + struct item *fn = statement->head->head; + if (statement->head->type == ITEM_STRING) { + if (!strcmp (statement->head->value, "quote")) { + return statement->head->next; + } else if (!strcmp (statement->head->value, "arg")) { + // TODO: rename \d+ variables to arguments + } else { + // TODO: resolve the string + fn = NULL; + } } - return true; + // TODO: assign the rest of items to variables + return execute (ctx, fn); +} + +// Execute a block and return whatever the last statement returned +static struct item * +execute (struct context *ctx, struct item *script) { + struct item *result = NULL; + for (; script; script = script->next) { + assert (script->type == ITEM_LIST); + item_free_list (result); + result = execute_one (ctx, script); + } + return result; } // --- Runtime library --------------------------------------------------------- @@ -784,39 +803,33 @@ execute (struct context *ctx, struct item *script) { #define defn(name) static bool name (struct context *ctx) static bool -init_runtime_library_scripts (void) { +init_runtime_library_scripts (struct context *ctx) { bool ok = true; // It's much cheaper (and more fun) to define functions in terms of other // ones. The "unit tests" serve a secondary purpose of showing the usage. - struct script { + struct { const char *name; ///< Name of the function const char *definition; ///< The defining script - } scripts[] = { + } functions[] = { { "greet", "arg _name\n" "print (.. 'hello ' (.. @_name))" }, }; - for (size_t i = 0; i < N_ELEMENTS (scripts); i++) { + for (size_t i = 0; i < N_ELEMENTS (functions); i++) { char *e = NULL; - struct item *script = parse (scripts[i].definition, - strlen (scripts[i].definition), &e); + struct item *body = parse (functions[i].definition, + strlen (functions[i].definition), &e); if (e) { - printf ("error parsing internal script `%s': %s\n", - scripts[i].definition, e); + printf ("error parsing internal function `%s': %s\n", + functions[i].definition, e); free (e); ok = false; } else - ok &= register_script (scripts[i].name, script); + set (ctx, functions[i].name, body); } return ok; } -static struct item * -var (struct context *ctx, const char *name) { - // TODO: go through the "ctx->variables" list of lists and look for "name" - return NULL; -} - defn (fn_print) { struct buffer buf = BUFFER_INITIALIZER; struct item *item = var (ctx, "1"); @@ -840,17 +853,16 @@ defn (fn_concatenate) { static bool init_runtime_library (void) { - return register_handler ("..", fn_concatenate) - && register_handler ("print", fn_print) - && init_runtime_library_scripts (); + return register_native ("..", fn_concatenate) + && register_native ("print", fn_print); } static void free_runtime_library (void) { - struct fn *next, *iter; - for (iter = g_functions; iter; iter = next) { + struct native_fn *next, *iter; + for (iter = g_native; iter; iter = next) { next = iter->next; - free_function (iter); + free (iter); } } @@ -858,9 +870,6 @@ free_runtime_library (void) { int main (int argc, char *argv[]) { - if (!init_runtime_library ()) - printf ("%s\n", "runtime library initialization failed"); - // TODO: load the entirety of stdin const char *program = "print 'hello world\\n'"; @@ -874,8 +883,11 @@ main (int argc, char *argv[]) { struct context ctx; context_init (&ctx); + if (!init_runtime_library () + || !init_runtime_library_scripts (&ctx)) + printf ("%s\n", "runtime library initialization failed"); ctx.user_data = NULL; - execute (&ctx, tree); + item_free_list (execute (&ctx, tree)); item_free_list (tree); const char *failure = NULL;