Files
glisp/src/list.c
T
2026-09-08 11:27:11 -07:00

299 lines
7.2 KiB
C

#include "list.h"
#include "function.h"
DEFINE_SYMBOL(circular_list_error, "circular-list-error");
DEFINE_CONDITION_CLASS(circular_list_error, error);
intptr_t list_length(LispVal *list) {
assert(LISTP(list));
LispVal *tortise = list;
LispVal *hare = list;
intptr_t length = 0;
while (CONSP(tortise)) {
tortise = XCDR_SAFE(tortise);
hare = XCDR_SAFE(XCDR_SAFE(hare));
if (!NILP(hare) && tortise == hare) {
return -1;
}
++length;
}
return length;
}
bool list_length_eq(LispVal *list, intptr_t size) {
while (size && CONSP(list)) {
list = XCDR(list);
--size;
}
return size == 0 && NILP(list);
}
DEFUN(consp, "consp", (LispVal * val), "(val)", "") {
return CONSP(val) ? Qt : Qnil;
}
DEFUN(atom, "atom", (LispVal * val), "(val)", "") {
return ATOM(val) ? Qt : Qnil;
}
DEFUN(cons, "cons", (LispVal * car, LispVal *cdr), "(car cdr)",
"Construct a new cons object from CAR and CDR.") {
return CONS(car, cdr);
}
DEFUN(car, "car", (LispVal * list), "(list)", "") {
CHECK_LISTP(list);
return NILP(list) ? Qnil : XCAR(list);
}
DEFUN(cdr, "cdr", (LispVal * list), "(list)", "") {
CHECK_LISTP(list);
return NILP(list) ? Qnil : XCDR(list);
}
DEFUN(rplaca, "rplaca", (LispVal * cons, LispVal *newcar), "(cons newcar)",
"") {
CHECK_TYPE(cons, TYPE_CONS);
RPLACA(cons, newcar);
return newcar;
}
DEFUN(rplacd, "rplacd", (LispVal * cons, LispVal *newcdr), "(cons newcdr)",
"") {
CHECK_TYPE(cons, TYPE_CONS);
RPLACD(cons, newcdr);
return newcdr;
}
DEFUN(length_eq, "length=", (LispVal * list, LispVal *length), "(list length)",
"Return non-nil if LIST's length is LENGTH.") {
CHECK_LISTP(list);
return list_length_eq(list, XFIXNUM(length)) ? Qt : Qnil;
}
DEFUN(nreverse, "nreverse", (LispVal * list), "(list)", "") {
LispVal *rev = Qnil;
while (!NILP(list)) {
CHECK_LISTP(list);
LispVal *next = XCDR(list);
RPLACD(list, rev);
rev = list;
list = next;
}
return rev;
}
DEFUN(reverse, "reverse", (LispVal * list), "(list)", "") {
LispVal *out = Qnil;
DOLIST_SAFE(cur, list) {
out = CONS(cur, out);
}
return out;
}
DEFUN(append, "append", (LispVal * lists), "(&rest lists)", "") {
LispVal *out = Qnil;
LispVal *end = Qnil;
DOTAILS(rest, lists) {
LispVal *cur = XCAR(rest);
CHECK_LISTP(cur);
if (NILP(cur)) {
continue;
}
if (!NILP(XCDR(rest))) {
cur = Fcopy_list(cur);
}
if (NILP(out)) {
out = cur;
} else {
RPLACD(end, cur);
}
if (!NILP(XCDR(rest))) {
end = Flast(cur);
if (!NILP(XCDR(end))) {
signal_type_error(cur, Qlist);
}
}
}
return out;
}
DEFUN(nconc, "nconc", (LispVal * lists), "(&rest lists)", "") {
LispVal *out = Qnil;
LispVal *end = Qnil;
DOTAILS(rest, lists) {
LispVal *cur = XCAR(rest);
CHECK_LISTP(cur);
if (NILP(cur)) {
continue;
}
if (NILP(out)) {
out = cur;
end = Flast(cur);
} else {
RPLACD(end, cur);
end = Flast(cur);
}
if (!NILP(XCDR(end)) && !NILP(XCDR(rest))) {
signal_type_error(cur, Qlist);
}
}
return out;
}
DEFUN(last, "last", (LispVal * list), "(list)", "") {
if (NILP(list)) {
return Qnil;
}
CHECK_LISTP(list);
while (CONSP(XCDR(list))) {
list = XCDR(list);
}
return list;
}
DEFUN(listp, "listp", (LispVal * obj), "(obj)", "") {
return LISTP(obj) ? Qt : Qnil;
}
DEFUN(proper_list_p, "proper-list-p", (LispVal * obj), "(obj)", "") {
CHECK_LISTP(obj);
return (NILP(Fcircular_list_p(obj)) && NILP(XCDR(Flast(obj)))) ? Qt : Qnil;
}
DEFUN(circular_list_p, "circular-list-p", (LispVal * obj), "(obj)", "") {
CHECK_LISTP(obj);
return list_length(obj) == -1 ? Qt : Qnil;
}
DEFUN(dotted_list_p, "dotted-list-p", (LispVal * obj), "(obj)", "") {
CHECK_LISTP(obj);
return (NILP(Fcircular_list_p(obj)) && !NILP(XCDR(Flast(obj)))) ? Qt : Qnil;
}
DEFUN(list, "list", (LispVal * args), "(&rest args)", "") {
return args;
}
DEFUN(list_star, "list*", (LispVal * arg, LispVal *args), "(arg &rest args)",
"") {
if (NILP(args)) {
return arg;
} else if (NILP(XCDR(args))) {
return CONS(arg, XCAR(args));
}
LispVal *out = CONS(arg, args);
args = out;
while (CONSP(args) && CONSP(XCDR(args)) && CONSP(XCDR(XCDR(args)))) {
args = XCDR(args);
}
RPLACD(args, XCAR(XCDR(args)));
return out;
}
DEFUN(copy_list, "copy-list", (LispVal * list), "(list)", "") {
LispVal *start = Qnil;
LispVal *end = NULL;
DOTAILS(rest, list) {
if (!CONSP(rest)) {
RPLACD(end, rest);
break;
}
if (NILP(start)) {
start = CONS(XCAR(rest), Qnil);
end = start;
} else {
RPLACD(end, CONS(XCAR(rest), Qnil));
end = XCDR(end);
}
}
return start;
}
LispVal *nth(size_t n, LispVal *list) {
size_t i = 0;
DOLIST_SAFE(elt, list) {
if (i == n) {
return elt;
}
++i;
}
return Qnil;
}
DEFUN(nth, "nth", (LispVal * n, LispVal *list), "(n list)", "") {
CHECK_TYPE(n, TYPE_FIXNUM);
return nth(XFIXNUM(n), list);
}
DEFUN(memq, "memq", (LispVal * elt, LispVal *list), "(elt list)", "") {
DOTAILS(rest, list) {
CHECK_LISTP(rest);
if (elt == XCAR(rest)) {
return rest;
}
}
return Qnil;
}
DEFUN(member_if, "member-if", (LispVal * pred, LispVal *list), "(pred list)",
"") {
DOTAILS(rest, list) {
CHECK_LISTP(rest);
if (!NILP(CALL(pred, XCAR(rest)))) {
return rest;
}
}
return Qnil;
}
DEFUN(plist_put, "plist-put", (LispVal * plist, LispVal *prop, LispVal *value),
"(plist prop value)", "") {
LispVal *rest = plist;
while (!NILP(rest)) {
CHECK_LISTP(rest);
CHECK_TYPE(XCDR(rest), TYPE_CONS);
if (EQ(XCAR(rest), prop)) {
RPLACA(XCDR(rest), value);
return plist;
}
rest = XCDR(XCDR(rest));
}
return CONS(prop, CONS(value, plist));
}
DEFUN(plist_get, "plist-get", (LispVal * plist, LispVal *prop, LispVal *def),
"(plist prop &optional default)", "") {
while (!NILP(plist)) {
CHECK_LISTP(plist);
CHECK_TYPE(XCDR(plist), TYPE_CONS);
if (EQ(XCAR(plist), prop)) {
return SECOND(plist);
}
plist = XCDR(XCDR(plist));
}
return def;
}
DEFUN(assoc, "assoc", (LispVal * key, LispVal *alist, LispVal *pred),
"(key alist &optional pred)", "") {
if (NILP(pred)) {
DOLIST_SAFE(ent, alist) {
CHECK_TYPE(ent, TYPE_CONS);
if (EQ(key, XCAR(ent))) {
return ent;
}
}
return Qnil;
} else {
DOLIST_SAFE(ent, alist) {
CHECK_TYPE(ent, TYPE_CONS);
if (!NILP(CALL(pred, key, XCAR(ent)))) {
return ent;
}
}
return Qnil;
}
}