Work on function stuff

This commit is contained in:
2025-07-03 01:36:25 +09:00
parent e557e58168
commit a19071c35c
5 changed files with 498 additions and 133 deletions

View File

@ -1,5 +1,7 @@
#include "lisp.h"
#include "read.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
@ -90,10 +92,7 @@ void _internal_lisp_delete_object(LispVal *val) {
LispFunction *fn = (LispFunction *) val;
lisp_unref(fn->doc);
lisp_unref(fn->args);
for (size_t i = 0; i < fn->n_kw; ++i) {
lisp_unref(fn->kwargs[i]);
}
lisp_free(fn->kwargs);
lisp_unref(fn->kwargs);
if (!fn->is_builtin) {
lisp_unref(fn->body);
}
@ -176,6 +175,7 @@ LispVal *make_lisp_symbol(LispVal *name) {
self->plist = Qnil;
self->function = Qunbound;
self->value = Qunbound;
self->is_constant = false;
return LISPVAL(self);
}
@ -213,6 +213,116 @@ LispVal *make_lisp_vector(LispVal **data, size_t length) {
return LISPVAL(self);
}
DEF_STATIC_SYMBOL(opt, "&opt");
DEF_STATIC_SYMBOL(key, "&key");
DEF_STATIC_SYMBOL(allow_other_keys, "&allow-other-keys");
DEF_STATIC_SYMBOL(rest, "&rest");
void set_function_args(LispFunction *func, LispVal *args) {
// in case func is static
if (func->args) {
lisp_unref(func->args);
}
if (func->kwargs) {
lisp_unref(func->kwargs);
}
int mode = 0; // required
bool has_opt = false; // mode 1
bool has_key = false; // mode 2
bool has_rest = false; // mode 3
func->n_req = 0;
func->n_opt = 0;
func->has_rest = false;
size_t n_kw = 0;
func->kwargs = lisp_ref(make_lisp_hashtable(Qnil, Qnil));
func->allow_other_keys = false;
FOREACH(arg, args) {
if (!SYMBOLP(arg) || VALUE_CONSTANTP(arg)) {
goto malformed;
} else if (arg == Qopt) {
if (has_opt || mode == 3) {
goto malformed;
}
has_opt = true;
mode = 1;
} else if (arg == Qkey) {
if (has_key || mode == 3) {
goto malformed;
}
has_key = true;
mode = 2;
} else if (arg == Qrest) {
if (has_rest) {
goto malformed;
}
has_rest = true;
mode = 3;
} else if (arg == Qallow_other_keys) {
if (func->allow_other_keys || mode != 2) {
goto malformed;
}
func->allow_other_keys = true;
mode = -1;
} else {
switch (mode) {
case 0:
++func->n_req;
break;
case 1:
++func->n_opt;
break;
case 2: {
LispString *sn = ((LispSymbol *) arg)->name;
char kns[sn->length + 2];
kns[0] = ':';
memcpy(kns + 1, sn->data, sn->length);
kns[sn->length + 1] = '\n';
LispVal *kn =
make_lisp_string(kns, sn->length + 1, false, false);
lisp_ref(kn);
Fputhash(func->kwargs, Fintern(kn), make_lisp_integer(n_kw));
lisp_unref(kn);
} break;
case 3:
if (func->has_rest) {
goto malformed;
}
func->has_rest = true;
mode = -1;
break;
case -1:
goto malformed;
}
}
}
// do this last
func->args = lisp_ref(args);
return;
malformed:
lisp_unref(func->kwargs);
Fthrow(Qmalformed_lambda_list_error, Fpair(args, Qnil));
}
LispVal *make_lisp_function(LispVal *args, LispVal *doc, LispVal *lexenv,
LispVal *body, bool is_macro) {
LispFunction *self = lisp_malloc(sizeof(LispFunction));
self->type = TYPE_FUNCTION;
self->ref_count = 0;
self->is_builtin = false;
self->is_macro = is_macro;
self->args = Qnil;
self->kwargs = Qnil;
void *cl = register_cleanup(&free_double_ptr, &self);
set_function_args(self, args);
cancel_cleanup(cl);
// do these after the potential throw
self->doc = lisp_ref(doc);
self->lexenv = lisp_ref(lexenv);
self->body = lisp_ref(body);
return LISPVAL(self);
}
LispVal *make_lisp_hashtable(LispVal *eq_fn, LispVal *hash_fn) {
LispHashtable *self = lisp_malloc(sizeof(LispHashtable));
self->type = TYPE_HASHTABLE;
@ -292,9 +402,9 @@ static bool hash_table_eq(LispHashtable *self, LispVal *v1, LispVal *v2) {
return !NILP(Fstrings_equal(v1, v2));
} else {
LispVal *eq_obj;
LispVal *args = make_list(2, v1, v2);
LispVal *args = const_list(2, v1, v2);
WITH_CLEANUP(args, {
eq_obj = lisp_ref(Ffuncall(self->eq_fn, args)); //
eq_obj = Ffuncall(self->eq_fn, args); //
});
lisp_ref(eq_obj);
bool result = !NILP(eq_obj);
@ -314,7 +424,7 @@ static uint64_t hash_table_hash(LispHashtable *self, LispVal *key) {
return hash;
} else {
LispVal *hash_obj;
LispVal *args = make_list(1, key);
LispVal *args = const_list(1, key);
WITH_CLEANUP(args, {
hash_obj = Ffuncall(self->hash_fn, args); //
});
@ -549,6 +659,18 @@ void *register_cleanup(lisp_cleanup_func_t fun, void *data) {
return entry;
}
void free_double_ptr(void *ptr) {
free(*(void **) ptr);
}
void unref_free_list_double_ptr(void *ptr) {
struct UnrefListData *data = ptr;
for (size_t i = 0; i < data->len; ++i) {
lisp_unref(data->vals[i]);
}
lisp_free(data->vals);
}
void cancel_cleanup(void *handle) {
struct CleanupHandlerEntry *entry = the_stack->cleanup_handlers;
if (entry == handle) {
@ -587,9 +709,11 @@ DEFUN(backtrace, "backtrace", ()) {
return head;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winfinite-recursion"
DEFUN(throw, "throw", (LispVal * signal, LispVal *rest)) {
CHECK_TYPE(TYPE_SYMBOL, signal);
LispVal *error_arg = make_list(2, Fpair(signal, rest), Fbacktrace());
LispVal *error_arg = const_list(2, Fpair(signal, rest), Fbacktrace());
for (; the_stack; stack_leave()) {
if (!the_stack->enable_handlers) {
continue;
@ -628,6 +752,7 @@ DEFUN(throw, "throw", (LispVal * signal, LispVal *rest)) {
fprintf(stderr, "Lisp will now exit...");
abort();
}
#pragma GCC diagnostic pop
DEF_STATIC_SYMBOL(shutdown_signal, "shutdown-signal");
DEF_STATIC_SYMBOL(type_error, "type-error");
@ -636,38 +761,55 @@ DEF_STATIC_SYMBOL(eof_error, "eof-error");
DEF_STATIC_SYMBOL(void_variable_error, "void-variable-error");
DEF_STATIC_SYMBOL(void_function_error, "void-function-error");
DEF_STATIC_SYMBOL(circular_error, "circular-error");
DEF_STATIC_SYMBOL(malformed_lambda_list_error, "malformed-lambda-list-error");
DEF_STATIC_SYMBOL(argument_error, "argument-error");
LispVal *Vobarray = Qnil;
void lisp_init() {
Vobarray = lisp_ref(make_lisp_hashtable(Qstrings_equal, Qhash_string));
#define REGISTER_SYMBOL(fn) \
Fputhash(Vobarray, LISPVAL(((LispSymbol *) Q##fn)->name), Q##fn)
REGISTER_SYMBOL(nil);
REGISTER_SYMBOL(t);
REGISTER_SYMBOL(opt);
REGISTER_SYMBOL(allow_other_keys);
REGISTER_SYMBOL(key);
REGISTER_SYMBOL(rest);
// TODO fill in the other fields
REGISTER_SYMBOL(pair);
REGISTER_SYMBOL(head);
REGISTER_SYMBOL(tail);
REGISTER_SYMBOL(quote);
REGISTER_SYMBOL(exit);
REGISTER_SYMBOL(print);
REGISTER_SYMBOL(println);
REGISTER_SYMBOL(not);
REGISTER_SYMBOL(when);
REGISTER_SYMBOL(add);
REGISTER_SYMBOL(if);
REGISTER_SYMBOL(setq);
#undef REGISTER_SYMBOL
REGISTER_FUNCTION(pair, "(head tail)",
"Return a new pair with HEAD and TAIL.");
REGISTER_FUNCTION(head, "(pair)", "Return the head of PAIR.");
REGISTER_FUNCTION(tail, "(pair)", "Return the tail of PAIR.");
REGISTER_FUNCTION(quote, "(form)", "Return FORM as read by the reader.");
REGISTER_FUNCTION(exit, "(&opt code)",
"Exit with CODE, defaulting to zero.");
REGISTER_FUNCTION(print, "(obj)",
"Print a human-readable representation of OBJ.");
REGISTER_FUNCTION(
println, "(obj)",
"Print a human-readable representation of OBJ followed by a newline.");
REGISTER_FUNCTION(not, "(obj)",
"Return t if OBJ is nil, otherwise return t.");
REGISTER_FUNCTION(when, "(cond &rest body)",
"Evaluate BODY if COND is non-nil.");
REGISTER_FUNCTION(add, "(&rest nums)", "Return the sun of NUMS.");
REGISTER_FUNCTION(
if, "(cond then &rest else)",
"Evaluate THEN if COND is non-nil, otherwise evaluate ELSE.");
REGISTER_FUNCTION(
setq, "(&rest args)",
"Set each of a number of variables to their respective values.");
REGISTER_FUNCTION(progn, "(&rest forms)", "Evaluate each of FORMS.");
REGISTER_FUNCTION(symbol_function, "(sym &opt resolve)", "");
REGISTER_FUNCTION(fset, "(sym new-func)", "");
}
void lisp_shutdown() {
UNREF_INPLACE(Vobarray);
}
void register_static_function(LispVal *func) {}
static LispVal *find_in_lexenv(LispVal *lexenv, LispVal *key) {
while (HASHTABLEP(lexenv)) {
LispVal *value = Fgethash(lexenv, key, Qunbound);
@ -690,7 +832,7 @@ static LispVal *symbol_value_in_lexenv(LispVal *lexenv, LispVal *key) {
if (sym_val != Qunbound) {
return sym_val;
}
Fthrow(Qvoid_variable_error, make_list(1, key));
Fthrow(Qvoid_variable_error, const_list(1, key));
}
DEFUN(symbol_function, "symbol-function",
@ -730,30 +872,126 @@ static inline LispVal *eval_function_args(LispVal *args, LispVal *lexenv) {
return final_args;
}
static LispVal *call_builtin(LispVal *name, LispFunction *func, LispVal *args) {
// TODO actually implement this
size_t count = list_length(args);
switch (count) {
case 0:
return ((LispVal * (*) ()) func->builtin)();
case 1:
return ((LispVal * (*) (LispVal * val)) func->builtin)(Fhead(args));
case 2:
return ((LispVal * (*) (LispVal * val1, LispVal * val2))
func->builtin)(Fhead(args), Fhead(Ftail(args)));
case 3:
return ((LispVal * (*) (LispVal * val1, LispVal * val2, LispVal * val3))
func->builtin)(Fhead(args), Fhead(Ftail(args)),
Fhead(Ftail(Ftail(args))));
/* case 4: */
/* return func->builtin(HEAD(args)); */
/* case 5: */
/* return func->builtin(HEAD(args)); */
/* case 6: */
/* return func->builtin(HEAD(args)); */
default:
return ((LispVal * (*) (LispVal * val)) func->builtin)(args);
static LispVal **process_builtin_args(LispFunction *func, LispVal *args,
size_t *nargs) {
size_t raw_count =
(func->n_req + func->n_opt + ((LispHashtable *) func->kwargs)->count
+ (func->has_rest));
*nargs = raw_count;
LispVal **vec = lisp_malloc(sizeof(LispVal *) * raw_count);
memset(vec, 0, sizeof(LispVal *) * raw_count);
LispVal *rest = Qnil;
LispVal *rest_end;
size_t have_count = 0;
LispVal *index;
LispVal *arg = Qnil; // last arg processed
while (!NILP(args)) {
arg = Fhead(args);
if (have_count < func->n_req + func->n_opt) {
vec[have_count++] = lisp_ref(arg);
} else if (KEYWORDP(arg)
&& !NILP(index = Fgethash(func->kwargs, arg, Qnil))
&& NILP(rest)) {
LispInteger *n = (LispInteger *) index;
if (vec[n->value]) {
goto multikey;
}
args = Ftail(args);
if (NILP(args)) {
goto key_no_val;
}
vec[n->value] = lisp_ref(Fhead(arg));
} else if (KEYWORDP(arg) && !func->allow_other_keys && NILP(rest)) {
goto unknown_key;
} else if (!func->has_rest) {
goto too_many;
} else if (NILP(rest)) {
rest = Fpair(arg, Qnil);
rest_end = rest;
} else {
LispVal *new_end = Fpair(arg, Qnil);
Fsettail(rest_end, new_end);
rest_end = new_end;
}
args = Ftail(args);
}
if (have_count < func->n_req) {
goto too_few;
}
if (func->has_rest) {
vec[raw_count - 1] = lisp_ref(rest);
}
for (size_t i = 0; i < raw_count; ++i) {
if (!vec[i]) {
vec[i] = Qnil;
}
}
return vec;
// TODO different messages
key_no_val:
too_many:
multikey:
unknown_key:
too_few:
lisp_unref(rest);
for (size_t i = 0; i < raw_count; ++i) {
if (vec[i]) {
lisp_unref(vec[i]);
}
}
lisp_free(vec);
Fthrow(Qargument_error, Qnil);
return NULL;
}
static LispVal *call_builtin(LispVal *name, LispFunction *func, LispVal *args) {
size_t nargs;
LispVal **arg_vec = process_builtin_args(func, args, &nargs);
struct UnrefListData cleanup_data = {.vals = arg_vec, .len = nargs};
void *cl = register_cleanup(&unref_free_list_double_ptr, &cleanup_data);
LispVal *retval;
switch (nargs) {
case 0:
retval = ((LispVal * (*) ()) func->builtin)();
break;
case 1:
retval = ((LispVal * (*) (LispVal *) ) func->builtin)(arg_vec[0]);
break;
case 2:
retval = ((LispVal * (*) (LispVal *, LispVal *) )
func->builtin)(arg_vec[0], arg_vec[1]);
break;
case 3:
retval = ((LispVal * (*) (LispVal *, LispVal *, LispVal *) )
func->builtin)(arg_vec[0], arg_vec[1], arg_vec[2]);
break;
case 4:
retval =
((LispVal * (*) (LispVal *, LispVal *, LispVal *, LispVal *) )
func->builtin)(arg_vec[0], arg_vec[1], arg_vec[2], arg_vec[3]);
break;
case 5:
retval =
((LispVal
* (*) (LispVal *, LispVal *, LispVal *, LispVal *, LispVal *) )
func->builtin)(arg_vec[0], arg_vec[1], arg_vec[2], arg_vec[3],
arg_vec[4]);
break;
case 6:
retval = ((LispVal
* (*) (LispVal *, LispVal *, LispVal *, LispVal *, LispVal *,
LispVal *) ) func->builtin)(arg_vec[0], arg_vec[1],
arg_vec[2], arg_vec[3],
arg_vec[4], arg_vec[5]);
break;
default:
fprintf(stderr,
"Builtin functions cannot have more than 6 arguments!\n");
abort();
}
cancel_cleanup(cl);
unref_free_list_double_ptr(&cleanup_data);
return retval;
}
static LispVal *call_lisp_function(LispVal *name, LispFunction *func,
@ -773,7 +1011,7 @@ static LispVal *call_function(LispVal *func, LispVal *args,
fobj = (LispFunction *) Fsymbol_function(func, Qt);
}
if (LISPVAL(fobj) == Qunbound) {
Fthrow(Qvoid_function_error, make_list(1, func));
Fthrow(Qvoid_function_error, const_list(1, func));
}
CHECK_TYPE(TYPE_FUNCTION, fobj);
if (!fobj->is_macro && eval_args) {
@ -781,7 +1019,8 @@ static LispVal *call_function(LispVal *func, LispVal *args,
}
lisp_ref(args);
LispVal *retval = Qnil;
WITH_PUSH_FRAME(func, args, false, {
// builtin macros inherit their parents lexenv
WITH_PUSH_FRAME(func, args, fobj->is_macro && fobj->is_builtin, {
void *cl_handle = register_cleanup(
(lisp_cleanup_func_t) &lisp_unref_double_ptr, &args);
check_args_for_function(fobj, args);
@ -883,7 +1122,7 @@ DEFUN(exit, "exit", (LispVal * code)) {
if (!NILP(code) && !INTEGERP(code)) {
Fthrow(Qtype_error, Qnil);
}
Fthrow(Qshutdown_signal, make_list(1, code));
Fthrow(Qshutdown_signal, const_list(1, code));
}
DEFMACRO(quote, "'", (LispVal * form)) {
@ -912,14 +1151,22 @@ DEFMACRO(if, "if", (LispVal * cond, LispVal *t, LispVal *nil)) {
if (!NILP(res)) {
retval = Feval(t);
} else {
retval = Feval(nil);
LispVal *body = Fpair(Qprogn, nil);
WITH_CLEANUP(body, {
retval = Feval(body); //
});
}
});
return retval;
}
DEFMACRO(when, "when", (LispVal * cond, LispVal *t)) {
return Fif(cond, t, Qnil);
LispVal *body = Fpair(Qprogn, t);
LispVal *retval = Qnil;
WITH_CLEANUP(body, {
retval = Fif(cond, body, Qnil); //
});
return retval;
}
DEFUN(add, "+", (LispVal * n1, LispVal *n2)) {
@ -949,6 +1196,23 @@ DEFMACRO(setq, "setq", (LispVal * name, LispVal *value)) {
return evaled;
}
DEFMACRO(progn, "progn", (LispVal * forms)) {
LispVal *retval = Qnil;
FOREACH(form, forms) {
retval = Feval(form);
}
return retval;
}
DEFUN(fset, "fset", (LispVal * sym, LispVal *new_func)) {
CHECK_TYPE(TYPE_SYMBOL, sym);
LispSymbol *sobj = ((LispSymbol *) sym);
// TODO make sure this is not constant
lisp_unref(sobj->function);
sobj->function = lisp_ref(new_func);
return new_func;
}
static void debug_dump_real(FILE *stream, void *obj, bool first) {
switch (TYPEOF(obj)) {
case TYPE_STRING: {