Add basic lexenv support
This commit is contained in:
@ -3,6 +3,7 @@
|
|||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
#include "hashtable.h"
|
#include "hashtable.h"
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
|
#include "list.h"
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -75,7 +76,7 @@ DEFUN(id, "id", (LispVal * obj), "(id)", "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(eq, "eq", (LispVal * obj1, LispVal *obj2), "(obj1 obj2)", "") {
|
DEFUN(eq, "eq", (LispVal * obj1, LispVal *obj2), "(obj1 obj2)", "") {
|
||||||
return obj1 == obj2 ? Qt : Qnil;
|
return EQ(obj1, obj2) ? Qt : Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFSPECIAL(quote, "quote", (LispVal * form), "(form)", "") {
|
DEFSPECIAL(quote, "quote", (LispVal * form), "(form)", "") {
|
||||||
|
|||||||
@ -282,6 +282,10 @@ static ALWAYS_INLINE bool NILP(LispVal *val) {
|
|||||||
return val == Qnil;
|
return val == Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE bool EQ(LispVal *val1, LispVal *val2) {
|
||||||
|
return val1 == val2;
|
||||||
|
}
|
||||||
|
|
||||||
// Some core functions
|
// Some core functions
|
||||||
DECLARE_FUNCTION(id, (LispVal * obj));
|
DECLARE_FUNCTION(id, (LispVal * obj));
|
||||||
DECLARE_FUNCTION(eq, (LispVal * obj1, LispVal *obj2));
|
DECLARE_FUNCTION(eq, (LispVal * obj1, LispVal *obj2));
|
||||||
|
|||||||
@ -106,7 +106,7 @@ void parse_lambda_list(LambdaListParseResult *result, LispVal *list) {
|
|||||||
out->kw = Qnil;
|
out->kw = Qnil;
|
||||||
out->rest = Qnil;
|
out->rest = Qnil;
|
||||||
size_t cur_idx = 0; // for keyword args
|
size_t cur_idx = 0; // for keyword args
|
||||||
FOREACH_TAIL(list, tail) {
|
DOTAILS(tail, list) {
|
||||||
if (!LISTP(tail)) {
|
if (!LISTP(tail)) {
|
||||||
RETURN_ERROR(LLPS_DOTTED, list);
|
RETURN_ERROR(LLPS_DOTTED, list);
|
||||||
} else if (out->allow_other_keys) {
|
} else if (out->allow_other_keys) {
|
||||||
@ -178,7 +178,7 @@ void parse_lambda_list(LambdaListParseResult *result, LispVal *list) {
|
|||||||
}
|
}
|
||||||
#undef RETURN_ERROR
|
#undef RETURN_ERROR
|
||||||
|
|
||||||
LispVal *make_builtin_function(LispVal *name, LispVal *(*cfunc)(),
|
LispVal *make_builtin_function(LispVal *name, LispVal *(*cfunc)(void),
|
||||||
const char *lisp_args, size_t args_len,
|
const char *lisp_args, size_t args_len,
|
||||||
LispVal *docstr) {
|
LispVal *docstr) {
|
||||||
LispFunction *obj = lisp_alloc_object(sizeof(LispFunction), TYPE_FUNCTION);
|
LispFunction *obj = lisp_alloc_object(sizeof(LispFunction), TYPE_FUNCTION);
|
||||||
@ -230,11 +230,11 @@ call_simple_native(LispVal *orig_func, LispFunction *fobj, LispVal *args) {
|
|||||||
}
|
}
|
||||||
LispVal *arg_arr[MAX_NATIVE_FUNCTION_ARGS];
|
LispVal *arg_arr[MAX_NATIVE_FUNCTION_ARGS];
|
||||||
size_t acount = 0;
|
size_t acount = 0;
|
||||||
FOREACH(args, arg) {
|
DOLIST(arg, args) {
|
||||||
if (fobj->flags.no_eval_args) {
|
if (fobj->flags.no_eval_args) {
|
||||||
arg_arr[acount] = arg;
|
arg_arr[acount] = arg;
|
||||||
} else {
|
} else {
|
||||||
arg_arr[acount] = Feval(arg);
|
arg_arr[acount] = Feval(arg, PARENT_LEXENV());
|
||||||
}
|
}
|
||||||
add_local_reference(arg_arr[acount++]);
|
add_local_reference(arg_arr[acount++]);
|
||||||
}
|
}
|
||||||
@ -361,7 +361,7 @@ call_complex_native(LispVal *orig_func, LispFunction *fobj, LispVal *args) {
|
|||||||
if (!arg_arr[i]) {
|
if (!arg_arr[i]) {
|
||||||
arg_arr[i] = Qnil;
|
arg_arr[i] = Qnil;
|
||||||
} else if (!fobj->flags.no_eval_args) {
|
} else if (!fobj->flags.no_eval_args) {
|
||||||
arg_arr[i] = Feval(arg_arr[i]);
|
arg_arr[i] = Feval(arg_arr[i], PARENT_LEXENV());
|
||||||
}
|
}
|
||||||
add_local_reference(arg_arr[i]);
|
add_local_reference(arg_arr[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
#define INCLUDED_FUNCTION_H
|
#define INCLUDED_FUNCTION_H
|
||||||
|
|
||||||
#include "base.h"
|
#include "base.h"
|
||||||
#include "lisp_string.h"
|
|
||||||
|
|
||||||
DECLARE_SYMBOL(and_optional);
|
DECLARE_SYMBOL(and_optional);
|
||||||
DECLARE_SYMBOL(and_rest);
|
DECLARE_SYMBOL(and_rest);
|
||||||
|
|||||||
@ -56,7 +56,7 @@ static uintptr_t hash_key_for_table(LispHashTable *ht, LispVal *key) {
|
|||||||
|
|
||||||
static bool compare_keys(LispHashTable *ht, LispVal *key1, LispVal *key2) {
|
static bool compare_keys(LispHashTable *ht, LispVal *key1, LispVal *key2) {
|
||||||
if (NILP(ht->eq_fn) || ht->eq_fn == Qeq) {
|
if (NILP(ht->eq_fn) || ht->eq_fn == Qeq) {
|
||||||
return key1 == key2;
|
return EQ(key1, key2);
|
||||||
} else if (ht->eq_fn == Qstrings_equal) { // needed for initialization
|
} else if (ht->eq_fn == Qstrings_equal) { // needed for initialization
|
||||||
return !NILP(Fstrings_equal(key1, key2));
|
return !NILP(Fstrings_equal(key1, key2));
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/lisp.c
61
src/lisp.c
@ -57,7 +57,23 @@ void lisp_shutdown(void) {
|
|||||||
lisp_teardown_stack();
|
lisp_teardown_stack();
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(eval, "eval", (LispVal * form), "(form)", "") {
|
static inline LispVal *lookup_variable(LispSymbol *name, LispVal *lexenv) {
|
||||||
|
LispVal *lexval = Fplist_get(lexenv, name, Qunbound);
|
||||||
|
if (lexval != Qunbound) {
|
||||||
|
return lexval;
|
||||||
|
}
|
||||||
|
if (name->value == Qunbound) {
|
||||||
|
// TODO better error
|
||||||
|
printf("Unbound symbol: ");
|
||||||
|
debug_print(stdout, name);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return name->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(eval, "eval", (LispVal * form, LispVal *lexenv),
|
||||||
|
"(form &optional lexenv)", "") {
|
||||||
if (!OBJECTP(form)) {
|
if (!OBJECTP(form)) {
|
||||||
// fixnum or float
|
// fixnum or float
|
||||||
return form;
|
return form;
|
||||||
@ -75,21 +91,12 @@ DEFUN(eval, "eval", (LispVal * form), "(form)", "") {
|
|||||||
out_data[i] = Qnil;
|
out_data[i] = Qnil;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < vec->length; ++i) {
|
for (size_t i = 0; i < vec->length; ++i) {
|
||||||
out_data[i] = Feval(vec->data[i]);
|
out_data[i] = Feval(vec->data[i], lexenv);
|
||||||
}
|
}
|
||||||
return newvec;
|
return newvec;
|
||||||
}
|
}
|
||||||
case TYPE_SYMBOL: {
|
case TYPE_SYMBOL:
|
||||||
// TODO local bindings
|
return lookup_variable(form, lexenv);
|
||||||
LispSymbol *sym = form;
|
|
||||||
if (sym->value == Qunbound) {
|
|
||||||
printf("Unbound symbol: ");
|
|
||||||
debug_print(stdout, form);
|
|
||||||
fputc('\n', stdout);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return sym->value;
|
|
||||||
}
|
|
||||||
case TYPE_CONS: {
|
case TYPE_CONS: {
|
||||||
return Ffuncall(XCAR(form), XCDR(form));
|
return Ffuncall(XCAR(form), XCDR(form));
|
||||||
}
|
}
|
||||||
@ -131,7 +138,7 @@ void debug_print(FILE *file, LispVal *obj) {
|
|||||||
}
|
}
|
||||||
case TYPE_CONS: {
|
case TYPE_CONS: {
|
||||||
fputc('(', file);
|
fputc('(', file);
|
||||||
FOREACH_TAIL(obj, tail) {
|
DOTAILS(tail, obj) {
|
||||||
if (CONSP(tail)) {
|
if (CONSP(tail)) {
|
||||||
debug_print(file, XCAR(tail));
|
debug_print(file, XCAR(tail));
|
||||||
if (!NILP(XCDR(tail))) {
|
if (!NILP(XCDR(tail))) {
|
||||||
@ -162,6 +169,32 @@ void debug_print(FILE *file, LispVal *obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFSPECIAL(progn, "progn", (LispVal * forms), "(&rest forms)", "") {
|
||||||
|
LispVal *rval = Qnil;
|
||||||
|
DOLIST(form, forms) {
|
||||||
|
rval = Feval(form, TOP_LEXENV());
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFSPECIAL(let, "let", (LispVal * bindings, LispVal *body),
|
||||||
|
"(bindings &rest body)", "") {
|
||||||
|
CHECK_LISTP(bindings);
|
||||||
|
copy_parent_lexenv();
|
||||||
|
DOLIST(binding, bindings) {
|
||||||
|
if (SYMBOLP(binding)) {
|
||||||
|
new_lexical_variable(binding, Qnil);
|
||||||
|
} else if (CONSP(binding) && list_length_eq(binding, 2)) {
|
||||||
|
new_lexical_variable(FIRST(binding),
|
||||||
|
Feval(SECOND(binding), TOP_LEXENV()));
|
||||||
|
} else {
|
||||||
|
// TODO better error
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Fprogn(body);
|
||||||
|
}
|
||||||
|
|
||||||
void debug_obj_info(FILE *file, LispVal *obj) {
|
void debug_obj_info(FILE *file, LispVal *obj) {
|
||||||
fprintf(file, "%s -> ", LISP_TYPE_NAMES[TYPE_OF(obj)]);
|
fprintf(file, "%s -> ", LISP_TYPE_NAMES[TYPE_OF(obj)]);
|
||||||
debug_print(file, obj);
|
debug_print(file, obj);
|
||||||
|
|||||||
@ -16,7 +16,9 @@ void lisp_init(void);
|
|||||||
|
|
||||||
void lisp_shutdown(void);
|
void lisp_shutdown(void);
|
||||||
|
|
||||||
DECLARE_FUNCTION(eval, (LispVal * form));
|
DECLARE_FUNCTION(eval, (LispVal * form, LispVal *lexenv));
|
||||||
|
DECLARE_FUNCTION(progn, (LispVal * forms));
|
||||||
|
DECLARE_FUNCTION(let, (LispVal * bindings, LispVal *body));
|
||||||
|
|
||||||
__attribute__((no_sanitize("address"))) void debug_print(FILE *file,
|
__attribute__((no_sanitize("address"))) void debug_print(FILE *file,
|
||||||
LispVal *obj);
|
LispVal *obj);
|
||||||
|
|||||||
23
src/list.c
23
src/list.c
@ -60,3 +60,26 @@ DEFUN(listp, "listp", (LispVal * obj), "(obj)", "") {
|
|||||||
DEFUN(list, "list", (LispVal * args), "(&rest args)", "") {
|
DEFUN(list, "list", (LispVal * args), "(&rest args)", "") {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(plist_put, "plist-put", (LispVal * plist, LispVal *prop, LispVal *value),
|
||||||
|
"(plist prop value)", "") {
|
||||||
|
CHECK_LISTP(plist);
|
||||||
|
DOTAILS(rest, plist) {
|
||||||
|
if (EQ(XCAR(rest), prop)) {
|
||||||
|
RPLACA(XCDR(rest), value);
|
||||||
|
return plist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CONS(prop, CONS(value, plist));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN(plist_get, "plist-get", (LispVal * plist, LispVal *prop, LispVal *def),
|
||||||
|
"(plist prop &optional default)", "") {
|
||||||
|
CHECK_LISTP(plist);
|
||||||
|
DOTAILS(rest, plist) {
|
||||||
|
if (EQ(XCAR(rest), prop)) {
|
||||||
|
return SECOND(rest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|||||||
@ -99,11 +99,11 @@ static ALWAYS_INLINE LispVal *LIST_N(int count, ...) {
|
|||||||
#define FOURTH(x) XCAR(XCDR(XCDR(XCDR(x))))
|
#define FOURTH(x) XCAR(XCDR(XCDR(XCDR(x))))
|
||||||
#define FIFTH(x) XCAR(XCDR(XCDR(XCDR(XCDR(x)))))
|
#define FIFTH(x) XCAR(XCDR(XCDR(XCDR(XCDR(x)))))
|
||||||
|
|
||||||
#define FOREACH(l, v) \
|
#define DOLIST(v, l) \
|
||||||
for (LispVal *_tail = (l), *v = XCAR(_tail); !NILP(_tail); \
|
for (LispVal *_tail = (l), *v = XCAR(_tail); !NILP(_tail); \
|
||||||
_tail = XCDR(_tail), v = XCAR(_tail))
|
_tail = XCDR(_tail), v = XCAR(_tail))
|
||||||
|
|
||||||
#define FOREACH_TAIL(l, v) for (LispVal *v = (l); !NILP(v); v = XCDR_SAFE(v))
|
#define DOTAILS(v, l) for (LispVal *v = (l); !NILP(v); v = XCDR_SAFE(v))
|
||||||
|
|
||||||
// return -1 list is circular
|
// return -1 list is circular
|
||||||
intptr_t list_length(LispVal *list);
|
intptr_t list_length(LispVal *list);
|
||||||
@ -122,4 +122,7 @@ static ALWAYS_INLINE void CHECK_LISTP(LispVal *obj) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DECLARE_FUNCTION(plist_put, (LispVal * plist, LispVal *prop, LispVal *value));
|
||||||
|
DECLARE_FUNCTION(plist_get, (LispVal * plist, LispVal *prop, LispVal *def));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
16
src/main.c
16
src/main.c
@ -1,26 +1,20 @@
|
|||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
DEFUN(print, "print", (LispVal * v), "(v)", "") {
|
||||||
|
debug_obj_info(stdout, v);
|
||||||
DEFUN(cool_func, "cool-func", (LispVal * a, LispVal *b), "(a &optional b)",
|
|
||||||
"") {
|
|
||||||
printf("A: ");
|
|
||||||
debug_obj_info(stdout, a);
|
|
||||||
printf("B: ");
|
|
||||||
debug_obj_info(stdout, b);
|
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
lisp_init();
|
lisp_init();
|
||||||
REGISTER_GLOBAL_FUNCTION(cool_func);
|
REGISTER_GLOBAL_FUNCTION(print);
|
||||||
push_stack_frame(Qnil, Qnil, Qnil);
|
push_stack_frame(Qnil, Qnil, Qnil);
|
||||||
ReadStream s;
|
ReadStream s;
|
||||||
const char BUF[] = "(cool-func 1 (cons 1 2))";
|
const char BUF[] = "(let ((a 1)) (print a))";
|
||||||
read_stream_init(&s, BUF, sizeof(BUF) - 1);
|
read_stream_init(&s, BUF, sizeof(BUF) - 1);
|
||||||
LispVal *l = read(&s);
|
LispVal *l = read(&s);
|
||||||
Feval(l);
|
Feval(l, Qnil);
|
||||||
lisp_gc_now(NULL);
|
lisp_gc_now(NULL);
|
||||||
pop_stack_frame();
|
pop_stack_frame();
|
||||||
lisp_shutdown();
|
lisp_shutdown();
|
||||||
|
|||||||
30
src/stack.c
30
src/stack.c
@ -161,7 +161,7 @@ static inline void add_local_refs_for_object_sub_vals(LispVal *seen_objs,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
// ho held refs
|
// no held refs
|
||||||
break;
|
break;
|
||||||
case TYPE_FIXNUM:
|
case TYPE_FIXNUM:
|
||||||
case TYPE_FLOAT:
|
case TYPE_FLOAT:
|
||||||
@ -190,3 +190,31 @@ void compact_stack_frame(struct StackFrame *restrict frame) {
|
|||||||
lisp_realloc(refs->blocks, sizeof(struct LocalReferencesBlock *));
|
lisp_realloc(refs->blocks, sizeof(struct LocalReferencesBlock *));
|
||||||
refs->num_blocks = 1;
|
refs->num_blocks = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool set_lexical_variable(LispVal *name, LispVal *value,
|
||||||
|
bool create_if_absent) {
|
||||||
|
assert(the_stack.depth != 0);
|
||||||
|
DOTAILS(rest, LISP_STACK_TOP()->lexenv) {
|
||||||
|
if (EQ(XCAR(rest), name)) {
|
||||||
|
RPLACA(XCDR(rest), value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (create_if_absent) {
|
||||||
|
new_lexical_variable(name, value);
|
||||||
|
}
|
||||||
|
return create_if_absent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void new_lexical_variable(LispVal *name, LispVal *value) {
|
||||||
|
assert(the_stack.depth != 0);
|
||||||
|
LISP_STACK_TOP()->lexenv =
|
||||||
|
CONS(name, CONS(value, LISP_STACK_TOP()->lexenv));
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_parent_lexenv(void) {
|
||||||
|
assert(the_stack.depth != 0);
|
||||||
|
if (the_stack.depth > 1) {
|
||||||
|
LISP_STACK_TOP()->lexenv = the_stack.frames[the_stack.depth - 2].lexenv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
17
src/stack.h
17
src/stack.h
@ -40,6 +40,15 @@ static ALWAYS_INLINE struct StackFrame *LISP_STACK_TOP(void) {
|
|||||||
return the_stack.depth ? &the_stack.frames[the_stack.depth - 1] : NULL;
|
return the_stack.depth ? &the_stack.frames[the_stack.depth - 1] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE LispVal *TOP_LEXENV(void) {
|
||||||
|
return the_stack.depth ? LISP_STACK_TOP()->lexenv : Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE LispVal *PARENT_LEXENV(void) {
|
||||||
|
return the_stack.depth > 1 ? the_stack.frames[the_stack.depth - 2].lexenv
|
||||||
|
: Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
void lisp_init_stack(void);
|
void lisp_init_stack(void);
|
||||||
void lisp_teardown_stack(void);
|
void lisp_teardown_stack(void);
|
||||||
void push_stack_frame(LispVal *name, LispVal *fobj, LispVal *args);
|
void push_stack_frame(LispVal *name, LispVal *fobj, LispVal *args);
|
||||||
@ -47,6 +56,14 @@ void pop_stack_frame(void);
|
|||||||
void add_local_reference_no_recurse(LispVal *obj);
|
void add_local_reference_no_recurse(LispVal *obj);
|
||||||
void add_local_reference(LispVal *obj);
|
void add_local_reference(LispVal *obj);
|
||||||
|
|
||||||
|
// Return true if successful, false if not found and not created
|
||||||
|
bool set_lexical_variable(LispVal *name, LispVal *value, bool create_if_absent);
|
||||||
|
// Just add a new lexical variable without any checking
|
||||||
|
void new_lexical_variable(LispVal *name, LispVal *value);
|
||||||
|
|
||||||
|
// Copy the previous frame's lexenv to the top of the stack.
|
||||||
|
void copy_parent_lexenv(void);
|
||||||
|
|
||||||
// used by the GC
|
// used by the GC
|
||||||
void compact_stack_frame(struct StackFrame *restrict frame);
|
void compact_stack_frame(struct StackFrame *restrict frame);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user