aboutsummaryrefslogtreecommitdiff
path: root/src/interp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interp.c')
-rw-r--r--src/interp.c38
1 files changed, 33 insertions, 5 deletions
diff --git a/src/interp.c b/src/interp.c
index e5e9167..5e181a8 100644
--- a/src/interp.c
+++ b/src/interp.c
@@ -52,10 +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, "defmacro", primitive_defmacro);
Interp_add_primitive(self, "funcall", primitive_funcall);
Interp_add_primitive(self, "apply", primitive_apply);
Interp_add_primitive(self, "quote", primitive_quote);
Interp_add_primitive(self, "quasiquote", primitive_quasi);
+ Interp_add_primitive(self, "macroexpand-1", primitive_macroexpand1);
Interp_add_userfunc(self, "eval", lisp_eval);
Interp_add_userfunc(self, "show", builtin_show);
@@ -223,6 +225,16 @@ const char* lisp_to_string(Interp *interp, SExpRef val) {
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_apply(interp, fn, args);
+ POP_REG();
+ return ret;
+error:
+ return new_error(interp, "macroexpand: syntax error.\n");
+}
+
void lisp_defun(Interp *interp, const char *name, SExpRef val) {
SExpRef binding = REF(interp->top_level)->env.bindings;
while (REF(binding)->type != kNilSExp) {
@@ -264,7 +276,7 @@ SExpRef lisp_setq(Interp *interp, const char *name, SExpRef val) {
while (REF(binding)->type != kNilSExp) {
if (strcmp(name, REF(REF(binding)->binding.name)->str) == 0) {
REF(binding)->binding.value = val;
- return NIL;
+ return val;
}
binding = REF(binding)->binding.next;
}
@@ -455,14 +467,22 @@ SExpRef lisp_eval(Interp *interp, SExpRef sexp) {
}
}
SExpRef fn = lisp_lookup_func(interp, symbol);
- if (!ERRORP(fn)) {
+ if (ERRORP(fn)) {
+ ret = new_error(interp, "eval: \"%s\" is not a primitive, function, or macro.\n", symbol);
+ goto end;
+ }
+ if (VALTYPE(fn) == kFuncSExp || VALTYPE(fn) == kUserFuncSExp) {
SExpRef args = CDR(sexp);
ret = primitive_funcall(interp, CONS(fn, args));
goto end;
+ } else if (VALTYPE(fn) == kMacroSExp) {
+ SExpRef args = CDR(sexp);
+ SExpRef newast = lisp_macroexpand1(interp, fn, args);
+ PUSH_REG(newast);
+ ret = EVAL(newast);
+ POP_REG();
+ goto end;
}
- // TODO: macro / func
- ret = new_error(interp, "eval: \"%s\" is not a primitive, function, or macro.\n", symbol);
- goto end;
}
ret = new_error(interp, "eval: unknown syntax.\n");
end:
@@ -500,6 +520,14 @@ SExpRef new_lambda(Interp *interp, SExpRef param, SExpRef body, SExpRef 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;