~brenns10/funlisp

044858c2d8d40183cf781bc1723ea329067a5dd5 — Stephen Brennan 9 months ago c2cd4cb + 9e597f3
Merge branch 'feature_modules'
M Makefile => Makefile +5 -4
@@ 6,7 6,8 @@
include Makefile.conf

OBJS=src/builtins.o src/charbuf.o src/gc.o src/hashtable.o src/iter.o \
     src/parse.o src/ringbuf.o src/types.o src/util.o src/textcache.o
     src/parse.o src/ringbuf.o src/types.o src/util.o src/textcache.o \
     src/module.o

# https://semver.org
VERSION=1.1.0


@@ 39,7 40,7 @@ bin/example_list_append: tools/example_list_append.o bin/libfunlisp.a
	$(CC) $(CFLAGS) $^ -o $@

clean: FORCE
	rm -rf bin/* src/*.o tools/*.o src/*.gcda src/*.gcno
	rm -rf bin/* {src,tools}/*.{o,gcda,gcno}

# Meant to be run after downloading the source tarball. This has an un-expressed
# dependency on `man/funlisp.3`, since the source tarball includes generated


@@ 82,7 83,7 @@ doc: FORCE

test: all FORCE
	rm -f cov*.html src/*.gcda
	@python test.py scripts/tests
	@cd scripts && python ../test.py tests -r ../bin/funlisp
	gcovr -r src --html --html-details -o cov.html

clean_doc:


@@ 103,7 104,7 @@ package: FORCE clean doc
		inc/*.h src/*.c src/*.h tools/*.c scripts/*.lisp bin/.gitkeep

serve_doc: doc
	cd doc/_build/html; python -m http.server --bind 0.0.0.0 8080
	cd html; python -m http.server --bind 0.0.0.0 8080

# Named differently from Makefile.dep to avoid implicit rules for inclusion.
depend: FORCE

M Makefile.dep => Makefile.dep +2 -0
@@ 5,6 5,8 @@ gc.o: src/gc.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h
hashtable.o: src/hashtable.c src/iter.h src/hashtable.h
iter.o: src/iter.c src/iter.h
module.o: src/module.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h
parse.o: src/parse.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h src/charbuf.h
ringbuf.o: src/ringbuf.c src/ringbuf.h

M doc/api.rst => doc/api.rst +6 -0
@@ 39,6 39,12 @@ Builtin Functions
.. doxygengroup:: builtins
   :content-only:

Modules
-------

.. doxygengroup:: modules
   :content-only:

Embedding Tools
---------------


M inc/funlisp.h => inc/funlisp.h +84 -2
@@ 197,6 197,12 @@ typedef struct lisp_lambda lisp_lambda;
typedef struct lisp_list lisp_list;

/**
 * Data structure representing a module.
 * @ingroup types
 */
typedef struct lisp_module lisp_module;

/**
 * @defgroup value Lisp Values
 * @{
 */


@@ 508,6 514,11 @@ extern lisp_type *type_builtin;
extern lisp_type *type_lambda;

/**
 * Type object of ::lisp_module
 */
extern lisp_type *type_module;

/**
 * Flag instructing string/symbol creation routines that they should copy the
 * string buffer itself, and use the copy rather than the original argument.
 * This could be useful in case callers would like to free the string after


@@ 713,6 724,76 @@ int lisp_get_args(lisp_runtime *rt, lisp_list *list, char *format, ...);

/**
 * @}
 * @defgroup modules Modules
 * @{
 */

/**
 * @brief Create a new, empty module object.
 *
 * The module object represents a separate namespace which can contain symbols
 * mapped to builtins, lambdas, constants, and more. It is a great place to
 * store related functionality. The module can represent all of the symbols
 * defined in a single lisp file. It can also represent a collection of
 * resources created by C code.
 *
 * C code which wishes to create a module should start here, access the scope
 * from the resulting module using lisp_module_get_scope(), and populate the
 * scope with any builtins or other data necessary. Finally, modules should be
 * registered with the runtime using lisp_register_module().
 *
 * @param rt runtime
 * @param name the name of the module, used for imports
 * @param file filename for the module object. This file is not actually loaded,
 * simply stored as part of the module metadata. If you would like to load a
 * file as a module, see lisp_import_file().
 * @return a new, empty module object
 */
lisp_module *lisp_new_module(lisp_runtime *rt, lisp_string *name, lisp_string *file);

/**
 * @brief Given a module returned by lisp_new_module(), access its scope
 * @param module A module to get the scope of
 * @return the scope of that module
 */
lisp_scope *lisp_module_get_scope(lisp_module *module);

/**
 * @brief Given a module object, register it with the runtime
 *
 * The module is mapped using the name given at creation time. For example, if a
 * module was given the name ``foo``, then it can be imported with the statement
 * ``(import foo)``.
 *
 * @param rt runtime
 * @param module module to register
 */
void lisp_register_module(lisp_runtime *rt, lisp_module *module);

/**
 * @brief Load a file of lisp code and return it as a module object.
 * @param name Name for the resulting module
 * @param file Filename to read and load as a module
 * @return the resulting module
 */
lisp_module *lisp_import_file(lisp_runtime *rt, lisp_string *name, lisp_string *file);

/**
 * @brief Given a module name, import and return it if possible, or raise error
 *
 * This process works as follows. First, check whether an existing module has
 * already been registered by that name. If so, return it. Second, attempt to
 * read ``./NAME.lisp`` as a lisp file, and return that as a module. Raise error
 * on failure.
 *
 * @param rt runtime
 * @param name name of module to import
 * @return the resulting module
 */
lisp_module *lisp_do_import(lisp_runtime *rt, lisp_symbol *name);

/**
 * @}
 * @defgroup embed Embedding API
 * @{
 */


@@ 864,6 945,7 @@ enum lisp_errno {
	LE_EXIT,     /* exit the interpreter */
	LE_ASSERT,   /* assertion error */
	LE_VALUE,    /* invalid argument */
	LE_ERRNO,    /* used for C library errors, does perror() */

	LE_MAX_ERR   /* not a real error, don't use */
};


@@ 882,11 964,11 @@ extern const char *lisp_error_name[LE_MAX_ERR];
 * @endcode
 *
 * @param rt runtime
 * @param errno error number, for easy programatic acccess
 * @param err_num error number, for easy programatic acccess
 * @param message message to show the user
 * @return NULL
 */
lisp_value *lisp_error(lisp_runtime *rt, enum lisp_errno errno, char *message);
lisp_value *lisp_error(lisp_runtime *rt, enum lisp_errno err_num, char *message);

/**
 * Dump the execution stack to a file. This is useful if you want to print a

A scripts/tests/modules.lisp => scripts/tests/modules.lisp +12 -0
@@ 0,0 1,12 @@
(assert (equal? '(getattr a 'b) 'a.b))
(assert (equal? '(getattr (getattr one 'two) 'three) 'one.two.three))

(import os)

(import filter)

(assert (equal?
          (filter.filter (lambda (v) (< v 3)) '(1 2 3 4 5))
          '(1 2)))

; OUTPUT(0)

M src/builtins.c => src/builtins.c +45 -5
@@ 619,7 619,7 @@ static lisp_value *lisp_builtin_assert_error(
	/* args are NOT evaluated, to avoid error handling short circuit */
	lisp_value *sym, *expr;
	lisp_symbol *sym_evald;
	enum lisp_errno errno;
	enum lisp_errno err_num;
	(void) user;

	if (!lisp_get_args(rt, arglist, "**", &sym, &expr))


@@ 629,19 629,19 @@ static lisp_value *lisp_builtin_assert_error(
	lisp_error_check(sym_evald);
	if (sym_evald->type != type_symbol)
		return lisp_error(rt, LE_TYPE, "error type must be symbol");
	errno = lisp_sym_to_errno(sym_evald);
	if (errno == LE_MAX_ERR)
	err_num = lisp_sym_to_errno(sym_evald);
	if (err_num == LE_MAX_ERR)
		return lisp_error(rt, LE_VALUE, "unrecognized error type");

	lisp_eval(rt, scope, expr); /* we don't care, cause we expect error */
	/* NO ERROR CHECK HERE */

	if (errno == lisp_get_errno(rt)) {
	if (err_num == lisp_get_errno(rt)) {
		lisp_clear_error(rt);
		return (lisp_value*) sym_evald;
	} else {
		fprintf(stderr, "Assertion error! Expected %s\n",
			lisp_error_name[errno]);
			lisp_error_name[err_num]);
		fprintf(stderr, "This was the actual error encountered: ");
		lisp_print_error(rt, stderr);
		fprintf(stderr, "\nBelow should be the assertion error stack trace.\n");


@@ 742,6 742,44 @@ static lisp_value *lisp_builtin_let(
	return lisp_progn(rt, new_scope, expressions);
}

static lisp_value *lisp_builtin_import(
		lisp_runtime *rt, lisp_scope *scope, lisp_list *arglist, void *user)
{
	/*
	 * args are NOT evaluated
	 * (import symbol)  ->  looks up module "symbol" and inserts it into
	 * current scope
	 */
	lisp_symbol *sym;
	lisp_module *mod;

	(void) user; /* unused */

	if (!lisp_get_args(rt, arglist, "s", &sym))
		return NULL;

	mod = lisp_do_import(rt, sym);
	lisp_error_check(mod);

	lisp_scope_bind(scope, sym, (lisp_value*)mod);
	return (lisp_value*)mod;
}

static lisp_value *lisp_builtin_getattr(
		lisp_runtime *rt, lisp_scope *scope, lisp_list *arglist, void *user)
{
	lisp_module *mod;
	lisp_symbol *sym;

	(void) user;
	(void) scope; /* unused */

	if (!lisp_get_args(rt, arglist, "*s", &mod, &sym))
		return NULL;

	return lisp_scope_lookup(rt, mod->contents, sym);
}

void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope)
{
	lisp_scope_add_builtin(rt, scope, "eval", lisp_builtin_eval, NULL, 1);


@@ 779,4 817,6 @@ void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope)
	lisp_scope_add_builtin(rt, scope, "cond", lisp_builtin_cond, NULL, 0);
	lisp_scope_add_builtin(rt, scope, "list", lisp_builtin_list, NULL, 1);
	lisp_scope_add_builtin(rt, scope, "let", lisp_builtin_let, NULL, 0);
	lisp_scope_add_builtin(rt, scope, "import", lisp_builtin_import, NULL, 0);
	lisp_scope_add_builtin(rt, scope, "getattr", lisp_builtin_getattr, NULL, 1);
}

M src/funlisp_internal.h => src/funlisp_internal.h +17 -4
@@ 64,15 64,15 @@ struct lisp_runtime {
	/* Data we use for reporting errors. This is very single-threaded of me,
	 * but it works for now.
	 * REQUIREMENTS:
	 *   - error and errno must both always be non-NULL and non-0
	 *   - error and err_num must both always be non-NULL and non-0
	 *     respectively when an error occurs
	 *   - error_line and error_stack may optionally be set
	 * You can use lisp_error() to set error, errno, and error_stack.
	 * Parsing functions typically use a macro to set error, errno, and
	 * You can use lisp_error() to set error, err_num, and error_stack.
	 * Parsing functions typically use a macro to set error, err_num, and
	 * error_line.
	 */
	char *error;
	enum lisp_errno errno;
	enum lisp_errno err_num;
	unsigned int error_line;
	lisp_list *error_stack;



@@ 84,6 84,8 @@ struct lisp_runtime {
	struct hashtable *symcache;
	/* Maintain cache of lisp_string */
	struct hashtable *strcache;
	/* Maintain builtin module list */
	lisp_scope *modules;
};

/* The below ARE lisp_values! */


@@ 143,6 145,13 @@ struct lisp_lambda {
	int lambda_type;
};

struct lisp_module {
	LISP_VALUE_HEAD;
	lisp_scope *contents;
	lisp_string *name;
	lisp_string *file;
};

/**
 * A function which consumes a single ::lisp_value and produces a new one as a
 * result.


@@ 186,4 195,8 @@ int lisp_text_compare(void *left, void *right);
void lisp_textcache_remove(struct hashtable *cache, struct lisp_text *t);

int lisp_truthy(lisp_value *v);

lisp_module *create_os_module(lisp_runtime *rt);
lisp_module *lisp_lookup_module(lisp_runtime *rt, lisp_symbol *name);

#endif

M src/gc.c => src/gc.c +4 -0
@@ 24,6 24,9 @@ void lisp_init(lisp_runtime *rt)
	rt->stack_depth = 0;
	rt->symcache = NULL;
	rt->strcache = NULL;
	rt->modules = lisp_new_empty_scope(rt);

	lisp_register_module(rt, create_os_module(rt));
}

void lisp_destroy(lisp_runtime *rt)


@@ 70,6 73,7 @@ static void lisp_mark_basics(lisp_runtime *rt)
	if (rt->error_stack)
		lisp_mark(rt, (lisp_value *) rt->error_stack);
	lisp_mark(rt, (lisp_value *) rt->stack);
	lisp_mark(rt, (lisp_value *) rt->modules);
}

void lisp_sweep(lisp_runtime *rt)

M src/iter.c => src/iter.c +45 -1
@@ 7,6 7,7 @@
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>

#include "iter.h"



@@ 94,7 95,7 @@ struct iterator iterator_concat2(struct iterator left, struct iterator right)
}

struct iterator iterator_concat3(struct iterator a, struct iterator b,
																 struct iterator c)
				 struct iterator c)
{
	struct iterator *arr = calloc(sizeof(struct iterator), 3);
	arr[0] = a;


@@ 124,3 125,46 @@ struct iterator iterator_empty()
	it.close=iterator_close_noop;
	return it;
}

static bool array_has_next(struct iterator *iter)
{
	return iter->index < iter->state_int;
}

static void *array_next(struct iterator *iter)
{
	return ((void**)iter->ds)[iter->index++];
}

static void array_close(struct iterator *iter)
{
	if ((bool) iter->state_ptr)
		free(iter->ds);
}

struct iterator iterator_array(void **array, int len, bool own)
{
	struct iterator it = {0};
	it.index = 0;
	it.ds = array;
	it.state_int = len;
	it.state_ptr = (void*) own;
	it.has_next = array_has_next;
	it.next = array_next;
	it.close = array_close;
	return it;
}

struct iterator iterator_from_args(int n, ...)
{
	void **array = calloc(sizeof(void*), n);
	va_list va;
	int i;

	va_start(va, n);
	for (i = 0; i < n; i++)
		array[i] = va_arg(va, void*);
	va_end(va);

	return iterator_array(array, n, true);
}

M src/iter.h => src/iter.h +4 -0
@@ 36,4 36,8 @@ struct iterator iterator_concat2(struct iterator left, struct iterator right);
struct iterator iterator_concat3(
	struct iterator a, struct iterator b, struct iterator c);

/* return an iterator that yields an array */
struct iterator iterator_array(void **array, int len, bool own);
struct iterator iterator_from_args(int n, ...);

#endif

A src/module.c => src/module.c +112 -0
@@ 0,0 1,112 @@
/*
 * Module-related functionality. Includes builtin modules.
 */
#include <stdlib.h>
#include <string.h>

#include "funlisp_internal.h"

static lisp_value *lisp_os_getenv(lisp_runtime *rt, lisp_scope *scope,
                                  lisp_list *arguments, void *user)
{
	lisp_string *str;
	char *res;

	/* args evaluated */
	(void) user;
	(void) scope;

	if (!lisp_get_args(rt, arguments, "S", &str))
		return NULL;

	res = getenv(str->s);
	if (res)
		return (lisp_value*) lisp_string_new(rt, res, LS_CPY | LS_OWN);
	else
		return lisp_nil_new(rt);
}

lisp_module *create_os_module(lisp_runtime *rt)
{

	lisp_module *m = lisp_new_module(rt, lisp_string_new(rt, "os", 0),
		lisp_string_new(rt, __FILE__, 0));
	lisp_scope_add_builtin(rt, m->contents, "getenv", lisp_os_getenv, NULL, 1);
	return m;
}

void lisp_register_module(lisp_runtime *rt, lisp_module *m)
{
	/* TODO this depends on symbols and strings hashing to same value and
	 * comparing equal */
	lisp_scope_bind(rt->modules, (lisp_symbol*)m->name, (lisp_value*) m);
}

lisp_module *lisp_lookup_module(lisp_runtime *rt, lisp_symbol *name)
{
	lisp_module *m = (lisp_module*) lisp_scope_lookup(rt, rt->modules, name);

	if (!m) /* not found is not necessarily an error */
		lisp_clear_error(rt);

	return m;
}

lisp_module *lisp_new_module(lisp_runtime *rt, lisp_string *name, lisp_string *file)
{
	lisp_module *m = (lisp_module *)lisp_new(rt, type_module);
	m->name = name;
	m->file = file;
	m->contents = lisp_new_empty_scope(rt);
	return m;
}

lisp_scope *lisp_module_get_scope(lisp_module *module)
{
	return module->contents;
}

lisp_module *lisp_import_file(lisp_runtime *rt, lisp_string *name, lisp_string *file)
{
	FILE *f;
	lisp_scope *builtins = lisp_new_default_scope(rt);
	lisp_scope *modscope = lisp_new_empty_scope(rt);
	lisp_module *module;
	lisp_value *v;
	modscope->up = builtins;

	f = fopen(file->s, "r");
	if (!f) {
		return (lisp_module*) lisp_error(rt, LE_ERRNO, "error opening file for import");
	}

	v = lisp_load_file(rt, modscope, f);
	lisp_error_check(v);

	module = (lisp_module *)lisp_new(rt, type_module);
	module->contents = modscope;
	module->name = name;
	module->file = file;
	lisp_register_module(rt, module);
	return module;
}

lisp_module *lisp_do_import(lisp_runtime *rt, lisp_symbol *name)
{
	lisp_module *m;
	lisp_string *file, *namestr;
	char *filestr;
	int len;

	m = lisp_lookup_module(rt, name);
	if (m)
		return m;

	len = strlen(name->s);
	len += 1 /*nul*/ + 7 /*./ .lisp */;
	filestr = malloc(len);
	sprintf(filestr, "./%s.lisp", name->s);
	file = lisp_string_new(rt, filestr, LS_OWN);
	namestr = lisp_symbol_new(rt, name->s, LS_OWN | LS_CPY);
	return lisp_import_file(rt, namestr, file);
}

M src/parse.c => src/parse.c +67 -3
@@ 175,21 175,85 @@ static result lisp_parse_list_or_sexp(lisp_runtime *rt, char *input, int index)
	}
}

static lisp_value *split_symbol(lisp_runtime *rt, char *string, int dotcount, int remain)
{
	char *delim, *tok;
	int i, len;
	lisp_value *prev = NULL;
	lisp_symbol *sym;
	lisp_list *list;
	lisp_symbol *getattr = lisp_symbol_new(rt, "getattr", 0);

	/* Create the first symbol, which is the left hand side */
	delim = strchr(string, '.');
	len = (int) (delim - string);
	tok = malloc(len + 1);
	strncpy(tok, string, len);
	tok[len] = '\0';
	sym = lisp_symbol_new(rt, tok, LS_OWN);
	prev = (lisp_value*) sym;
	string = delim + 1;
	remain -= len + 1;

	/* Create a "getattr" for each right hand side remaining */
	for (i = 0; i < dotcount; i++) {
		/* Attribute symbol */
		if (i < dotcount - 1) {
			delim = strchr(string, '.');
			len = (int) (delim - string);
		} else {
			len = remain;
		}
		tok = malloc(len + 1);
		strncpy(tok, string, len);
		tok[len] = '\0';
		sym = lisp_symbol_new(rt, tok, LS_OWN);

		/* Create (getattr PREV 'tok) */
		list = lisp_list_new(rt,
			(lisp_value *) getattr,
			(lisp_value *) lisp_list_new(rt,
				prev,
				(lisp_value *) lisp_list_new(rt,
					(lisp_value *) lisp_quote_with(rt, (lisp_value *) sym, "quote"),
					lisp_nil_new(rt)
				)
			)
		);
		prev = (lisp_value *) list;
		string = delim + 1;
		remain -= len + 1;
	}
	return prev;
}

static result lisp_parse_symbol(lisp_runtime *rt, char *input, int index)
{
	int n = 0;
	int dotcount = 0;
	char *copy;
	lisp_symbol *s;

	while (input[index + n] && !isspace(input[index + n]) &&
	       input[index + n] != ')' && input[index + n] != '.' &&
	       input[index + n] != ')' && /* input[index + n] != '.' && */
	       input[index + n] != '\'' && input[index + n] != COMMENT) {
		if (input[index + n] == '.')
			dotcount++;
		n++;
	}
	if (!input[index]) {
		rt->error = "unexpected eof while parsing symbol";
		return_result_err(NULL, index, LE_EOF);
	}

	if (dotcount) {
		if (input[index] == '.' || input[index + n - 1] == '.') {
			rt->error = "unexpected '.' at beginning or end of symbol";
			return_result_err(NULL, index, LE_SYNTAX);
		}
		return_result(split_symbol(rt, input + index, dotcount, n), index + n);
	}

	copy = malloc(n + 1);
	strncpy(copy, input + index, n);
	copy[n] = '\0';


@@ 263,7 327,7 @@ int lisp_parse_value(lisp_runtime *rt, char *input, int index, lisp_value **outp
	result r = lisp_parse_value_internal(rt, input, index);
	bytes = r.index - index;
	if (r.error) {
		rt->errno = r.error;
		rt->err_num = r.error;
		set_error_lineno(rt, input, r.index);
		bytes = -1;
	}


@@ 327,7 391,7 @@ lisp_value *lisp_parse_progn_f(lisp_runtime *rt, FILE *input)
	input_string = read_file(input);
	if (!input_string) {
		rt->error = "error reading from input file";
		rt->errno = LE_FERROR;
		rt->err_num = LE_FERROR;
		return NULL;
	}
	result = lisp_parse_progn(rt, input_string);

M src/types.c => src/types.c +58 -1
@@ 179,7 179,7 @@ static struct iterator scope_expand(lisp_value *v)
	lisp_scope *scope = (lisp_scope *) v;
	if (scope->up) {
		return iterator_concat3(
			iterator_single_value(&scope->up),
			iterator_single_value(scope->up),
			ht_iter_keys_ptr(&scope->scope),
			ht_iter_values_ptr(&scope->scope)
		);


@@ 795,3 795,60 @@ int lisp_compare(lisp_value *self, lisp_value *other)
{
	return self->type->compare(self, other);
}

/*
 * module
 */

static void module_print(FILE *f, lisp_value*v);
static lisp_value *module_new(lisp_runtime *rt);
static struct iterator module_expand(lisp_value *);
static int module_compare(lisp_value *self, lisp_value *other);

static lisp_type type_module_obj = {
	TYPE_HEADER,
	/* name */ "module",
	/* print */ module_print,
	/* new */ module_new,
	/* free */ simple_free,
	/* expand */ module_expand,
	/* eval */ eval_error,
	/* call */ call_error,
	/* compare */ module_compare,
};
lisp_type *type_module = &type_module_obj;

static lisp_value *module_new(lisp_runtime *rt)
{
	lisp_module *module;
	(void) rt; /* unused */

	module = malloc(sizeof(lisp_module));
	module->contents = NULL;
	module->name = NULL;
	module->name = NULL;
	return (lisp_value*)module;
}

static void module_print(FILE *f, lisp_value *v)
{
	lisp_module *module = (lisp_module*) v;
	fprintf(f, "<module '%s' from '%s' at 0x%p>",
			module->name->s, module->file->s, (void*)module);
}

static struct iterator module_expand(lisp_value *v)
{
	lisp_module *module = (lisp_module *) v;
	return iterator_from_args(3,
		module->name,
		module->file,
		module->contents
	);
}

static int module_compare(lisp_value *self, lisp_value *other)
{
	/* Compare by value for simplicity. */
	return self == other;
}

M src/util.c => src/util.c +17 -8
@@ 12,6 12,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include "funlisp_internal.h"
#include "hashtable.h"


@@ 33,6 34,7 @@ const char *lisp_error_name[LE_MAX_ERR] = {
	"LE_EXIT",
	"LE_ASSERT",
	"LE_VALUE",
	"LE_ERRNO",
};

void lisp_scope_bind(lisp_scope *scope, lisp_symbol *symbol, lisp_value *value)


@@ 181,7 183,7 @@ int lisp_get_args(lisp_runtime *rt, lisp_list *list, char *format, ...)
		type = lisp_get_type(*format);
		if (type != NULL && type != list->left->type) {
			rt->error = "incorrect argument type";
			rt->errno = LE_TYPE;
			rt->err_num = LE_TYPE;
			return 0;
		}
		*v = list->left;


@@ 190,11 192,11 @@ int lisp_get_args(lisp_runtime *rt, lisp_list *list, char *format, ...)
	}
	if (*format != '\0') {
		rt->error = "not enough arguments";
		rt->errno = LE_2FEW;
		rt->err_num = LE_2FEW;
		return 0;
	} else if(!lisp_nil_p((lisp_value*)list)) {
		rt->error = "too many arguments";
		rt->errno = LE_2MANY;
		rt->err_num = LE_2MANY;
		return 0;
	}
	return 1;


@@ 385,10 387,10 @@ void lisp_dump_stack(lisp_runtime *rt, lisp_list *stack, FILE *file)
	}
}

lisp_value *lisp_error(lisp_runtime *rt, enum lisp_errno errno, char *message)
lisp_value *lisp_error(lisp_runtime *rt, enum lisp_errno err_num, char *message)
{
	rt->error = message;
	rt->errno = errno;
	rt->err_num = err_num;
	rt->error_stack = rt->stack;
	return NULL;
}


@@ 400,7 402,7 @@ char *lisp_get_error(lisp_runtime *rt)

enum lisp_errno lisp_get_errno(lisp_runtime *rt)
{
	return rt->errno;
	return rt->err_num;
}

void lisp_clear_error(lisp_runtime *rt)


@@ 408,11 410,12 @@ void lisp_clear_error(lisp_runtime *rt)
	rt->error = NULL;
	rt->error_stack = NULL;
	rt->error_line = 0;
	rt->errno = 0;
	rt->err_num = 0;
}

void lisp_print_error(lisp_runtime *rt, FILE *file)
{
	char *errmsg;
	if (!rt->error) {
		fprintf(stderr, "BUG: lisp_print_error() expects error, found none\n");
		return;


@@ 421,7 424,13 @@ void lisp_print_error(lisp_runtime *rt, FILE *file)
	if (rt->error_line)
		fprintf(file, "at line %d: ", rt->error_line);

	fprintf(file, "Error %s: %s\n", lisp_error_name[rt->errno], rt->error);
	if (rt->err_num == LE_ERRNO) {
		errmsg = strerror(errno);
		fprintf(file, "Error %s: %s\nSystem error: %s\n",
			lisp_error_name[rt->err_num], rt->error, errmsg);
	} else {
		fprintf(file, "Error %s: %s\n", lisp_error_name[rt->err_num], rt->error);
	}

	if (rt->error_stack)
		lisp_dump_stack(rt, rt->error_stack, file);