Split native functions from non-native

This commit is contained in:
Přemysl Eric Janouch 2017-05-18 18:01:57 +02:00
parent 44b97f61c1
commit 329f604204
Signed by: p
GPG Key ID: B715679E3A361BE6
1 changed files with 115 additions and 103 deletions

218
ell.c
View File

@ -643,6 +643,38 @@ parse (const char *s, size_t len, char **e) {
// --- Runtime ----------------------------------------------------------------- // --- 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 context {
struct item *variables; ///< List of variables struct item *variables; ///< List of variables
@ -653,19 +685,6 @@ struct context {
void *user_data; ///< User data 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 static void
context_init (struct context *ctx) { context_init (struct context *ctx) {
memset (ctx, 0, sizeof *ctx); memset (ctx, 0, sizeof *ctx);
@ -691,20 +710,46 @@ set_error (struct context *ctx, const char *format, ...) {
return false; 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 static bool
call_function (struct context *ctx, const char *name) { call_function (struct context *ctx, const char *name) {
struct fn *iter; struct item *body = var (ctx, name);
for (iter = g_functions; iter; iter = iter->next) if (!body) {
if (!strcmp (name, iter->name)) struct native_fn *fn;
goto found; for (fn = g_native; fn; fn = fn->next)
return set_error (ctx, "unknown function: %s", name); if (!strcmp (name, fn->name))
break;
found: if (!fn)
if (iter->handler return set_error (ctx, "unknown function: %s", name);
? iter->handler (ctx) if (fn->handler (ctx))
: execute (ctx, iter->script)) 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; return true;
// In this case, `error' is NULL // In this case, `error' is NULL
@ -719,64 +764,38 @@ found:
return false; return false;
} }
static void static struct item *execute (struct context *ctx, struct item *script);
free_function (struct fn *fn) {
item_free_list (fn->script);
free (fn);
}
static void static struct item *
unregister_function (const char *name) { execute_one (struct context *ctx, struct item *statement) {
for (struct fn **iter = &g_functions; *iter; iter = &(*iter)->next) if (!statement->head)
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)
return NULL; return NULL;
strcpy (fn->name, name); struct item *fn = statement->head->head;
fn->next = g_functions; if (statement->head->type == ITEM_STRING) {
return g_functions = fn; if (!strcmp (statement->head->value, "quote")) {
} return statement->head->next;
} else if (!strcmp (statement->head->value, "arg")) {
static bool // TODO: rename \d+ variables to arguments
register_handler (const char *name, handler_fn handler) { } else {
unregister_function (name); // TODO: resolve the string
struct fn *fn = prepend_new_fn (name); fn = NULL;
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
} }
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 --------------------------------------------------------- // --- Runtime library ---------------------------------------------------------
@ -784,39 +803,33 @@ execute (struct context *ctx, struct item *script) {
#define defn(name) static bool name (struct context *ctx) #define defn(name) static bool name (struct context *ctx)
static bool static bool
init_runtime_library_scripts (void) { init_runtime_library_scripts (struct context *ctx) {
bool ok = true; bool ok = true;
// It's much cheaper (and more fun) to define functions in terms of other // 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. // ones. The "unit tests" serve a secondary purpose of showing the usage.
struct script { struct {
const char *name; ///< Name of the function const char *name; ///< Name of the function
const char *definition; ///< The defining script const char *definition; ///< The defining script
} scripts[] = { } functions[] = {
{ "greet", "arg _name\n" "print (.. 'hello ' (.. @_name))" }, { "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; char *e = NULL;
struct item *script = parse (scripts[i].definition, struct item *body = parse (functions[i].definition,
strlen (scripts[i].definition), &e); strlen (functions[i].definition), &e);
if (e) { if (e) {
printf ("error parsing internal script `%s': %s\n", printf ("error parsing internal function `%s': %s\n",
scripts[i].definition, e); functions[i].definition, e);
free (e); free (e);
ok = false; ok = false;
} else } else
ok &= register_script (scripts[i].name, script); set (ctx, functions[i].name, body);
} }
return ok; 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) { defn (fn_print) {
struct buffer buf = BUFFER_INITIALIZER; struct buffer buf = BUFFER_INITIALIZER;
struct item *item = var (ctx, "1"); struct item *item = var (ctx, "1");
@ -840,17 +853,16 @@ defn (fn_concatenate) {
static bool static bool
init_runtime_library (void) init_runtime_library (void)
{ {
return register_handler ("..", fn_concatenate) return register_native ("..", fn_concatenate)
&& register_handler ("print", fn_print) && register_native ("print", fn_print);
&& init_runtime_library_scripts ();
} }
static void static void
free_runtime_library (void) { free_runtime_library (void) {
struct fn *next, *iter; struct native_fn *next, *iter;
for (iter = g_functions; iter; iter = next) { for (iter = g_native; iter; iter = next) {
next = iter->next; next = iter->next;
free_function (iter); free (iter);
} }
} }
@ -858,9 +870,6 @@ free_runtime_library (void) {
int int
main (int argc, char *argv[]) { main (int argc, char *argv[]) {
if (!init_runtime_library ())
printf ("%s\n", "runtime library initialization failed");
// TODO: load the entirety of stdin // TODO: load the entirety of stdin
const char *program = "print 'hello world\\n'"; const char *program = "print 'hello world\\n'";
@ -874,8 +883,11 @@ main (int argc, char *argv[]) {
struct context ctx; struct context ctx;
context_init (&ctx); context_init (&ctx);
if (!init_runtime_library ()
|| !init_runtime_library_scripts (&ctx))
printf ("%s\n", "runtime library initialization failed");
ctx.user_data = NULL; ctx.user_data = NULL;
execute (&ctx, tree); item_free_list (execute (&ctx, tree));
item_free_list (tree); item_free_list (tree);
const char *failure = NULL; const char *failure = NULL;