diff options
Diffstat (limited to 'interp.c')
| -rw-r--r-- | interp.c | 1175 |
1 files changed, 1175 insertions, 0 deletions
diff --git a/interp.c b/interp.c new file mode 100644 index 0000000..0af16de --- /dev/null +++ b/interp.c @@ -0,0 +1,1175 @@ +#include "interp.h" + +#include <stdarg.h> +#include <inttypes.h> + +#include <algds/hash_table.h> +#include <algds/str.h> + +#include "sexp.h" +#include "builtins.h" +#include "primitives.h" +#include "parser.h" +#include "prelude.h" + +#include "vector.h" + +#define BUFSIZE 1024 + +bool SExpRef_eq(SExpRef a, SExpRef b) { + return a.idx == b.idx; +} + +uint64_t SExpRef_hash(SExpRef s) { + // FNV-1a 64-bit hash + uint32_t idx = s.idx; + uint8_t byte0 = idx & 0xff; + uint8_t byte1 = (idx >> 8) & 0xff; + uint8_t byte2 = (idx >> 16) & 0xff; + uint8_t byte3 = (idx >> 24) & 0xff; + uint64_t hash = 14695981039346656037ULL; + hash = hash ^ byte0; + hash = hash * 1099511628211ULL; + hash = hash ^ byte1; + hash = hash * 1099511628211ULL; + hash = hash ^ byte2; + hash = hash * 1099511628211ULL; + hash = hash ^ byte3; + hash = hash * 1099511628211ULL; + return hash; +} + +HASH_TABLE_IMPL(SExpRef, SExpRef); + +#define UNBOUND ((SExpRef){-1}) + +// for wasm +Interp *new_interp() { + Interp *ret = malloc(sizeof(Interp)); + Interp_init(ret); + return ret; +} + +// for wasm +void print_lisp_error(Interp *interp, SExpRef err) { + if (VALTYPE(err) == kErrSignal) { + fprintf(stderr, "Error: %s", REF(err)->str); + } else if (VALTYPE(err) == kExceptionSignal) { + const char *exception_str = lisp_to_string(interp, REF(err)->ret); + fprintf(stderr, "Exception: %s\n", exception_str); + free((void*)exception_str); + } +} + +const char *lisp_stacktrace_to_string(Interp *interp, SExpRef stacktrace) { + str_builder_t sb; + init_str_builder(&sb); + str_builder_append(&sb, "Stacktrace:\n"); + for (SExpRef iter = stacktrace; !NILP(iter); iter = CDR(iter)) { + SExpRef i = CAR(iter); + SExpRef filename = CAR(i); + SExpRef linenum = CADR(i); + SExpRef sym = CADDR(i); + str_builder_append(&sb, " %s:%d %s\n", REF(filename)->str, REF(linenum)->integer, REF(sym)->str); + } + return sb.buf; +} + +void Interp_init(Interp *self) { + self->recursion_depth = 0; + self->gensym_cnt = 42; + self->parser = malloc(sizeof(Parser)); + Parser_init(self->parser); + self->parser->ctx = self; + self->errmsg_buf = malloc(BUFSIZE); + SExpVector_init(&self->objs); + IntVector_init(&self->empty_space); + SExpRef2SExpRefHashTable_init(&self->topbindings); + String2IntHashTable_init(&self->symbols); + int i = 0; + SExp sexp; + sexp.marked = false; + sexp.type = kNilSExp; + SExpVector_push_back(&self->objs, sexp); + self->nil = (SExpRef){i}; i++; + + sexp.type = kEnvSExp; + sexp.env.parent= self->nil; + sexp.env.bindings = self->nil; + SExpVector_push_back(&self->objs, sexp); + self->top_level = (SExpRef){i}; i++; + + sexp.type = kBooleanSExp; + sexp.boolean = true; + SExpVector_push_back(&self->objs, sexp); + self->t= (SExpRef){i}; i++; + + sexp.type = kBooleanSExp; + sexp.boolean = false; + SExpVector_push_back(&self->objs, sexp); + self->f = (SExpRef){i}; i++; + + sexp.type = kEmptySExp; + for (; i < 1024; i++) { + SExpVector_push_back(&self->objs, sexp); + IntVector_push_back(&self->empty_space, i); + } + + self->stack = lisp_cons(self, self->top_level, self->nil); + self->reg = self->nil; + self->stacktrace = self->nil; + + Interp_add_primitive(self, "eval", primitive_eval); + Interp_add_primitive(self, "apply", primitive_apply); + Interp_add_primitive(self, "if", primitive_if); + Interp_add_primitive(self, "cond", primitive_cond); + Interp_add_primitive(self, "while", primitive_while); + Interp_add_primitive(self, "progn", primitive_progn); + Interp_add_primitive(self, "and", primitive_and); + Interp_add_primitive(self, "or", primitive_or); + Interp_add_primitive(self, "let", primitive_let); + Interp_add_primitive(self, "setq", primitive_setq); + Interp_add_primitive(self, "lambda", primitive_lambda); + Interp_add_primitive(self, "function", primitive_function); + Interp_add_primitive(self, "defun", primitive_defun); + Interp_add_primitive(self, "defvar", primitive_defvar); + Interp_add_primitive(self, "defmacro", primitive_defmacro); + Interp_add_primitive(self, "funcall", primitive_funcall); + Interp_add_primitive(self, "quote", primitive_quote); + Interp_add_primitive(self, "quasiquote", primitive_quasi); + Interp_add_primitive(self, "macroexpand-1", primitive_macroexpand1); + Interp_add_primitive(self, "return", primitive_return); + Interp_add_primitive(self, "break", primitive_break); + Interp_add_primitive(self, "continue", primitive_continue); + Interp_add_primitive(self, "assert", primitive_assert); + Interp_add_primitive(self, "assert-error", primitive_assert_error); + Interp_add_primitive(self, "assert-exception", primitive_assert_exception); + Interp_add_primitive(self, "load", primitive_load); + Interp_add_primitive(self, "try", primitive_try); + Interp_add_primitive(self, "unwind-protect", primitive_unwind_protect); + + Interp_add_userfunc(self, "throw", builtin_throw); + Interp_add_userfunc(self, "function?", builtin_functionp); + Interp_add_userfunc(self, "map", builtin_map); + Interp_add_userfunc(self, "filter", builtin_filter); + Interp_add_userfunc(self, "remove", builtin_remove); + Interp_add_userfunc(self, "count", builtin_count); + Interp_add_userfunc(self, "foreach", builtin_foreach); + Interp_add_userfunc(self, "symbol->string", builtin_symbol2string); + Interp_add_userfunc(self, "intern", builtin_intern); + Interp_add_userfunc(self, "gensym", builtin_gensym); + Interp_add_userfunc(self, "float", builtin_float); + Interp_add_userfunc(self, "tan", builtin_tan); + Interp_add_userfunc(self, "asin", builtin_asin); + Interp_add_userfunc(self, "acos", builtin_acos); + Interp_add_userfunc(self, "log2", builtin_log2); + Interp_add_userfunc(self, "pow", builtin_pow); + Interp_add_userfunc(self, "expt", builtin_pow); + Interp_add_userfunc(self, "exp", builtin_exp); + Interp_add_userfunc(self, "sqrt", builtin_sqrt); + Interp_add_userfunc(self, "cbrt", builtin_cbrt); + Interp_add_userfunc(self, "log10", builtin_log10); + Interp_add_userfunc(self, "eq?", builtin_eq); + Interp_add_userfunc(self, "ln", builtin_ln); + Interp_add_userfunc(self, "=", builtin_num_equal); + Interp_add_userfunc(self, "/=", builtin_num_neq); + Interp_add_userfunc(self, "concat", builtin_concat); + Interp_add_userfunc(self, "string", builtin_string); + Interp_add_userfunc(self, "string=", builtin_string_eq); + Interp_add_userfunc(self, "string>=", builtin_string_ge); + Interp_add_userfunc(self, "string<=", builtin_string_le); + Interp_add_userfunc(self, "string>", builtin_string_gt); + Interp_add_userfunc(self, "string<", builtin_string_lt); + Interp_add_userfunc(self, "string/=", builtin_string_neq); + Interp_add_userfunc(self, "split-string", builtin_split_string); + Interp_add_userfunc(self, "strip-string", builtin_strip_string); + Interp_add_userfunc(self, "print", builtin_print); + Interp_add_userfunc(self, "format", builtin_format); + Interp_add_userfunc(self, "truncate", builtin_truncate); + Interp_add_userfunc(self, "mod", builtin_mod); + Interp_add_userfunc(self, "+", builtin_add); + Interp_add_userfunc(self, "-", builtin_sub); + Interp_add_userfunc(self, "*", builtin_mul); + Interp_add_userfunc(self, "/", builtin_div); + Interp_add_userfunc(self, "i/", builtin_idiv); + Interp_add_userfunc(self, ">", builtin_gt); + Interp_add_userfunc(self, "<", builtin_lt); + Interp_add_userfunc(self, ">=", builtin_ge); + Interp_add_userfunc(self, "<=", builtin_le); + Interp_add_userfunc(self, "abs", builtin_abs); + Interp_add_userfunc(self, "list", builtin_list); + Interp_add_userfunc(self, "car", builtin_car); + Interp_add_userfunc(self, "sin", builtin_sin); + Interp_add_userfunc(self, "max", builtin_max); + Interp_add_userfunc(self, "exit", builtin_exit); + Interp_add_userfunc(self, "not", builtin_not); + Interp_add_userfunc(self, "cos", builtin_cos); + Interp_add_userfunc(self, "princ", builtin_princ); + Interp_add_userfunc(self, "equal?", builtin_equal); + Interp_add_userfunc(self, "atan", builtin_atan); + Interp_add_userfunc(self, "cons", builtin_cons); + Interp_add_userfunc(self, "cdr", builtin_cdr); + Interp_add_userfunc(self, "ceiling", builtin_ceiling); + Interp_add_userfunc(self, "round", builtin_round); + Interp_add_userfunc(self, "floor", builtin_floor); + Interp_add_userfunc(self, "min", builtin_min); + Interp_add_userfunc(self, "error", builtin_error); + Interp_add_userfunc(self, "set-car", builtin_set_car); + Interp_add_userfunc(self, "set-cdr", builtin_set_cdr); + Interp_add_userfunc(self, "length", builtin_length); + Interp_add_userfunc(self, "nth", builtin_nth); + Interp_add_userfunc(self, "nthcdr", builtin_nthcdr); + Interp_add_userfunc(self, "list?", builtin_listp); + Interp_add_userfunc(self, "cons?", builtin_consp); + Interp_add_userfunc(self, "atom?", builtin_atomp); + Interp_add_userfunc(self, "null?", builtin_nullp); + Interp_add_userfunc(self, "member?", builtin_memberp); + Interp_add_userfunc(self, "number?", builtin_numberp); + Interp_add_userfunc(self, "integer?", builtin_integerp); + Interp_add_userfunc(self, "float?", builtin_floatp); + Interp_add_userfunc(self, "nreverse", builtin_nreverse); + Interp_add_userfunc(self, "reverse", builtin_reverse); + Interp_add_userfunc(self, "last", builtin_last); + Interp_add_userfunc(self, "char?", builtin_charp); + Interp_add_userfunc(self, "char=", builtin_char_eq); + Interp_add_userfunc(self, "char>", builtin_char_gt); + Interp_add_userfunc(self, "char<", builtin_char_lt); + Interp_add_userfunc(self, "char>=", builtin_char_ge); + Interp_add_userfunc(self, "char<=", builtin_char_le); + Interp_add_userfunc(self, "char/=", builtin_char_neq); + Interp_add_userfunc(self, "int->char", builtin_int2char); + Interp_add_userfunc(self, "char->int", builtin_char2int); + Interp_add_userfunc(self, "alphabetic?", builtin_alphabeticp); + Interp_add_userfunc(self, "numeric?", builtin_numericp); + Interp_add_userfunc(self, "alphanum?", builtin_alphanump); + Interp_add_userfunc(self, "set-nth", builtin_setnth); + Interp_add_userfunc(self, "set-nthcdr", builtin_setnthcdr); + Interp_add_userfunc(self, "foldl", builtin_foldl); + Interp_add_userfunc(self, "append", builtin_append); + Interp_add_userfunc(self, "nconc", builtin_nconc); + Interp_add_userfunc(self, "logand", builtin_logand); + Interp_add_userfunc(self, "logior", builtin_logior); + Interp_add_userfunc(self, "logxor", builtin_logxor); + Interp_add_userfunc(self, "lognot", builtin_lognot); + Interp_add_userfunc(self, "lsh", builtin_lsh); + Interp_add_userfunc(self, "ash", builtin_ash); + + Interp_add_userfunc(self, "_gcstat", builtin_gcstat); + Interp_add_userfunc(self, "_alwaysgc", builtin_alwaysgc); + + // extentions + bamboo_lisp_init_vector(self); + + SExpRef ret = Interp_eval_string(self, bamboo_lisp_prelude); + Interp *interp = self; + if (VALTYPE(ret) == kErrSignal) { + fprintf(stderr, "Failed to load prelude: %s", REF(ret)->str); + } + if (VALTYPE(ret) == kExceptionSignal) { + const char *exception_str = lisp_to_string(interp, Interp_ref(self, ret)->ret); + fprintf(stderr, "Failed to load prelude, uncatched exception: %s\n", exception_str); + free((void*)exception_str); + } +} + + +SExpRef Interp_eval_string(Interp *interp, const char * str) { + Parser_set_string(interp->parser, str); + SExpRef sexp, ret; + ParseResult parse_result; + while (1) { + parse_result = parse_sexp(interp->parser); + if (parse_result.errmsg != NULL) { + ret = new_error(interp, "Parsing error: %s", parse_result.errmsg); + goto end; + } + ret = lisp_eval(interp, parse_result.val, false); + if (Interp_ref(interp, ret)->type == kErrSignal + || Interp_ref(interp, ret)->type == kExceptionSignal) { + goto end; + } + if (Interp_ref(interp, ret)->type == kBreakSignal + || Interp_ref(interp, ret)->type == kContinueSignal + || Interp_ref(interp, ret)->type == kReturnSignal) { + ret = new_error(interp, "Eval error: unexpected control flow signal.\n"); + goto end; + } + if (Parser_is_end(interp->parser)) goto end; + } +end: + return ret; +} + +SExpRef Interp_load_file(Interp *interp, const char *filename) { + FILE *fp = NULL; + fp = fopen(filename, "r"); + if (fp == NULL) { + return new_error(interp, "Failed to open file: %s\n", filename); + goto end; + } + Parser_set_file(interp->parser, fp); + SExpRef sexp, ret; + ParseResult parse_result; + SExpRef old_filename = interp->filename; + int old_linenum = interp->linenum; + interp->filename = new_string(interp, filename); + interp->linenum = 1; + while (1) { + parse_result = parse_sexp(interp->parser); + if (parse_result.errmsg != NULL) { + ret = new_error(interp, "Parsing error: %s", parse_result.errmsg); + goto end; + } + ret = lisp_eval(interp, parse_result.val, false); + if (Interp_ref(interp, ret)->type == kErrSignal + || Interp_ref(interp, ret)->type == kExceptionSignal) { + goto end; + } + if (Interp_ref(interp, ret)->type == kBreakSignal + || Interp_ref(interp, ret)->type == kContinueSignal + || Interp_ref(interp, ret)->type == kReturnSignal) { + ret = new_error(interp, "Eval error: unexpected control flow signal.\n"); + goto end; + } + if (Parser_is_end(interp->parser)) goto end; + } +end: + interp->filename = old_filename; + interp->linenum = old_linenum; + fclose(fp); + return ret; +} + +void Interp_add_userfunc(Interp *interp, const char *name, LispUserFunc fn) { + SExpRef userfunc = new_userfunc(interp, fn); + SExpRef sym = new_symbol(interp, name); + lisp_defun(interp, sym, userfunc); +} + +void Interp_free(Interp *self) { + for (size_t i = 0; i < SExpVector_len(&self->objs); i++) { + SExp *obj = SExpVector_ref(&self->objs, i); + if (obj->type == kStringSExp) { + free((void*)obj->str); + } + if (obj->type == kUserDataSExp) { + if (obj->userdata_meta && obj->userdata_meta->free) { + (*obj->userdata_meta->free)(obj->userdata); + } + } + } + for (String2IntHashTableIter iter = String2IntHashTable_begin(&self->symbols); + iter != NULL; + iter = String2IntHashTable_next(&self->symbols, iter)) { + free((void*)iter->key); + } + String2IntHashTable_free(&self->symbols); + SExpVector_free(&self->objs); + IntVector_free(&self->empty_space); + SExpRef2SExpRefHashTable_free(&self->topbindings); + free(self->errmsg_buf); + Parser_free(self->parser); + free(self->parser); +} + +SExp* Interp_ref(Interp *self, SExpRef ref) { + if (ref.idx > SExpVector_len(&self->objs)) return NULL; + SExp *res = SExpVector_ref(&self->objs, ref.idx); + return res; +} + +void Interp_add_primitive(Interp *self, const char *name, LispPrimitive fn) { + SExpRef sym = new_symbol(self, name); + SExpRef prim = new_primitive(self, fn); + lisp_defun(self, sym, prim); +} + +void Interp_gc(Interp *interp, SExpRef tmproot) { + int freesize = IntVector_len(&interp->empty_space); + int heapsize = SExpVector_len(&interp->objs); + if (freesize > (heapsize >> 4) && !interp->alwaysgc) { + return; + } + SExpPtrVector gcstack; + SExpPtrVector_init(&gcstack); + // add root + SExpPtrVector_push_back(&gcstack, REF(tmproot)); + SExpPtrVector_push_back(&gcstack, REF(interp->nil)); + SExpPtrVector_push_back(&gcstack, REF(interp->t)); + SExpPtrVector_push_back(&gcstack, REF(interp->f)); + SExpPtrVector_push_back(&gcstack, REF(interp->stack)); + SExpPtrVector_push_back(&gcstack, REF(interp->top_level)); + SExpPtrVector_push_back(&gcstack, REF(interp->reg)); + SExpPtrVector_push_back(&gcstack, REF(interp->stacktrace)); + // mark + while (!SExpPtrVector_empty(&gcstack)) { + SExpPtr obj = *SExpPtrVector_last(&gcstack); + SExpPtr child; + SExpPtrVector_pop(&gcstack); + if (!obj) continue; + if (obj->marked) continue; + obj->marked = true; + if (obj->type == kPairSExp) { + child = REF(obj->pair.car); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->pair.cdr); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->pair.filename); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kFuncSExp) { + child = REF(obj->func.args); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->func.body); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->func.env); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kEnvSExp) { + child = REF(obj->env.bindings); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->env.parent); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kBindingSExp) { + child = REF(obj->binding.name); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->binding.value); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->binding.func); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->binding.next); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kMacroSExp) { + child = REF(obj->macro.args); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->macro.body); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kReturnSignal) { + child = REF(obj->ret); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kTailcallSExp) { + child = REF(obj->tailcall.args); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + child = REF(obj->tailcall.fn); + if (child && !child->marked) SExpPtrVector_push_back(&gcstack, child); + } else if (obj->type == kUserDataSExp) { + if (obj->userdata_meta && obj->userdata_meta->gcmark) { + (*obj->userdata_meta->gcmark)(interp, &gcstack, obj->userdata); + } + } + } + SExpPtrVector_free(&gcstack); + // sweep + for (int i = 0; i < SExpVector_len(&interp->objs); i++) { + SExp *obj = SExpVector_ref(&interp->objs, i); + if (obj->marked) { + obj->marked = false; + continue; + } + if (obj->type == kSymbolSExp) continue; + if (obj->type == kEmptySExp) continue; + if (obj->type == kStringSExp) free((void*)obj->str); + if (obj->type == kUserDataSExp) { + if (obj->userdata_meta && obj->userdata_meta->free) { + (*obj->userdata_meta->free)(obj->userdata); + } + } + obj->type = kEmptySExp; + IntVector_push_back(&interp->empty_space, i); + } + // enlarge heap + heapsize = SExpVector_len(&interp->objs); + int usedsize = heapsize - IntVector_len(&interp->empty_space); + if (heapsize < usedsize * 4) { + SExp sexp; + sexp.marked = false; + sexp.type = kEmptySExp; + while (SExpVector_len(&interp->objs) < usedsize * 4) { + SExpVector_push_back(&interp->objs, sexp); + IntVector_push_back(&interp->empty_space, SExpVector_len(&interp->objs) - 1); + } + } +} + +bool lisp_truep(Interp *interp, SExpRef a) { + if (REF(a)->type == kNilSExp) return false; + if (REF(a)->type == kBooleanSExp && !REF(a)->boolean) return false; + return true; +} + +SExpRef lisp_cons(Interp *interp, SExpRef a, SExpRef b) { + SExpRef obj = new_sexp(interp); + REF(obj)->type = kPairSExp; + REF(obj)->pair.car = a; + REF(obj)->pair.cdr = b; + REF(obj)->pair.filename = NIL; + REF(obj)->pair.line = -1; + return obj; +} + +SExpRef lisp_dup(Interp *interp, SExpRef arg) { + SExpRef obj = new_sexp(interp); + *REF(obj) = *REF(arg); + return obj; +} + +SExpRef lisp_car(Interp *interp, SExpRef arg) { + if (REF(arg)->type != kPairSExp) { + return new_error(interp, "car: wrong argument type."); + } + return REF(arg)->pair.car; +} + +SExpRef lisp_cdr(Interp *interp, SExpRef arg) { + if (REF(arg)->type != kPairSExp) { + return new_error(interp, "cdr: wrong argument type."); + } + return REF(arg)->pair.cdr; +} + +bool lisp_check_list(Interp *interp, SExpRef lst) { + while (REF(lst)->type == kPairSExp) { + lst = CDR(lst); + } + return REF(lst)->type == kNilSExp; +} + +void lisp_to_string_impl(str_builder_t *sb, Int2IntHashTable *visited, Interp *interp, SExpRef val) { + SExp *pe = REF(val); + if (pe->type == kIntegerSExp) { + str_builder_append(sb, "%"PRId64, pe->integer); + } else if (pe->type == kRealSExp) { + str_builder_append(sb, "%lg", pe->real); + } else if (pe->type == kCharSExp) { + str_builder_append(sb, "#\\%c", pe->character); + } else if (pe->type == kBooleanSExp) { + if (pe->boolean) str_builder_append(sb, "#t"); + else str_builder_append(sb, "#f"); + } else if (pe->type == kCharSExp) { + str_builder_append(sb, "#\%c", pe->character); + } else if (pe->type == kSymbolSExp) { + str_builder_append(sb, "%s", pe->str); + } else if (pe->type == kStringSExp) { + str_builder_append(sb, "\"%s\"", pe->str); + } else if (pe->type == kFuncSExp) { + str_builder_append(sb, "<FUNCTION>"); + } else if (pe->type == kUserFuncSExp) { + str_builder_append(sb, "<FUNCTION>"); + } else if (pe->type == kMacroSExp) { + str_builder_append(sb, "<MACRO>"); + } else if (pe->type == kEnvSExp) { + str_builder_append(sb, "<ENV>"); + } else if (pe->type == kBindingSExp) { + str_builder_append(sb, "<BINDING>"); + } else if (pe->type == kNilSExp) { + str_builder_append(sb, "()"); + } else if (pe->type == kErrSignal) { + str_builder_append(sb, "<ERROR>"); + } else if (pe->type == kExceptionSignal) { + str_builder_append(sb, "<EXCEPTION>"); + } else if (pe->type == kReturnSignal) { + str_builder_append(sb, "<RETURN>"); + } else if (pe->type == kBreakSignal) { + str_builder_append(sb, "<BREAK>"); + } else if (pe->type == kContinueSignal) { + str_builder_append(sb, "<CONTINUE>"); + } else if (pe->type == kTailcallSExp) { + str_builder_append(sb, "<TAILCALL>"); + } else if (pe->type == kUserDataSExp) { + str_builder_append(sb, "<USERDATA>"); + } else if (pe->type == kPairSExp) { + if (Int2IntHashTable_find(visited, val.idx) != NULL) { + str_builder_append(sb, "<%d>", val.idx); + } else { + str_builder_append_char(sb, '('); + SExpRef cur = val; + while (REF(cur)->type == kPairSExp + && Int2IntHashTable_find(visited, cur.idx) == NULL) { + Int2IntHashTable_insert(visited, cur.idx, 1); + lisp_to_string_impl(sb, visited, interp, CAR(cur)); + str_builder_append_char(sb, ' '); + cur = CDR(cur); + } + if (REF(cur)->type == kNilSExp) { + sb->buf[sb->size - 1] = ')'; + } else if (REF(cur)->type != kPairSExp) { + str_builder_append(sb, ". "); + lisp_to_string_impl(sb, visited, interp, cur); + str_builder_append(sb, ")"); + } else { + str_builder_append(sb, "<%d>)", cur.idx); + } + } + } +} + + +const char* lisp_to_string(Interp *interp, SExpRef val) { + str_builder_t sb; + Int2IntHashTable visited; + Int2IntHashTable_init(&visited); + init_str_builder(&sb); + lisp_to_string_impl(&sb, &visited, interp, val); + str_builder_append_char(&sb, '\0'); + Int2IntHashTable_free(&visited); + return sb.buf; +} + +SExpRef lisp_macroexpand1(Interp *interp, SExpRef macro, SExpRef args) { + SExpRef fn = new_lambda(interp, REF(macro)->macro.args, REF(macro)->macro.body, interp->top_level); + PUSH_REG(fn); + SExpRef ret = lisp_call(interp, fn, args); + POP_REG(); + return ret; +error: + return new_error(interp, "macroexpand: syntax error.\n"); +} + +void lisp_defun(Interp *interp, SExpRef name, SExpRef val) { + SExpRef binding = REF(interp->top_level)->env.bindings; + while (REF(binding)->type != kNilSExp) { + if (name.idx == REF(binding)->binding.name.idx) { + REF(binding)->binding.func = val; + return; + } + binding = REF(binding)->binding.next; + } + binding = REF(interp->top_level)->env.bindings; + SExpRef newbinding = new_binding(interp, name, NIL); + REF(newbinding)->binding.func = val; + REF(newbinding)->binding.value = UNBOUND; + REF(newbinding)->binding.next = binding; + REF(interp->top_level)->env.bindings = newbinding; + SExpRef2SExpRefHashTable_insert(&interp->topbindings, name, newbinding); +} + +void lisp_defvar(Interp *interp, SExpRef name, SExpRef val) { + SExpRef binding = REF(interp->top_level)->env.bindings; + while (REF(binding)->type != kNilSExp) { + if (name.idx == REF(binding)->binding.name.idx) { + REF(binding)->binding.value = val; + return; + } + binding = REF(binding)->binding.next; + } + binding = REF(interp->top_level)->env.bindings; + SExpRef newbinding = new_binding(interp, name, NIL); + REF(newbinding)->binding.func = UNBOUND; + REF(newbinding)->binding.value = val; + REF(newbinding)->binding.next = binding; + REF(interp->top_level)->env.bindings = newbinding; + SExpRef2SExpRefHashTable_insert(&interp->topbindings, name, newbinding); +} + +SExpRef lisp_setq(Interp *interp, SExpRef name, SExpRef val) { + SExpRef env = CAR(interp->stack); + while (REF(env)->type != kNilSExp) { + SExpRef binding = REF(env)->env.bindings; + while (REF(binding)->type != kNilSExp) { + if (name.idx == REF(binding)->binding.name.idx) { + REF(binding)->binding.value = val; + return val; + } + binding = REF(binding)->binding.next; + } + env = REF(env)->env.parent; + } + return new_error(interp, "Unbound variable: %s.\n", REF(name)->str); +} + +SExpRef lisp_lookup_topvar(Interp *interp, SExpRef name); + +SExpRef lisp_lookup(Interp *interp, SExpRef name) { + SExpRef env = CAR(interp->stack); + while (REF(env)->type != kNilSExp) { + if (env.idx == interp->top_level.idx) { + return lisp_lookup_topvar(interp, name); + } + SExpRef binding = REF(env)->env.bindings; + while (REF(binding)->type != kNilSExp) { + if (name.idx == REF(binding)->binding.name.idx) { + SExpRef ret = REF(binding)->binding.value; + if (ret.idx < 0) goto notfound; + return ret; + } + binding = REF(binding)->binding.next; + } + env = REF(env)->env.parent; + } +notfound: + return new_error(interp, "Unbound variable: %s.\n", REF(name)->str); +} + +void lisp_print(Interp *interp, SExpRef obj, FILE *fp) { + const char *str = lisp_to_string(interp, obj); + fprintf(fp, "%s\n", str); + free((void*)str); +} + +SExpRef lisp_lookup_topvar(Interp *interp, SExpRef name) { + SExpRef *pbinding = SExpRef2SExpRefHashTable_get(&interp->topbindings, name); + if (pbinding == NULL) goto notfound; + SExpRef ret = REF(*pbinding)->binding.value; + if (ret.idx < 0) goto notfound; + return ret; +notfound: + return new_error(interp, "Unbound variable: %s.\n", REF(name)->str); +} + +SExpRef lisp_lookup_func(Interp *interp, SExpRef name) { + SExpRef *pbinding = SExpRef2SExpRefHashTable_get(&interp->topbindings, name); + if (pbinding == NULL) goto notfound; + SExpRef ret = REF(*pbinding)->binding.func; + if (ret.idx < 0) goto notfound; + return ret; +notfound: + return new_error(interp, "Unbound function: %s.\n", REF(name)->str); +} + +bool lisp_nilp(Interp *interp, SExpRef obj) { + return REF(obj)->type == kNilSExp; +} + +SExpRef lisp_reverse(Interp *interp, SExpRef lst) { + SExpRef cur = lst; + SExpRef ret = NIL; + while (!NILP(cur)) { + ret = CONS(CAR(cur), ret); + cur = CDR(cur); + } + return ret; +} + +SExpRef lisp_nreverse(Interp *interp, SExpRef lst) { + SExpRef prev = NIL; + SExpRef cur = lst; + SExpRef next_node; + + while (!NILP(cur)) { + next_node = CDR(cur); + REF(cur)->pair.cdr = prev; + prev = cur; + cur = next_node; + } + return prev; +} + +SExpRef lisp_eval_args(Interp *interp, SExpRef args) { + SExpRef ret = interp->nil; + SExpRef cur = args; + SExpRef evalres; + + while (!NILP(cur)) { + // save ret in register + PUSH_REG(ret); + evalres = EVAL(CAR(cur)); + POP_REG(); + if (CTL_FL(evalres)) { + ret = evalres; + goto end; + } + ret = CONS(evalres, ret); + cur = CDR(cur); + } + ret = lisp_nreverse(interp, ret); +end: + Interp_gc(interp, ret); + return ret; +} + +int lisp_length(Interp *interp, SExpRef lst) { + int cnt = 0; + if (VALTYPE(lst) == kNilSExp) { + return 0; + } else if (VALTYPE(lst) == kPairSExp) { + while (REF(lst)->type == kPairSExp) { + cnt++; + lst = CDR(lst); + } + return cnt; + } else if (VALTYPE(lst) == kStringSExp) { + return strlen(REF(lst)->str); + } else return -1; +} + +static SExpRef build_function_env(Interp *interp, SExpRef func, SExpRef args) { + SExpRef param = REF(func)->func.args; + SExpRef iparam = param; + SExpRef iargs = args; + SExpRef env = new_env(interp); + REF(env)->env.parent = REF(func)->func.env; + while (!NILP(iparam)) { + if (VALTYPE(iparam) == kSymbolSExp) { + SExpRef binding = new_binding(interp, iparam, iargs); + REF(binding)->binding.next = REF(env)->env.bindings; + REF(env)->env.bindings = binding; + return env; + } + SExpRef name = CAR(iparam); + if (VALTYPE(name) != kSymbolSExp) { + return new_error(interp, "function syntax error: parameter must be a symbol.\n"); + } + if (NILP(iargs)) return new_error(interp, "funcall: wrong argument number.\n"); + SExpRef binding = new_binding(interp, name, CAR(iargs)); + REF(binding)->binding.next = REF(env)->env.bindings; + REF(env)->env.bindings = binding; + iargs = CDR(iargs); + iparam = CDR(iparam); + } + if (!NILP(iargs)) return new_error(interp, "funcall: wrong argument number.\n"); + return env; +} + +SExpRef lisp_call(Interp *interp, SExpRef fn, SExpRef args) { + SExpRef ret = lisp_apply(interp, fn, args, false); + while (VALTYPE(ret) == kTailcallSExp) { + fn = REF(ret)->tailcall.fn; + args = REF(ret)->tailcall.args; + PUSH_REG(ret); + ret = lisp_apply(interp, fn, args, false); + POP_REG(); + if (CTL_FL(ret)) break; + } + if (VALTYPE(ret) == kBreakSignal + || VALTYPE(ret) == kContinueSignal + || VALTYPE(ret) == kReturnSignal) { + return new_error(interp, "call: unexpected control flow signal.\n"); + } + return ret; +} + +SExpRef lisp_apply(Interp *interp, SExpRef fn, SExpRef args, bool istail) { + if (interp->recursion_depth > 2048) + return new_error(interp, "apply: stack overflow.\n"); + interp->recursion_depth++; + SExpRef exp, env, ret, iter; + if (istail) { + interp->recursion_depth--; + return new_tailcall(interp, fn, args); + } + if (VALTYPE(fn) == kFuncSExp) { + env = build_function_env(interp, fn, args); + if (CTL_FL(env)) { + interp->recursion_depth--; + return env; + } + interp->stack = CONS(env, interp->stack); + iter = REF(fn)->func.body; + while (!NILP(iter)) { + exp = CAR(iter); + if (NILP(CDR(iter))) { + ret = lisp_eval(interp, exp, true); + goto end; + } else { + ret = EVAL(exp); + } + if (CTL_FL(ret)) goto end; + iter = CDR(iter); + } + } else if (VALTYPE(fn) == kUserFuncSExp) { + LispUserFunc fnptr = REF(fn)->userfunc; + PUSH_REG(args); + ret = (*fnptr)(interp, args); + POP_REG(); + interp->recursion_depth--; + return ret; + } +end: + if (VALTYPE(ret) == kBreakSignal || VALTYPE(ret) == kContinueSignal) { + ret = new_error(interp, "function call: unexpected control flow signal.\n"); + } + if (VALTYPE(ret) == kReturnSignal) { + ret = REF(ret)->ret; + } + interp->stack = CDR(interp->stack); + interp->recursion_depth--; + return ret; +} + + +SExpRef lisp_eval(Interp *interp, SExpRef sexp, bool istail) { + if (interp->recursion_depth > 2048) { + return new_error(interp, "eval: stack overflow.\n"); + } + interp->recursion_depth++; + SExpRef ret; + SExpType type; + PUSH_REG(sexp); + type = REF(sexp)->type; + if (type == kEnvSExp || type == kEnvSExp || type == kBindingSExp) { + ret = new_error(interp, "type error: cannot eval.\n"); + goto end; + } + if (type == kIntegerSExp + || type == kStringSExp + || type == kBooleanSExp + || type == kCharSExp + || type == kErrSignal + || type == kExceptionSignal + || type == kBreakSignal + || type == kContinueSignal + || type == kReturnSignal + || type == kTailcallSExp + || type == kFuncSExp + || type == kUserFuncSExp + || type == kRealSExp) { + ret = sexp; + goto end; + } + if (type == kSymbolSExp) { + ret = lisp_lookup(interp, sexp); + goto end; + } + SExpRef fn, funcallargs, args; + SExpRef filename = NIL; + SExpRef sym = NIL; + int line = -1; + if (type == kPairSExp) { + if (!lisp_check_list(interp, sexp)) { + ret = new_error(interp, "eval: list not proper.\n"); + goto end; + } + if (REF(CAR(sexp))->type != kSymbolSExp) { + ret = new_error(interp, "eval: first elem must be a symbol.\n"); + goto end; + } + if (!NILP(REF(sexp)->pair.filename)) { + line = REF(sexp)->pair.line; + filename = REF(sexp)->pair.filename; + sym = REF(sexp)->pair.car; + } + SExpRef symbol = CAR(sexp); + fn = lisp_lookup_func(interp, symbol); + if (CTL_FL(fn)) { + ret = new_error(interp, "eval: \"%s\" is not a primitive, function, " + "or macro.\n", REF(symbol)->str); + goto end; + } + if (VALTYPE(fn) == kPrimitiveSExp) { + LispPrimitive primitive_fn = REF(fn)->primitive; + ret = (*primitive_fn)(interp, CDR(sexp), istail); + if (VALTYPE(ret) == kTailcallSExp && !istail) { + fn = REF(ret)->tailcall.fn; + args = REF(ret)->tailcall.args; + goto tailcall; + } + goto end; + } else if (VALTYPE(fn) == kFuncSExp || VALTYPE(fn) == kUserFuncSExp) { + args = CDR(sexp); + funcallargs = CONS(fn, args); + PUSH_REG(funcallargs); + ret = primitive_funcall(interp, funcallargs, istail); + POP_REG(); + if (VALTYPE(ret) == kTailcallSExp && !istail) { + fn = REF(ret)->tailcall.fn; + args = REF(ret)->tailcall.args; + goto tailcall; + } + goto end; + } else if (VALTYPE(fn) == kMacroSExp) { + SExpRef args = CDR(sexp); + SExpRef newast = lisp_macroexpand1(interp, fn, args); + PUSH_REG(newast); + ret = lisp_eval(interp, newast, istail); + POP_REG(); + goto end; + } else { + ret = new_error(interp, + "eval: fatal binding eval, %s is not a func, prim " + "or macro.\n", REF(symbol)->str); + goto end; + } + } + ret = new_error(interp, "eval: unknown syntax.\n"); +end: + if (VALTYPE(ret) == kErrSignal || VALTYPE(ret) == kExceptionSignal) { + if (!NILP(filename)) { + interp->stacktrace = + CONS(CONS(filename, CONS(new_integer(interp, line), CONS(sym, NIL))), + interp->stacktrace); + } + } + POP_REG(); + Interp_gc(interp, ret); + interp->recursion_depth--; + return ret; +tailcall: + while (1) { + PUSH_REG(CONS(fn, args)); + ret = lisp_apply(interp, fn, args, false); + POP_REG(); + if (VALTYPE(ret) != kTailcallSExp) break; + fn = REF(ret)->tailcall.fn; + args = REF(ret)->tailcall.args; + } + goto end; +} + +SExpRef new_sexp(Interp *interp) { + if (IntVector_len(&interp->empty_space) == 0) { + SExp sexp; + sexp.type = kEmptySExp; + sexp.marked = false; + SExpVector_push_back(&interp->objs, sexp); + return (SExpRef){ SExpVector_len(&interp->objs) - 1 }; + } + int idx = *IntVector_ref(&interp->empty_space, IntVector_len(&interp->empty_space) - 1); + IntVector_pop(&interp->empty_space); + return (SExpRef){idx}; +} + +SExpRef new_env(Interp *interp) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kEnvSExp; + REF(ret)->env.parent = NIL; + REF(ret)->env.bindings = NIL; + return ret; +} + +SExpRef new_tailcall(Interp *interp, SExpRef fn, SExpRef args) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kTailcallSExp; + REF(ret)->tailcall.fn = fn; + REF(ret)->tailcall.args= args; + return ret; +} + +SExpRef new_lambda(Interp *interp, SExpRef param, SExpRef body, SExpRef env) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kFuncSExp; + REF(ret)->func.args = param; + REF(ret)->func.body = body; + REF(ret)->func.env = env; + return ret; +} + +SExpRef new_macro(Interp *interp, SExpRef param, SExpRef body) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kMacroSExp; + REF(ret)->macro.args = param; + REF(ret)->macro.body = body; + return ret; +} + +SExpRef new_binding(Interp *interp, SExpRef sym, SExpRef val) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kBindingSExp; + REF(ret)->binding.name = sym; + REF(ret)->binding.value = val; + REF(ret)->binding.func = UNBOUND; + REF(ret)->binding.next = NIL; + return ret; +} + +SExpRef new_boolean(Interp *interp, bool val) { + if (val) return interp->t; + return interp->f; +} + +SExpRef new_error(Interp *interp, const char *format, ...) { + va_list args; + va_start(args, format); + vsnprintf(interp->errmsg_buf, BUFSIZE, format, args); + va_end(args); + SExpRef ret = new_sexp(interp); + REF(ret)->type = kErrSignal; + REF(ret)->str = interp->errmsg_buf; + return ret; +} + +SExpRef new_userfunc(Interp *interp, LispUserFunc val) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kUserFuncSExp; + REF(ret)->userfunc = val; + return ret; +} + +SExpRef new_char(Interp *interp, char val) { + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kCharSExp; + psexp->character = val; + return ret; +} + +SExpRef new_integer(Interp *interp, int64_t val) { + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kIntegerSExp; + psexp->integer = val; + return ret; +} + +SExpRef new_real(Interp *interp, double val) { + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kRealSExp; + psexp->real = val; + return ret; +} + +SExpRef new_string(Interp *interp, const char *val) { + char *dup = strdup(val); + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kStringSExp; + psexp->str = dup; + return ret; +} + +SExpRef new_symbol(Interp *interp, const char *val) { + String2IntHashTableIter iter = String2IntHashTable_find(&interp->symbols, val); + if (iter == NULL) { + char *dup = strdup(val); + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kSymbolSExp; + psexp->str = dup; + String2IntHashTable_insert(&interp->symbols, dup, ret.idx); + return ret; + } else { + return (SExpRef){ iter->val }; + } +} + +SExpRef new_return(Interp *interp, SExpRef obj) { + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kReturnSignal; + psexp->ret = obj; + return ret; +} + +SExpRef new_break(Interp *interp) { + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kBreakSignal; + return ret; +} + +SExpRef new_continue(Interp *interp) { + SExpRef ret = new_sexp(interp); + SExp *psexp = Interp_ref(interp, ret); + psexp->type = kContinueSignal; + return ret; +} + +SExpRef new_primitive(Interp *interp, LispPrimitive val) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kPrimitiveSExp; + REF(ret)->primitive = val; + return ret; +} + +SExpRef new_exception(Interp *interp, SExpRef e) { + SExpRef ret = new_sexp(interp); + REF(ret)->type = kExceptionSignal; + REF(ret)->ret = e; + return ret; +} + +SExpRef new_list2(Interp *interp, SExpRef e1, SExpRef e2) { + return CONS(e1, CONS(e2, NIL)); +} +SExpRef new_list3(Interp *interp, SExpRef e1, SExpRef e2, SExpRef e3); +SExpRef new_list4(Interp *interp, SExpRef e1, SExpRef e2, SExpRef e3, SExpRef e4); +SExpRef new_list5(Interp *interp, SExpRef e1, SExpRef e2, SExpRef e3, SExpRef e4, SExpRef e5); + |
