~brenns10/funlisp

c1833616eb394de3e7e91680449d7bf92a60066e — Stephen Brennan 2 years ago 40fdf0c
Add support for user contexts attached to builtins
7 files changed, 128 insertions(+), 68 deletions(-)

M doc/embedding.rst
M inc/funlisp.h
M src/funlisp_internal.h
M src/public_util.c
M src/types.c
M src/util.c
M tools/hello_repl.c
M doc/embedding.rst => doc/embedding.rst +26 -5
@@ 108,7 108,8 @@ Builtins must have the following signature:

   lisp_value *lisp_builtin_somename(lisp_runtime *rt,
                                     lisp_scope *scope,
                                     lisp_value *arglist);
                                     lisp_value *arglist,
                                     void *user);

The scope argument contains the current binding of names to values, and the
arglist is a list of arguments to your function, which **have not been


@@ 151,6 152,10 @@ it fails, it returns false, which you should typically handle by returning an
error (:c:func:`lisp_error_new()`). If it doesn't fail, your function is free to
do whatever logic you'd like.

Finally, note that the signature includes a ``void *user`` parameter. This "user
context" is specified when you register the builtin function, and passed back to
you at runtime.

Basics of Lisp Types
--------------------



@@ 200,20 205,30 @@ interpreter's global scope. Anything can be added to a scope with
``lisp_scope_bind()``., but the name needs to be a ``lisp_symbol`` instance and
the value needs to be a ``lisp_value``. To save you the trouble of creating
those objects, you can simply use ``lisp_scope_add_builtin()``, which takes a
scope, a string name, and a function pointer.
scope, a string name, a function pointer, and a user context pointer.

Here is a code example that puts all of this together, based on the REPL given
above.

.. literalinclude:: ../tools/runfile.c
.. literalinclude:: ../tools/hello_repl.c
  :language: C

An example session using the builtin:
In this example, we've added a builtin function, defined at (1). This function
takes a string, and prints a greeting message. It uses its "user context" object
as a string, to introduce itself. During startup, we register the builtin
function once, with name "hello" (2). We provide it with a user context of "a
computer." We register it again at (3), with the name "hello_from_stephen", and
the user context "Stephen."

An example session using the builtin shows how the functions may be called from
the REPL, and how the user context objects affect the builtin:

.. code::

   > (hello "Stephen")
   Hello, Stephen!
   Hello, Stephen! I'm a computer.
   > (hello_from_stephen "computer")
   Hello, computer! I'm Stephen.
   > (hello 1)
   error: expected a string!
   > (hello 'Stephen)


@@ 274,6 289,12 @@ that end, the embedding application may associate a "user context" with the
be some global state, etc. Later (e.g. within a builtin function), the context
may be retrieved with :c:func:`lisp_runtime_get_ctx()`.

Furthermore, each builtin function may associate itself with a user context
object as well (provided upon registration). This allows the same C function to
be registered multiple times as multiple Funlisp functions. It also allows the C
function to access additional context, which it may not have been able to get
through the context object attached to the runtime.

Advanced Topics
---------------


M inc/funlisp.h => inc/funlisp.h +14 -5
@@ 517,10 517,16 @@ int lisp_integer_get(lisp_integer *integer);
 */

/**
 * A built-in function. Takes a runtime, scope of evaluation, and a list of
 * arguments.
 * A built-in function. Takes four arguments:
 * 1. The ::lisp_runtime associated with it. This may be used to retrieved the
 *    runtime's user context object (see lisp_runtime_get_ctx()).
 * 2. The ::lisp_scope this function is being called executed within. Most
 *    builtin functions will want to evaluate this with lisp_eval_list().
 * 3. The arguments to this function, as a ::lisp_list. These have not yet been
 *    evaluated.
 * 4. The user context associated with this builtin.
 */
typedef lisp_value * (*lisp_builtin_func)(lisp_runtime*, lisp_scope*,lisp_value*);
typedef lisp_value * (*lisp_builtin_func)(lisp_runtime*, lisp_scope*, lisp_value*, void*);

/**
 * Create a new ::lisp_builtin from a function pointer, with a given name.


@@ 530,10 536,11 @@ typedef lisp_value * (*lisp_builtin_func)(lisp_runtime*, lisp_scope*,lisp_value*
 * @param rt runtime
 * @param name name of the builtin. the interpreter will never free the name!
 * @param call function pointer of the builtin
 * @param user a user context pointer which will be given to the builtin
 * @return new builtin object
 */
lisp_builtin *lisp_builtin_new(lisp_runtime *rt, char *name,
                               lisp_builtin_func call);
                               lisp_builtin_func call, void *user);

/**
 * Shortcut to declare a builtin function. Simply takes a function pointer and a


@@ 543,8 550,10 @@ lisp_builtin *lisp_builtin_new(lisp_runtime *rt, char *name,
 * @param scope scope to bind builtin in
 * @param name name of builtin
 * @param call function pointer defining the builtin
 * @param user a user context pointer which will be given to the builtin
 */
void lisp_scope_add_builtin(lisp_runtime *rt, lisp_scope *scope, char *name, lisp_builtin_func call);
void lisp_scope_add_builtin(lisp_runtime *rt, lisp_scope *scope, char *name,
                            lisp_builtin_func call, void *user);

/**
 * Given a list of arguments, evaluate each of them within a scope and return a

M src/funlisp_internal.h => src/funlisp_internal.h +1 -0
@@ 95,6 95,7 @@ struct lisp_builtin {
	LISP_VALUE_HEAD;
	lisp_builtin_func call;
	char *name;
	void *user;
};

struct lisp_lambda {

M src/public_util.c => src/public_util.c +2 -1
@@ 70,11 70,12 @@ lisp_value *lisp_run_main_if_exists(lisp_runtime *rt, lisp_scope *scope,
}

lisp_builtin *lisp_builtin_new(lisp_runtime *rt, char *name,
                               lisp_builtin_func call)
                               lisp_builtin_func call, void *user)
{
	lisp_builtin *builtin = (lisp_builtin*)lisp_new(rt, type_builtin);
	builtin->call = call;
	builtin->name = name;
	builtin->user = user;
	return builtin;
}


M src/types.c => src/types.c +1 -1
@@ 477,7 477,7 @@ static lisp_value *builtin_call(lisp_runtime *rt, lisp_scope *scope,
                                lisp_value *c, lisp_value *arguments)
{
	lisp_builtin *builtin = (lisp_builtin*) c;
	return builtin->call(rt, scope, arguments);
	return builtin->call(rt, scope, arguments, builtin->user);
}

/*

M src/util.c => src/util.c +73 -51
@@ 53,10 53,10 @@ lisp_value *lisp_scope_lookup_string(lisp_runtime *rt, lisp_scope *scope, char *
}

void lisp_scope_add_builtin(lisp_runtime *rt, lisp_scope *scope, char *name,
                            lisp_builtin_func call)
                            lisp_builtin_func call, void *user)
{
	lisp_symbol *symbol = lisp_symbol_new(rt, name);
	lisp_builtin *builtin = lisp_builtin_new(rt, name, call);
	lisp_builtin *builtin = lisp_builtin_new(rt, name, call, user);
	lisp_scope_bind(scope, symbol, (lisp_value*)builtin);
}



@@ 191,16 191,18 @@ lisp_value *lisp_singleton_list(lisp_runtime *rt, lisp_value *entry)
}

static lisp_value *lisp_builtin_eval(lisp_runtime *rt, lisp_scope *scope,
                                     lisp_value *arguments)
                                     lisp_value *arguments, void *user)
{
	(void) user; /* unused */
	lisp_list *evald = (lisp_list*)lisp_eval_list(rt, scope, arguments);
	lisp_value *result = lisp_eval(rt, scope, evald->left);
	return result;
}

static lisp_value *lisp_builtin_car(lisp_runtime *rt, lisp_scope *scope,
                                    lisp_value *a)
                                    lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_list *firstarg;
	lisp_list *arglist = (lisp_list*) lisp_eval_list(rt, scope, a);
	if (!lisp_get_args(arglist, "l", &firstarg)) {


@@ 213,8 215,9 @@ static lisp_value *lisp_builtin_car(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_cdr(lisp_runtime *rt, lisp_scope *scope,
                                    lisp_value *a)
                                    lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_list *firstarg;
	lisp_list *arglist = (lisp_list*) lisp_eval_list(rt, scope, a);
	if (!lisp_get_args(arglist, "l", &firstarg)) {


@@ 225,8 228,9 @@ static lisp_value *lisp_builtin_cdr(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_quote(lisp_runtime *rt, lisp_scope *scope,
                                      lisp_value *a)
                                      lisp_value *a, void *user)
{
	(void) user; /* unused */
	(void)scope;
	lisp_value *firstarg;
	lisp_list *arglist = (lisp_list*) a;


@@ 237,8 241,9 @@ static lisp_value *lisp_builtin_quote(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_cons(lisp_runtime *rt, lisp_scope *scope,
                                     lisp_value *a)
                                     lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_value *a1;
	lisp_value *l;
	lisp_list *arglist = (lisp_list*) lisp_eval_list(rt, scope, a);


@@ 252,8 257,9 @@ static lisp_value *lisp_builtin_cons(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_lambda(lisp_runtime *rt, lisp_scope *scope,
                                       lisp_value *a)
                                       lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_list *argnames;
	lisp_value *code;
	lisp_list *our_args = (lisp_list*)a;


@@ 279,8 285,9 @@ static lisp_value *lisp_builtin_lambda(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_define(lisp_runtime *rt, lisp_scope *scope,
                                       lisp_value *a)
                                       lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_symbol *s;
	lisp_value *expr;



@@ 295,8 302,9 @@ static lisp_value *lisp_builtin_define(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_plus(lisp_runtime *rt, lisp_scope *scope,
                                     lisp_value *a)
                                     lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_integer *i;
	lisp_list *args = (lisp_list*)lisp_eval_list(rt, scope, a);
	int sum = 0;


@@ 316,8 324,9 @@ static lisp_value *lisp_builtin_plus(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_minus(lisp_runtime *rt, lisp_scope *scope,
                                      lisp_value *a)
                                      lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_integer *i;
	lisp_list *args = (lisp_list*)lisp_eval_list(rt, scope, a);
	int val = 0;


@@ 348,8 357,9 @@ static lisp_value *lisp_builtin_minus(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_multiply(lisp_runtime *rt, lisp_scope *scope,
                                         lisp_value *a)
                                         lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_integer *i;
	lisp_list *args = (lisp_list*) lisp_eval_list(rt, scope, a);
	int product = 1;


@@ 369,8 379,9 @@ static lisp_value *lisp_builtin_multiply(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_divide(lisp_runtime *rt, lisp_scope *scope,
                                       lisp_value *a)
                                       lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_integer *i;
	lisp_list *args = (lisp_list*)lisp_eval_list(rt, scope, a);
	int val = 0;


@@ 400,8 411,9 @@ static lisp_value *lisp_builtin_divide(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_cmp_util(lisp_runtime *rt, lisp_scope *scope,
                                         lisp_value *a)
                                         lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_integer *first, *second;
	lisp_list *args = (lisp_list*) lisp_eval_list(rt, scope, a);



@@ 415,9 427,10 @@ static lisp_value *lisp_builtin_cmp_util(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_eq(lisp_runtime *rt, lisp_scope *scope,
                                   lisp_value *a)
                                   lisp_value *a, void *user)
{
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a);
	(void) user; /* unused */
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a, user);
	if (v->type == type_integer) {
		v->x = (v->x == 0);
	}


@@ 425,9 438,10 @@ static lisp_value *lisp_builtin_eq(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_gt(lisp_runtime *rt, lisp_scope *scope,
                                   lisp_value *a)
                                   lisp_value *a, void *user)
{
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a);
	(void) user; /* unused */
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a, user);
	if (v->type == type_integer) {
		v->x = (v->x > 0);
	}


@@ 435,9 449,10 @@ static lisp_value *lisp_builtin_gt(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_ge(lisp_runtime *rt, lisp_scope *scope,
                                   lisp_value *a)
                                   lisp_value *a, void *user)
{
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a);
	(void) user; /* unused */
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a, user);
	if (v->type == type_integer) {
		v->x = (v->x >= 0);
	}


@@ 445,9 460,10 @@ static lisp_value *lisp_builtin_ge(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_lt(lisp_runtime *rt, lisp_scope *scope,
                                   lisp_value *a)
                                   lisp_value *a, void *user)
{
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a);
	(void) user; /* unused */
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a, user);
	if (v->type == type_integer) {
		v->x = (v->x < 0);
	}


@@ 455,9 471,10 @@ static lisp_value *lisp_builtin_lt(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_le(lisp_runtime *rt, lisp_scope *scope,
                                   lisp_value *a)
                                   lisp_value *a, void *user)
{
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a);
	(void) user; /* unused */
	lisp_integer *v = (lisp_integer*)lisp_builtin_cmp_util(rt, scope, a, user);
	if (v->type == type_integer) {
		v->x = (v->x <= 0);
	}


@@ 465,8 482,9 @@ static lisp_value *lisp_builtin_le(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_if(lisp_runtime *rt, lisp_scope *scope,
                                   lisp_value *a)
                                   lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_value *condition, *body_true, *body_false;

	if (!lisp_get_args((lisp_list*)a, "***", &condition, &body_true, &body_false)) {


@@ 482,8 500,9 @@ static lisp_value *lisp_builtin_if(lisp_runtime *rt, lisp_scope *scope,
}

static lisp_value *lisp_builtin_null_p(lisp_runtime *rt, lisp_scope *scope,
                                       lisp_value *a)
                                       lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_value *v;
	lisp_list *args = (lisp_list*) lisp_eval_list(rt, scope, a);



@@ 547,8 566,9 @@ static lisp_list *advance_lists(lisp_runtime *rt, lisp_list *list_of_lists)
}

static lisp_value *lisp_builtin_map(lisp_runtime *rt, lisp_scope *scope,
                                    lisp_value *a)
                                    lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_value *f;
	lisp_list *ret = NULL, *args, *rv;
	lisp_list *map_args = (lisp_list *) lisp_eval_list(rt, scope, a);


@@ 574,8 594,9 @@ static lisp_value *lisp_builtin_map(lisp_runtime *rt, lisp_scope *scope,
	return (lisp_value*) rv;
}

static lisp_value *lisp_builtin_reduce(lisp_runtime *rt, lisp_scope *scope, lisp_value *a)
static lisp_value *lisp_builtin_reduce(lisp_runtime *rt, lisp_scope *scope, lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_list *args = (lisp_list*) lisp_eval_list(rt, scope, a);
	int length = lisp_list_length(args);
	lisp_value *callable, *initializer;


@@ 609,8 630,9 @@ static lisp_value *lisp_builtin_reduce(lisp_runtime *rt, lisp_scope *scope, lisp
	return initializer;
}

static lisp_value *lisp_builtin_print(lisp_runtime *rt, lisp_scope *scope, lisp_value *a)
static lisp_value *lisp_builtin_print(lisp_runtime *rt, lisp_scope *scope, lisp_value *a, void *user)
{
	(void) user; /* unused */
	lisp_value *v;
	lisp_list *args = (lisp_list*) lisp_eval_list(rt, scope, a);



@@ 625,26 647,26 @@ static lisp_value *lisp_builtin_print(lisp_runtime *rt, lisp_scope *scope, lisp_

void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope)
{
	lisp_scope_add_builtin(rt, scope, "eval", lisp_builtin_eval);
	lisp_scope_add_builtin(rt, scope, "car", lisp_builtin_car);
	lisp_scope_add_builtin(rt, scope, "cdr", lisp_builtin_cdr);
	lisp_scope_add_builtin(rt, scope, "quote", lisp_builtin_quote);
	lisp_scope_add_builtin(rt, scope, "cons", lisp_builtin_cons);
	lisp_scope_add_builtin(rt, scope, "lambda", lisp_builtin_lambda);
	lisp_scope_add_builtin(rt, scope, "define", lisp_builtin_define);
	lisp_scope_add_builtin(rt, scope, "+", lisp_builtin_plus);
	lisp_scope_add_builtin(rt, scope, "-", lisp_builtin_minus);
	lisp_scope_add_builtin(rt, scope, "*", lisp_builtin_multiply);
	lisp_scope_add_builtin(rt, scope, "/", lisp_builtin_divide);
	lisp_scope_add_builtin(rt, scope, "==", lisp_builtin_eq);
	lisp_scope_add_builtin(rt, scope, "=", lisp_builtin_eq);
	lisp_scope_add_builtin(rt, scope, ">", lisp_builtin_gt);
	lisp_scope_add_builtin(rt, scope, ">=", lisp_builtin_ge);
	lisp_scope_add_builtin(rt, scope, "<", lisp_builtin_lt);
	lisp_scope_add_builtin(rt, scope, "<=", lisp_builtin_le);
	lisp_scope_add_builtin(rt, scope, "if", lisp_builtin_if);
	lisp_scope_add_builtin(rt, scope, "null?", lisp_builtin_null_p);
	lisp_scope_add_builtin(rt, scope, "map", lisp_builtin_map);
	lisp_scope_add_builtin(rt, scope, "reduce", lisp_builtin_reduce);
	lisp_scope_add_builtin(rt, scope, "print", lisp_builtin_print);
	lisp_scope_add_builtin(rt, scope, "eval", lisp_builtin_eval, NULL);
	lisp_scope_add_builtin(rt, scope, "car", lisp_builtin_car, NULL);
	lisp_scope_add_builtin(rt, scope, "cdr", lisp_builtin_cdr, NULL);
	lisp_scope_add_builtin(rt, scope, "quote", lisp_builtin_quote, NULL);
	lisp_scope_add_builtin(rt, scope, "cons", lisp_builtin_cons, NULL);
	lisp_scope_add_builtin(rt, scope, "lambda", lisp_builtin_lambda, NULL);
	lisp_scope_add_builtin(rt, scope, "define", lisp_builtin_define, NULL);
	lisp_scope_add_builtin(rt, scope, "+", lisp_builtin_plus, NULL);
	lisp_scope_add_builtin(rt, scope, "-", lisp_builtin_minus, NULL);
	lisp_scope_add_builtin(rt, scope, "*", lisp_builtin_multiply, NULL);
	lisp_scope_add_builtin(rt, scope, "/", lisp_builtin_divide, NULL);
	lisp_scope_add_builtin(rt, scope, "==", lisp_builtin_eq, NULL);
	lisp_scope_add_builtin(rt, scope, "=", lisp_builtin_eq, NULL);
	lisp_scope_add_builtin(rt, scope, ">", lisp_builtin_gt, NULL);
	lisp_scope_add_builtin(rt, scope, ">=", lisp_builtin_ge, NULL);
	lisp_scope_add_builtin(rt, scope, "<", lisp_builtin_lt, NULL);
	lisp_scope_add_builtin(rt, scope, "<=", lisp_builtin_le, NULL);
	lisp_scope_add_builtin(rt, scope, "if", lisp_builtin_if, NULL);
	lisp_scope_add_builtin(rt, scope, "null?", lisp_builtin_null_p, NULL);
	lisp_scope_add_builtin(rt, scope, "map", lisp_builtin_map, NULL);
	lisp_scope_add_builtin(rt, scope, "reduce", lisp_builtin_reduce, NULL);
	lisp_scope_add_builtin(rt, scope, "print", lisp_builtin_print, NULL);
}

M tools/hello_repl.c => tools/hello_repl.c +11 -5
@@ 1,5 1,6 @@
/*
 * repl.c: A simple read-eval-print loop for funlisp
 * hello_repl.c: A simple read-eval-print loop for funlisp, with builtin
 * functions registered.
 *
 * Stephen Brennan <stephen@brennan.io>
 */


@@ 10,9 11,11 @@

#include "funlisp.h"

/* NEW: a builtin function declaration */
static lisp_value *say_hello(lisp_runtime *rt, lisp_scope *scope, lisp_value *a)
/* (1) Here is our builtin function declaration. */
static lisp_value *say_hello(lisp_runtime *rt, lisp_scope *scope,
                             lisp_value *a, void *user)
{
	char *from = user;
	lisp_string *s;
	lisp_value *arglist = lisp_eval_list(rt, scope, a);



@@ 20,7 23,7 @@ static lisp_value *say_hello(lisp_runtime *rt, lisp_scope *scope, lisp_value *a)
		return (lisp_value*)lisp_error_new(rt, "expected a string!");
	}

	printf("Hello, %s!\n", lisp_string_get(s));
	printf("Hello, %s! I'm %s.\n", lisp_string_get(s), from);

	/* must return something, so return nil */
	return lisp_nil_new(rt);


@@ 34,7 37,10 @@ int main(int argc, char **argv)
	lisp_runtime *rt = lisp_runtime_new();
	lisp_scope *scope = lisp_new_default_scope(rt);

	lisp_scope_add_builtin(rt, scope, "hello", say_hello);
	/* (2) Here we register the builtin once */
	lisp_scope_add_builtin(rt, scope, "hello", say_hello, "a computer");
	/* (3) Now register the same function with a different context object */
	lisp_scope_add_builtin(rt, scope, "hello_from_stephen", say_hello, "Stephen");

	for (;;) {
		char *input = readline("> ");