Implement if, for, map, filter

This commit is contained in:
Přemysl Eric Janouch 2017-05-20 21:24:11 +02:00
parent f0337aa481
commit d2155031d0
Signed by: p
GPG Key ID: B715679E3A361BE6
2 changed files with 187 additions and 39 deletions

View File

@ -10,6 +10,8 @@ reasonably comfortable to use.
This package is an implementation of said language, meant to be self-contained,
portable and reusable. Performance is specifically not an intent.
The project is currently in a "working proof of concept" stage.
Syntax
------
Owing to its Scheme heritage, 'ell' is homoiconic, that is a program can be
@ -70,18 +72,38 @@ from the outer scope.
Standard library
----------------
`if <cond> <body> [elif <cond> <body>]... [else <body>]`
`set <name> [<value>]`
Conditional evaluation, strings evaluate to themselves.
`funargs`
Returns arguments to the current evaluation context as a list.
Retrieves or sets a named variable.
`list`
Returns a list of parameters. The syntax sugar for lists is `[]`.
`if <cond> <body> [elif <cond> <body>]... [else <body>]`
Conditional evaluation, strings evaluate to themselves.
`for <list> <body>`
Run the body for each element.
`map <list> <body>`
Transform each element with the given function.
`filter <list> <body>`
Return a new list consisting of matching elements only.
`.. [<string>]...`
Concatenates strings.
`print [<item>]...`
Prints all items in sequence--strings directly, lists as source code.
Contributing and Support
------------------------
Use this project's GitHub to report any bugs, request features, or submit pull

192
ell.c
View File

@ -26,7 +26,6 @@
#include <assert.h>
#include <time.h>
#include <stdbool.h>
#include <math.h>
#include <setjmp.h>
#if defined __GNUC__
@ -768,7 +767,7 @@ execute_args (struct context *ctx, struct item *args, struct item **res) {
// 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_and_set (struct context *ctx, struct item *following) {
execute_and_set_args (struct context *ctx, struct item *following) {
struct item *args = NULL;
if (!execute_args (ctx, following, &args)) {
item_free_list (args);
@ -833,7 +832,8 @@ execute_statement
if (check (ctx, (*result = new_clone (body))))
return true;
} else {
if (execute_args_and_set (ctx, following)
// FIXME: this creates a confusing backtrace for argument evaluation
if (execute_and_set_args (ctx, following)
&& execute (ctx, body->head, result))
return true;
}
@ -866,31 +866,9 @@ execute (struct context *ctx, struct item *body, struct item **result) {
(struct context *ctx, struct item *args, struct item **result)
static bool
init_runtime_library_scripts (struct context *ctx) {
bool ok = true;
struct {
const char *name; ///< Name of the function
const char *definition; ///< The defining script
} functions[] = {
// TODO: try to think of something useful
};
for (size_t i = 0; i < N_ELEMENTS (functions); i++) {
struct parser parser;
parser_init (&parser,
functions[i].definition, strlen (functions[i].definition));
const char *e = NULL;
struct item *body = parser_run (&parser, &e);
if (e) {
printf ("error parsing internal function `%s': %s\n",
functions[i].name, e);
ok = false;
} else
ok &= set (ctx, functions[i].name, body);
parser_free (&parser);
}
return ok;
truthy (struct item *item) {
return item
&& ((item->type == ITEM_STRING && item->len != 0) || item->head);
}
defn (fn_set) {
@ -915,6 +893,114 @@ defn (fn_list) {
return check (ctx, (*result = new_list (values)));
}
defn (fn_if) {
struct item *cond, *body, *keyword;
for (cond = args; ; cond = keyword->next) {
if (!cond)
return set_error (ctx, "missing condition");
if (!(body = cond->next))
return set_error (ctx, "missing body");
struct item *res = NULL;
if (!execute_statement (ctx, cond, &res))
return false;
bool match = truthy (res);
item_free_list (res);
if (match)
return execute_statement (ctx, body, result);
if (!(keyword = body->next))
break;
if (keyword->type != ITEM_STRING)
return set_error (ctx, "expected keyword, got list");
if (!strcmp (keyword->value, "else")) {
if (!(body = keyword->next))
return set_error (ctx, "missing body");
return execute_statement (ctx, body, result);
}
if (strcmp (keyword->value, "elif"))
return set_error (ctx, "invalid keyword: %s", keyword->value);
}
return true;
}
// TODO: how to break out of the loop? Catchable error? Special value?
defn (fn_for) {
struct item *list = args, *body;
if (!list || list->type != ITEM_LIST)
return set_error (ctx, "first argument must be a list");
if (!(body = list->next) || body->type != ITEM_LIST)
return set_error (ctx, "second argument must be a function");
(void) result;
for (struct item *iter = list->head; iter; iter = iter->next) {
struct item *copy;
if (!check (ctx, (copy = new_clone (iter))))
return false;
struct item *res = NULL;
// FIXME: wrong thing is executed, see fn_map
bool ok = execute_statement (ctx, body, &res);
item_free_list (res);
if (!ok)
return false;
}
return true;
}
defn (fn_map) {
struct item *body = args, *values;
if (!body || body->type != ITEM_LIST)
return set_error (ctx, "first argument must be a function");
if (!(values = body->next) || values->type != ITEM_LIST)
return set_error (ctx, "second argument must be a list");
struct item *res = NULL, **out = &res;
for (struct item *v = values->head; v; v = v->next) {
// FIXME: wrong thing is executed
// -> either temporarily append the value to the body
// -> or modify execute_statement()
if (!execute_statement (ctx, v, out)) {
item_free_list (res);
return false;
}
while (*out && (*out)->next)
out = &(*out)->next;
}
return check (ctx, (*result = new_list (res)));
}
defn (fn_filter) {
struct item *body = args, *values;
if (!body || body->type != ITEM_LIST)
return set_error (ctx, "first argument must be a function");
if (!(values = body->next) || values->type != ITEM_LIST)
return set_error (ctx, "second argument must be a list");
struct item *res = NULL, **out = &res;
for (struct item *v = values->head; v; v = v->next) {
struct item *res = NULL;
// FIXME: wrong thing is executed, see fn_map
if (!execute_statement (ctx, body, &res)) {
item_free_list (res);
return false;
}
bool match = truthy (res);
item_free_list (res);
if (!match)
continue;
struct item *copy;
if (!check (ctx, (copy = new_clone (v)))) {
item_free_list (res);
return false;
}
out = &(*out = copy)->next;
}
return check (ctx, (*result = new_list (res)));
}
defn (fn_print) {
(void) result;
for (; args; args = args->next) {
@ -944,17 +1030,23 @@ defn (fn_concatenate) {
return ok;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static bool
init_runtime_library (void)
init_native_library (void)
{
return native_register ("set", fn_set)
&& native_register ("list", fn_list)
&& native_register ("if", fn_if)
&& native_register ("for", fn_for)
&& native_register ("map", fn_map)
&& native_register ("filter", fn_filter)
&& native_register ("print", fn_print)
&& native_register ("..", fn_concatenate);
}
static void
free_runtime_library (void) {
free_native_library (void) {
struct native_fn *next, *iter;
for (iter = g_native; iter; iter = next) {
next = iter->next;
@ -962,6 +1054,40 @@ free_runtime_library (void) {
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static bool
init_runtime_library (struct context *ctx) {
bool ok = true;
struct {
const char *name; ///< Name of the function
const char *definition; ///< The defining script
} functions[] = {
// TODO: try to think of something useful
// XXX: should we add a ';' token to substitute newlines?
// FIXME: this "unless" is probably not going to work
{ "unless", "arg cond body\nif (not (eval @cond)) @body" },
};
for (size_t i = 0; i < N_ELEMENTS (functions); i++) {
struct parser parser;
parser_init (&parser,
functions[i].definition, strlen (functions[i].definition));
const char *e = NULL;
struct item *body = parser_run (&parser, &e);
if (e) {
printf ("error parsing internal function `%s': %s\n",
functions[i].name, e);
ok = false;
} else
ok &= set (ctx, functions[i].name, body);
item_free_list (body);
parser_free (&parser);
}
return ok;
}
// --- Main --------------------------------------------------------------------
int
@ -998,8 +1124,8 @@ main (int argc, char *argv[]) {
struct context ctx;
context_init (&ctx);
if (!init_runtime_library ()
|| !init_runtime_library_scripts (&ctx))
if (!init_native_library ()
|| !init_runtime_library (&ctx))
printf ("%s\n", "runtime library initialization failed");
struct item *result = NULL;
@ -1014,7 +1140,7 @@ main (int argc, char *argv[]) {
printf ("%s: %s\n", "runtime error", failure);
context_free (&ctx);
free_runtime_library ();
free_native_library ();
return 0;
}