~brenns10/funlisp

e967c46715767ec8cb6efaa0aa10697c765d04ec — Stephen Brennan 3 years ago eb93130
We now have a library
6 files changed, 232 insertions(+), 62 deletions(-)

M Makefile
M depend
A inc/funlisp.h
M src/funlisp_internal.h
A src/public_util.c
R src/main.c => tools/repl.c
M Makefile => Makefile +7 -1
@@ 20,13 20,19 @@ all: bin/libfunlisp.a
obj/%.o: src/%.c
	$(CC) $(CFLAGS) -c $< -o $@

obj/%.o: tools/%.c
	$(CC) $(CFLAGS) -c $< -o $@

bin/libfunlisp.a: $(OBJS)
	ar rcs $@ $^

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

clean:
	rm -rf obj/* bin/*

depend: $(SRCS) src/*.h inc/*.h
depend: $(SRCS) src/*.h inc/*.h tools/*.c
	$(CC) $(CFLAGS) -MM obj $(SRCS) | sed 's!^\(.*\):!obj/\1:!' > depend

include depend

M depend => depend +10 -10
@@ 1,14 1,14 @@
obj/iter.o: src/iter.c src/iter.h
obj/types.o: src/types.c src/funlisp_internal.h src/iter.h src/ringbuf.h \
 src/hashtable.h
obj/types.o: src/types.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h
obj/charbuf.o: src/charbuf.c src/charbuf.h
obj/main.o: src/main.c src/funlisp_internal.h src/iter.h src/ringbuf.h \
 src/hashtable.h
obj/parse.o: src/parse.c src/funlisp_internal.h src/iter.h src/ringbuf.h \
 src/hashtable.h src/charbuf.h
obj/util.o: src/util.c src/funlisp_internal.h src/iter.h src/ringbuf.h \
 src/hashtable.h
obj/parse.o: src/parse.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h src/charbuf.h
obj/util.o: src/util.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h
obj/public_util.o: src/public_util.c src/funlisp_internal.h inc/funlisp.h \
 src/iter.h src/ringbuf.h src/hashtable.h
obj/hashtable.o: src/hashtable.c src/iter.h src/hashtable.h
obj/gc.o: src/gc.c src/funlisp_internal.h src/iter.h src/ringbuf.h \
 src/hashtable.h
obj/gc.o: src/gc.c src/funlisp_internal.h inc/funlisp.h src/iter.h \
 src/ringbuf.h src/hashtable.h
obj/ringbuf.o: src/ringbuf.c src/ringbuf.h

A inc/funlisp.h => inc/funlisp.h +136 -0
@@ 0,0 1,136 @@
/*
 * funlisp.h: public funlisp interface
 *
 * Stephen Brennan <stephen@brennan.io>
 */

#ifndef _FUNLISP_H
#define _FUNLISP_H

#include <stdio.h> /* for FILE* */

/*
 * A runtime object -- you need one to execute any code. Note the typedef.
 * Although I generally prefer no struct typedefs, these names are long enough
 * to merit it.
 */
typedef struct lisp_runtime lisp_runtime;

/*
 * Use these functions to create and free a runtime. You must use
 * lisp_runtime_free() to cleanup every runtime you create.
 */
lisp_runtime *lisp_runtime_new(void);
void lisp_runtime_free(lisp_runtime *);

/*
 * In funlisp, (almost) everything is a lisp_value -- that is, can be cast to a
 * lisp_value * and operated on. Integers, Strings, Code, etc. The only thing
 * which is not a lisp_value is the lisp_runtime.
 */
typedef struct lisp_value {
	struct lisp_type  *type; /* type object, USE ME TO TYPE CHECK */
	struct lisp_value *next; /* don't touch me */
	char mark;               /* don't touch me */
} lisp_value;

/*
 * Here are the lisp_values out there:
 */
typedef struct lisp_type lisp_type;
typedef struct lisp_scope lisp_scope;
typedef struct lisp_list lisp_list;
typedef struct lisp_symbol lisp_symbol;
typedef struct lisp_error lisp_error;
typedef struct lisp_integer lisp_integer;
typedef struct lisp_string lisp_string;
typedef struct lisp_builtin lisp_builtin;
typedef struct lisp_lambda lisp_lambda;

/*
 * Each one has an associated "type" object (which you saw above):
 * You can compare the value->type to these, in order to see what type object
 * you have.
 */
extern lisp_type *type_type;
extern lisp_type *type_scope;
extern lisp_type *type_list;
extern lisp_type *type_symbol;
extern lisp_type *type_error;
extern lisp_type *type_integer;
extern lisp_type *type_string;
extern lisp_type *type_builtin;
extern lisp_type *type_lambda;

/*
 * No matter what type you have, it can be printed. This does NOT print a
 * trailing newline, so as to give you the most flexibility.
 */
void lisp_print(FILE *f, lisp_value *value);

/*
 * Since lisp_values can be code, they can be evaluated on a runtime, in a given
 * scope. They, of course, return values.
 */
lisp_value *lisp_eval(lisp_runtime *rt, lisp_scope *scope, lisp_value *value);

/*
 * You get lisp_value's by parsing strings (or FILEs, TODO).
 */
lisp_value *lisp_parse(lisp_runtime *rt, char *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.
 */
lisp_scope *lisp_new_default_scope(lisp_runtime *rt);

/*
 * You can also get a new, empty scope with:
 */
lisp_scope *lisp_new_empty_scope(lisp_runtime *rt);

/*
 * With the above, you should be able to infer what basic use should look like:
 *
 *   lisp_runtime *rt = lisp_runtime_new();
 *   lisp_scope *scope = lisp_new_default_scope(rt);
 *   lisp_value *parsed = lisp_parse(rt, my_cool_code);
 *   lisp_value *result = lisp_eval(rt, scope, parsed);
 *   lisp_print(stdout, result);
 *
 * However, we're missing the memory management stuff here. How does it all get
 * cleaned up?
 *
 * Funlisp uses a mark-and-sweep garbage collector. It cleverly avoids the
 * cycles in data structures (very common in lisp), and frees up unused
 * lisp_values. However, it does need your help.
 */

/*
 * Each time you want to invoke the garbage collector, you should "mark" all the
 * objects you're interested in keeping. Marking an object marks every object
 * you can access from it, using a breadth-first search (so it could be a quite
 * expensive operation). This makes all the objects safe from the following
 * "sweep" operation, which frees all unmarked objects.
 *
 * The normal, correct think to do after evaluating some code, is to mark the
 * scope. Anything still there should be kept. Anything else can probably be
 * cleaned up.
 *
 * NOTE: be explicit about marking. If you do the following:
 *   lisp_value *result = lisp_eval(rt, scope, some_cool_code);
 *   lisp_mark((lisp_value*)scope);
 * There is a very good chance that `result` will be freed on the next sweep.
 * Yeah.
 */
void lisp_mark(lisp_runtime *rt, lisp_value *v);

/*
 * Now, once you've marked your scope (and any other values you're interested
 * in), sweep everything away!
 */
void lisp_sweep(lisp_runtime *rt);

#endif

M src/funlisp_internal.h => src/funlisp_internal.h +24 -39
@@ 10,6 10,7 @@
#include <stdbool.h>
#include <stdio.h>

#include "funlisp.h"
#include "iter.h"
#include "ringbuf.h"
#include "hashtable.h"


@@ 24,13 25,12 @@
	char mark                       \


/* Type declarations. */
typedef struct lisp_value {
	LISP_VALUE_HEAD;
} lisp_value;
/*
 * Type declarations. See funlisp.h for lisp_value.
 */

/* A lisp_runtime is NOT a lisp_value! */
typedef struct {
struct lisp_runtime {
	lisp_value *head;
	lisp_value *tail;



@@ 38,22 38,22 @@ typedef struct {
	lisp_value *nil;

	struct ringbuf rb;
} lisp_runtime;
};

/* The below ARE lisp_values! */
typedef struct lisp_scope {
struct lisp_scope {
	LISP_VALUE_HEAD;
	struct hashtable scope;
	struct lisp_scope *up;
} lisp_scope;
};

typedef struct {
struct lisp_list {
	LISP_VALUE_HEAD;
	lisp_value *left;
	lisp_value *right;
} lisp_list;
};

typedef struct lisp_type {
struct lisp_type {
	LISP_VALUE_HEAD;
	const char *name;
	void (*print)(FILE *f, lisp_value *value);


@@ 62,52 62,48 @@ typedef struct lisp_type {
	struct iterator (*expand)(lisp_value*);
	lisp_value * (*eval)(lisp_runtime *rt, lisp_scope *scope, lisp_value *value);
	lisp_value * (*call)(lisp_runtime *rt, lisp_scope *scope, lisp_value *callable, lisp_value *arg);
} lisp_type;
};

typedef struct {
struct lisp_symbol {
	LISP_VALUE_HEAD;
	char *sym;
} lisp_symbol;
};

typedef struct {
struct lisp_error {
	LISP_VALUE_HEAD;
	char *message;
} lisp_error;
};

typedef struct {
struct lisp_integer {
	LISP_VALUE_HEAD;
	int x;
} lisp_integer;
};

typedef struct {
struct lisp_string {
	LISP_VALUE_HEAD;
	char *s;
} lisp_string;
};

typedef lisp_value * (*lisp_builtin_func)(lisp_runtime*, lisp_scope*,lisp_value*);
typedef struct {
struct lisp_builtin {
	LISP_VALUE_HEAD;
	lisp_builtin_func call;
	char *name;
} lisp_builtin;
};

typedef struct {
struct lisp_lambda {
	LISP_VALUE_HEAD;
	lisp_list *args;
	lisp_value *code;
	lisp_scope *closure;
} lisp_lambda;
};

/* Interpreter stuff */
void lisp_init(lisp_runtime *rt);
void lisp_mark(lisp_runtime *rt, lisp_value *v);
void lisp_sweep(lisp_runtime *rt);
void lisp_destroy(lisp_runtime *rt);

/* Shortcuts for type operations. */
void lisp_print(FILE *f, lisp_value *value);
void lisp_free(lisp_value *value);
lisp_value *lisp_eval(lisp_runtime *rt, lisp_scope *scope, lisp_value *value);
lisp_value *lisp_call(lisp_runtime *rt, lisp_scope *scope, lisp_value *callable,
                      lisp_value *arguments);
lisp_value *lisp_new(lisp_runtime *rt, lisp_type *typ);


@@ 126,21 122,10 @@ lisp_value *lisp_scope_lookup(lisp_runtime *rt, lisp_scope *scope,
void lisp_scope_add_builtin(lisp_runtime *rt, lisp_scope *scope, char *name, lisp_builtin_func call);
void lisp_scope_populate_builtins(lisp_runtime *rt, lisp_scope *scope);
lisp_value *lisp_eval_list(lisp_runtime *rt, lisp_scope *scope, lisp_value *list);
lisp_value *lisp_parse(lisp_runtime *rt, char *input);
bool lisp_get_args(lisp_list *list, char *format, ...);
lisp_value *lisp_quote(lisp_runtime *rt, lisp_value *value);
/* List functions */
int lisp_list_length(lisp_list *list);
bool lisp_nil_p(lisp_value *l);

extern lisp_type *type_type;
extern lisp_type *type_scope;
extern lisp_type *type_list;
extern lisp_type *type_symbol;
extern lisp_type *type_error;
extern lisp_type *type_integer;
extern lisp_type *type_string;
extern lisp_type *type_builtin;
extern lisp_type *type_lambda;

#endif

A src/public_util.c => src/public_util.c +38 -0
@@ 0,0 1,38 @@
/*
 * public_util.c: public utility function implementations
 *
 * Contains implementations of simple functions that are only useful to people
 * that aren't peeping into the undefined world of "funlisp_internal.h".
 *
 * Stephen Brennan <stephen@brennan.io>
 */

#include <stdlib.h>

#include "funlisp_internal.h"

lisp_runtime *lisp_runtime_new(void)
{
	lisp_runtime *rt = malloc(sizeof(lisp_runtime));
	lisp_init(rt);
	return rt;
}

void lisp_runtime_free(lisp_runtime *rt)
{
	lisp_destroy(rt);
	free(rt);
}


lisp_scope *lisp_new_empty_scope(lisp_runtime *rt)
{
	return (lisp_scope *)lisp_new(rt, type_scope);
}

lisp_scope *lisp_new_default_scope(lisp_runtime *rt)
{
	lisp_scope *scope = lisp_new_empty_scope(rt);
	lisp_scope_populate_builtins(rt, scope);
	return scope;
}

R src/main.c => tools/repl.c +17 -12
@@ 1,33 1,38 @@
/*
 * repl.c: A simple read-eval-print loop for funlisp
 *
 * Stephen Brennan <stephen@brennan.io>
 */

#include <editline/readline.h>
#include <stdio.h>
#include <stdlib.h>

#include "funlisp_internal.h"
#include "funlisp.h"

int main(int argc, char **argv)
{
	(void)argc;
	(void)argc; /* unused parameters */
	(void)argv;
	lisp_runtime rt;
	lisp_init(&rt);
	lisp_scope *scope = (lisp_scope*)lisp_new(&rt, type_scope);
	lisp_scope_populate_builtins(&rt, scope);

	while (true) {
	lisp_runtime *rt = lisp_runtime_new();
	lisp_scope *scope = lisp_new_default_scope(rt);

	for (;;) {
		char *input = readline("> ");
		if (input == NULL) {
			break;
		}
		lisp_value *value = lisp_parse(&rt, input);
		lisp_value *value = lisp_parse(rt, input);
		add_history(input);
		free(input);
		lisp_value *result = lisp_eval(&rt, scope, value);
		lisp_value *result = lisp_eval(rt, scope, value);
		lisp_print(stdout, result);
		fprintf(stdout, "\n");
		lisp_mark(&rt, (lisp_value*)scope);
		lisp_sweep(&rt);
		lisp_mark(rt, (lisp_value*)scope);
		lisp_sweep(rt);
	}

	lisp_destroy(&rt);
	lisp_runtime_free(rt);
	return 0;
}