~brenns10/funlisp

39f61514cf684485ba5f2a5c6f1f052709a20f8b — Stephen Brennan 3 years ago ed6d970
Run basic scripts
6 files changed, 185 insertions(+), 1 deletions(-)

M Makefile
M inc/funlisp.h
M src/parse.c
M src/public_util.c
M src/util.c
A tools/runfile.c
M Makefile => Makefile +3 -0
@@ 29,6 29,9 @@ bin/libfunlisp.a: $(OBJS)
bin/repl: obj/repl.o bin/libfunlisp.a
	$(CC) -ledit $^ -o $@

bin/runfile:  obj/runfile.o bin/libfunlisp.a
	$(CC) $^ -o $@

clean:
	rm -rf obj/* bin/*


M inc/funlisp.h => inc/funlisp.h +35 -1
@@ 75,11 75,22 @@ void lisp_print(FILE *f, lisp_value *value);
lisp_value *lisp_eval(lisp_runtime *rt, lisp_scope *scope, lisp_value *value);

/*
 * You get lisp_value's by parsing strings (or FILEs, TODO).
 * You get lisp_value's by parsing strings. This parses exactly one lisp
 * expression out of a string and returns the code object, which needs to be
 * evaluated. Will return NULL if there is no expression in the string.
 */
lisp_value *lisp_parse(lisp_runtime *rt, char *input);

/*
 * Using files is a bit more straightforward. Loading a file will parse and
 * execute each expression in the file, returning the result of evaluating the
 * last expression (which could be NULL if there was no code).  You don't need
 * to eval the resulting object (it has already been evaluated).  Typically, you
 * will want to run some callback though (e.g. a main function).
 */
lisp_value *lisp_load_file(lisp_runtime *rt, lisp_scope *scope, FILE *input);

/*
 * To do most of this stuff, you need a scope! Thankfully, you can get a new,
 * default scope (containing the language default builtins) quite easily with
 * this function.


@@ 133,4 144,27 @@ void lisp_mark(lisp_runtime *rt, lisp_value *v);
 */
void lisp_sweep(lisp_runtime *rt);


/* UTILITIES */

/*
 * Convert the array of strings into a lisp list of string objects.
 * list: an array of strings
 * n: length of the array
 * can_free: Does the interpreter take ownership of the memory pointed at by
 *   the strings? If so, can_free should be non-zero. If not, it should be 0.
 */
lisp_value *lisp_list_of_strings(lisp_runtime *rt, char **list, size_t n, char can_free);

/*
 * Given a lisp_value, put it inside a list of size 0 and return it.
 */
lisp_value *lisp_singleton_list(lisp_runtime *rt, lisp_value *entry);

/*
 * Run the main function, if it exists. Otherwise, return NULL.
 */
lisp_value *lisp_run_main_if_exists(lisp_runtime *rt, lisp_scope *scope,
                                    int argc, char **argv);

#endif

M src/parse.c => src/parse.c +47 -0
@@ 160,3 160,50 @@ lisp_value *lisp_parse(lisp_runtime *rt, char *input)
{
	return lisp_parse_value(rt, input, 0).result;
}

static char *read_file(FILE *input)
{
	size_t bufsize = 1024;
	size_t length = 0;
	char *buf = malloc(1024);

	while (!feof(input) && !ferror(input)) {
		length += fread(buf, sizeof(char), bufsize - length, input);
		if (length >= bufsize) {
			bufsize *= 2;
			buf = realloc(buf, bufsize);
		}
	}

	if (feof(input)) {
		return buf;
	} else {
		free(buf);
		return NULL;
	}
}

lisp_value *lisp_load_file(lisp_runtime *rt, lisp_scope *scope, FILE *input)
{
	char *contents = read_file(input);
	lisp_value *last_val = NULL;
	result r;
	r.index = 0;
	r.result = NULL;

	if (!contents)
		return NULL;

	for (;;) {
		r = lisp_parse_value(rt, contents, r.index);
		if (!r.result) {
			lisp_mark(rt, (lisp_value *) scope);
			if (last_val)
				lisp_mark(rt, last_val);
			lisp_sweep(rt);
			free(contents);
			return last_val;
		}
		last_val = lisp_eval(rt, scope, r.result);
	}
}

M src/public_util.c => src/public_util.c +20 -0
@@ 36,3 36,23 @@ lisp_scope *lisp_new_default_scope(lisp_runtime *rt)
	lisp_scope_populate_builtins(rt, scope);
	return scope;
}

lisp_value *lisp_run_main_if_exists(lisp_runtime *rt, lisp_scope *scope,
                                    int argc, char **argv)
{
	lisp_value *args;
	lisp_value *main_func = lisp_scope_lookup(
		rt, scope, lisp_symbol_new(rt, "main"));

	if (main_func->type == type_error) {
		return NULL;
	}
	lisp_print(stdout, main_func);

	args = lisp_list_of_strings(rt, argv, argc, 0);
	args = lisp_quote(rt, args);
	args = lisp_singleton_list(rt, args);
	lisp_print(stdout, args);
	printf("\n");
	return lisp_call(rt, scope, main_func, args);
}

M src/util.c => src/util.c +35 -0
@@ 183,6 183,41 @@ bool lisp_get_args(lisp_list *list, char *format, ...)
	return true;
}

lisp_value *lisp_list_of_strings(lisp_runtime *rt, char **list, size_t n, char can_free)
{
	size_t i;
	lisp_list *rv, *l;
	lisp_string *s;

	if (n == 0)
		return lisp_nil_new(rt);

	rv = (lisp_list*) lisp_new(rt, type_list);
	l = rv;

	for (i = 0; i < n; i++) {
		s = (lisp_string *) lisp_new(rt, type_string);
		s->s = list[i];
		s->can_free = can_free;
		l->left = (lisp_value *) s;

		l->right = lisp_new(rt, type_list);
		l = (lisp_list *) l->right;
	}

	l->right = lisp_nil_new(rt);

	return (lisp_value *) rv;
}

lisp_value *lisp_singleton_list(lisp_runtime *rt, lisp_value *entry)
{
	lisp_list *l = (lisp_list *) lisp_new(rt, type_list);
	l->left = entry;
	l->right = lisp_nil_new(rt);
	return (lisp_value *) l;
}

static lisp_value *lisp_builtin_eval(lisp_runtime *rt, lisp_scope *scope,
                                     lisp_value *arguments)
{

A tools/runfile.c => tools/runfile.c +45 -0
@@ 0,0 1,45 @@
/*
 * runfile.c: Run a text file containing lisp code
 *
 * Stephen Brennan <stephen@brennan.io>
 */

#include <stdio.h>
#include <stdlib.h>

#include <funlisp.h>


int main(int argc, char **argv)
{
	FILE *input;
	lisp_runtime *rt;
	lisp_scope *scope;
	lisp_value *result;
	int rv;

	if (argc < 2) {
		fprintf(stderr, "error: expected at least one argument\n");
		return EXIT_FAILURE;
	}

	input = fopen(argv[1], "r");
	if (!input) {
		perror("open");
		return EXIT_FAILURE;
	}

	rt = lisp_runtime_new();
	scope = lisp_new_default_scope(rt);

	lisp_load_file(rt, scope, input);
	fclose(input);

	result = lisp_run_main_if_exists(rt, scope, argc - 2, argv + 2);
	if (result && result->type == type_error)
		rv = 1;
	else
		rv = 0;
	lisp_runtime_free(rt); /* sweeps everything before exit */
	return rv;
}