diff options
| author | Mistivia <i@mistivia.com> | 2025-06-20 18:12:25 +0800 |
|---|---|---|
| committer | Mistivia <i@mistivia.com> | 2025-06-20 18:12:25 +0800 |
| commit | 3cef9f993de2e0380c859d0e0ec88c07bfc3635f (patch) | |
| tree | 7f98824d36d545ed90dd4adc34a3e3d2c7cfc1ff /src/interp.c | |
| parent | ad580b3401deb4a41ee3a8f17824d136ee5cf38d (diff) | |
finish function
Diffstat (limited to 'src/interp.c')
| -rw-r--r-- | src/interp.c | 85 |
1 files changed, 74 insertions, 11 deletions
diff --git a/src/interp.c b/src/interp.c index dbc2357..f03a688 100644 --- a/src/interp.c +++ b/src/interp.c @@ -52,7 +52,12 @@ void Interp_init(Interp *self) { 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, "funcall", primitive_funcall); + Interp_add_primitive(self, "apply", primitive_apply); + Interp_add_primitive(self, "quote", primitive_quote); + Interp_add_userfunc(self, "eval", lisp_eval); + Interp_add_userfunc(self, "show", builtin_show); Interp_add_userfunc(self, "car", builtin_car); Interp_add_userfunc(self, "list", builtin_list); Interp_add_userfunc(self, "cdr", builtin_cdr); @@ -167,6 +172,8 @@ void lisp_to_string_impl(str_builder_t *sb, Int2IntHashTable *visited, Interp *i 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) { @@ -192,15 +199,12 @@ void lisp_to_string_impl(str_builder_t *sb, Int2IntHashTable *visited, Interp *i } if (REF(cur)->type == kNilSExp) { sb->buf[sb->size - 1] = ')'; - str_builder_append_char(sb, '\0'); } else if (REF(cur)->type != kPairSExp) { str_builder_append(sb, ". "); lisp_to_string_impl(sb, visited, interp, cur); str_builder_append(sb, ")"); - str_builder_append_char(sb, '\0'); } else { str_builder_append(sb, "<%d>)", cur.idx); - str_builder_append_char(sb, '\0'); } } } @@ -213,6 +217,7 @@ const char* lisp_to_string(Interp *interp, SExpRef val) { 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; } @@ -229,6 +234,7 @@ void lisp_defun(Interp *interp, const char *name, SExpRef val) { binding = REF(interp->top_level)->env.bindings; SExpRef newbinding = new_binding(interp, new_symbol(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; } @@ -294,10 +300,13 @@ SExpRef lisp_lookup_func(Interp *interp, const char *name) { SExpRef binding = REF(interp->top_level)->env.bindings; while (REF(binding)->type != kNilSExp) { if (strcmp(name, REF(REF(binding)->binding.name)->str) == 0) { - return REF(binding)->binding.func; + SExpRef ret = REF(binding)->binding.func; + if (ret.idx < 0) goto notfound; + return ret; } binding = REF(binding)->binding.next; } +notfound: return new_error(interp, "Unbound function: %s.\n", name); } @@ -345,6 +354,62 @@ int lisp_length(Interp *interp, SExpRef lst) { return cnt; } +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_apply(Interp *interp, SExpRef fn, SExpRef args) { + if (VALTYPE(fn) == kFuncSExp) { + SExpRef env = build_function_env(interp, fn, args); + if (ERRORP(env)) return env; + interp->stack = CONS(env, interp->stack); + SExpRef ret; + SExpRef iter = REF(fn)->func.body; + while (!NILP(iter)) { + SExpRef exp = CAR(iter); + ret = EVAL(exp); + if (ERRORP(exp)) goto end; + iter = CDR(iter); + } + end: + interp->stack = CDR(interp->stack); + return ret; + } else if (VALTYPE(fn) == kUserFuncSExp) { + PUSH_REG(args); + LispUserFunc fnptr = REF(fn)->userfunc; + SExpRef ret = (*fnptr)(interp, args); + POP_REG(); + return ret; + } +error: + return new_error(interp, "function call: syntax error.\n"); +} + + SExpRef lisp_eval(Interp *interp, SExpRef sexp) { SExpRef ret; @@ -361,6 +426,7 @@ SExpRef lisp_eval(Interp *interp, SExpRef sexp) { || type == kCharSExp || type == kErrSExp || type == kFuncSExp + || type == kUserFuncSExp || type == kRealSExp) { ret = sexp; goto end; @@ -388,19 +454,16 @@ SExpRef lisp_eval(Interp *interp, SExpRef sexp) { } } SExpRef fn = lisp_lookup_func(interp, symbol); - if (REF(fn)->type == kUserFuncSExp) { - SExpRef args = lisp_eval_args(interp, CDR(sexp)); - if (ERRORP(args)) { ret = args; goto end; } - PUSH_REG(args); - ret = (*REF(fn)->userfunc)(interp, args); - POP_REG(); + if (!ERRORP(fn)) { + SExpRef args = CDR(sexp); + ret = primitive_funcall(interp, CONS(fn, args)); goto end; } // TODO: macro / func ret = new_error(interp, "eval: \"%s\" is not a primitive, function, or macro.\n", symbol); goto end; } - ret = NIL; + ret = new_error(interp, "eval: unknown syntax.\n"); end: POP_REG(); Interp_gc(interp, ret); |
