ecl/src/c/list.d
Marius Gerbershagen 239a2aa4f1 threading: add support for atomic operations
We support both compare-and-swap and fetch-and-add (via
    atomic-incf/decf) on the most common places and define an
    extension interface for compare-and-swap similiar to the one in
    SBCL.

    These changes introduce new dependencies on the libatomic_ops
    macros AO_fetch_compare_and_swap and AO_fetch_and_add.
2018-10-15 19:55:39 +02:00

1110 lines
24 KiB
C

/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* vim: set filetype=c tabstop=2 shiftwidth=2 expandtab: */
/*
* list.d - list manipulating routines
*
* Copyright (c) 1984 Taiichi Yuasa and Masami Hagiya
* Copyright (c) 1990 Giuseppe Attardi
* Copyright (c) 2001 Juan Jose Garcia Ripoll
*
* See file 'LICENSE' for the copyright details.
*
*/
#include <ecl/ecl.h>
#include <ecl/ecl-inl.h>
#include <ecl/internal.h>
struct cl_test {
bool (*test_c_function)(struct cl_test *, cl_object);
cl_object (*key_c_function)(struct cl_test *, cl_object);
cl_env_ptr env;
cl_object key_function;
cl_objectfn key_fn;
cl_object test_function;
cl_objectfn test_fn;
cl_object item_compared;
};
static cl_object subst(struct cl_test *t, cl_object new_obj, cl_object tree);
static cl_object nsubst(struct cl_test *t, cl_object new_obj, cl_object tree);
static cl_object sublis(struct cl_test *t, cl_object alist, cl_object tree);
static cl_object nsublis(struct cl_test *t, cl_object alist, cl_object tree);
static cl_object do_assoc(struct cl_test *t, cl_object alist);
#define TEST(t,k) ((t)->test_c_function)((t),(k))
#define KEY(t,x) ((t)->key_c_function)((t),(x))
#define close_test(t) (void)0
static bool
test_compare(struct cl_test *t, cl_object x)
{
x = KEY(t,x);
t->env->function = t->test_function;
return t->test_fn(2, t->item_compared, x) != ECL_NIL;
}
static bool
test_compare_not(struct cl_test *t, cl_object x)
{
x = KEY(t,x);
t->env->function = t->test_function;
return t->test_fn(2, t->item_compared, x) == ECL_NIL;
}
static bool
test_eq(struct cl_test *t, cl_object x)
{
return (t->item_compared == KEY(t,x));
}
static bool
test_eql(struct cl_test *t, cl_object x)
{
return ecl_eql(t->item_compared, KEY(t,x));
}
static bool
test_equal(struct cl_test *t, cl_object x)
{
return ecl_equal(t->item_compared, KEY(t,x));
}
static bool
test_equalp(struct cl_test *t, cl_object x)
{
return ecl_equalp(t->item_compared, KEY(t,x));
}
static cl_object
key_function(struct cl_test *t, cl_object x)
{
t->env->function = t->key_function;
return t->key_fn(1,x);
}
static cl_object
key_identity(struct cl_test *t, cl_object x)
{
return x;
}
static void
setup_test(struct cl_test *t, cl_object item, cl_object test,
cl_object test_not, cl_object key)
{
cl_env_ptr env = t->env = ecl_process_env();
t->item_compared = item;
if (test != ECL_NIL) {
if (test_not != ECL_NIL)
FEerror("Both :TEST and :TEST-NOT are specified.", 0);
t->test_function = test = si_coerce_to_function(test);
if (test == ECL_SYM_FUN(@'eq')) {
t->test_c_function = test_eq;
} else if (test == ECL_SYM_FUN(@'eql')) {
t->test_c_function = test_eql;
} else if (test == ECL_SYM_FUN(@'equal')) {
t->test_c_function = test_equal;
} else if (test == ECL_SYM_FUN(@'equalp')) {
t->test_c_function = test_equalp;
} else {
t->test_c_function = test_compare;
t->test_fn = ecl_function_dispatch(env, test);
t->test_function = env->function;
}
} else if (test_not != ECL_NIL) {
t->test_c_function = test_compare_not;
test_not = si_coerce_to_function(test_not);
t->test_fn = ecl_function_dispatch(env, test_not);
t->test_function = env->function;
} else {
t->test_c_function = test_eql;
}
if (key != ECL_NIL) {
key = si_coerce_to_function(key);
t->key_fn = ecl_function_dispatch(env, key);
t->key_function = env->function;
t->key_c_function = key_function;
} else {
t->key_c_function = key_identity;
}
}
@(defun list (&rest args)
cl_object head = ECL_NIL;
@
if (narg--) {
cl_object tail = head = ecl_list1(ecl_va_arg(args));
while (narg--) {
cl_object cons = ecl_list1(ecl_va_arg(args));
ECL_RPLACD(tail, cons);
tail = cons;
}
}
@(return head);
@)
@(defun list* (&rest args)
cl_object head;
@
if (narg == 0)
FEwrong_num_arguments(@[list*]);
head = ecl_va_arg(args);
if (--narg) {
cl_object tail = head = ecl_list1(head);
while (--narg) {
cl_object cons = ecl_list1(ecl_va_arg(args));
ECL_RPLACD(tail, cons);
tail = cons;
}
ECL_RPLACD(tail, ecl_va_arg(args));
}
@(return head);
@)
static cl_object *
append_into(cl_object head, cl_object *tail, cl_object l)
{
if (!Null(*tail)) {
/* (APPEND '(1 . 2) 3) */
FEtype_error_proper_list(head);
}
while (CONSP(l)) {
cl_object cons = ecl_list1(ECL_CONS_CAR(l));
*tail = cons;
tail = &ECL_CONS_CDR(cons);
l = ECL_CONS_CDR(l);
}
*tail = l;
return tail;
}
@(defun append (&rest rest)
cl_object head = ECL_NIL, *tail = &head;
@
for (; narg > 1; narg--) {
cl_object other = ecl_va_arg(rest);
tail = append_into(head, tail, other);
}
if (narg) {
if (!Null(*tail)) {
/* (APPEND '(1 . 2) 3) */
FEtype_error_proper_list(head);
}
*tail = ecl_va_arg(rest);
}
@(return head);
@)
cl_object
ecl_append(cl_object x, cl_object y)
{
cl_object head = ECL_NIL;
cl_object *tail = &head;
if (!Null(x)) {
tail = append_into(head, tail, x);
}
if (!Null(*tail)) {
/* (APPEND '(1 . 2) 3) */
FEtype_error_proper_list(head);
}
*tail = y;
return head;
}
#define LENTH(n) (cl_object x) { \
const cl_env_ptr the_env = ecl_process_env(); \
ecl_return1(the_env, ecl_nth(n, x)); \
}
cl_object @fifth LENTH(4)
cl_object @sixth LENTH(5)
cl_object @seventh LENTH(6)
cl_object @eighth LENTH(7)
cl_object @ninth LENTH(8)
cl_object @tenth LENTH(9)
#undef LENTH
static bool
tree_equal(struct cl_test *t, cl_object x, cl_object y)
{
BEGIN:
if (CONSP(x)) {
if (CONSP(y)) {
if (tree_equal(t, ECL_CONS_CAR(x), ECL_CONS_CAR(y))) {
x = ECL_CONS_CDR(x);
y = ECL_CONS_CDR(y);
goto BEGIN;
} else {
return(FALSE);
}
} else {
return(FALSE);
}
} else {
t->item_compared = x;
if (TEST(t, y))
return(TRUE);
else
return(FALSE);
}
}
@(defun tree_equal (x y &key test test_not)
struct cl_test t;
cl_object output;
@
setup_test(&t, ECL_NIL, test, test_not, ECL_NIL);
output = tree_equal(&t, x, y)? ECL_T : ECL_NIL;
close_test(&t);
@(return output);
@)
cl_object
cl_endp(cl_object x)
{
cl_object output = ECL_NIL;
if (Null(x)) {
output = ECL_T;
} else if (ecl_unlikely(!LISTP(x))) {
FEwrong_type_only_arg(@[endp], x, @[list]);
}
@(return output);
}
bool
ecl_endp(cl_object x)
{
if (Null(x)) {
return TRUE;
} else if (ecl_unlikely(!LISTP(x))) {
FEwrong_type_only_arg(@[endp], x, @[list]);
}
return FALSE;
}
cl_object
cl_list_length(cl_object x)
{
cl_fixnum n;
cl_object fast, slow;
/* INV: A list's length always fits in a fixnum */
fast = slow = x;
for (n = 0; !Null(fast); n++, fast = ECL_CONS_CDR(fast)) {
if (ecl_unlikely(!LISTP(fast))) {
FEtype_error_list(fast);
}
if (n & 1) {
/* Circular list? */
if (slow == fast) @(return ECL_NIL);
slow = ECL_CONS_CDR(slow);
}
}
@(return ecl_make_fixnum(n));
}
cl_object
si_proper_list_p(cl_object x)
{
cl_fixnum n;
cl_object fast, slow, test = ECL_T;
/* INV: A list's length always fits in a fixnum */
fast = slow = x;
for (n = 0; !Null(fast); n++, fast = ECL_CONS_CDR(fast)) {
if (!LISTP(fast)) {
test = ECL_NIL;
break;
}
if (n & 1) {
/* Circular list? */
if (slow == fast) {
test = ECL_NIL;
break;
}
slow = ECL_CONS_CDR(slow);
}
}
@(return test);
}
cl_object
cl_nth(cl_object n, cl_object x)
{
@(return ecl_nth(ecl_to_size(n), x));
}
cl_object
ecl_nth(cl_fixnum n, cl_object x)
{
if (n < 0)
FEtype_error_index(x, n);
/* INV: No need to check for circularity since we visit
at most `n' conses */
for (; n > 0 && CONSP(x); n--)
x = ECL_CONS_CDR(x);
if (Null(x))
return ECL_NIL;
if (!LISTP(x))
FEtype_error_list(x);
return ECL_CONS_CAR(x);
}
cl_object
cl_nthcdr(cl_object n, cl_object x)
{
@(return ecl_nthcdr(ecl_to_size(n), x));
}
cl_object
ecl_nthcdr(cl_fixnum n, cl_object x)
{
if (n < 0)
FEtype_error_index(x, n);
while (n-- > 0 && !Null(x)) {
if (LISTP(x)) {
x = ECL_CONS_CDR(x);
} else {
FEtype_error_list(x);
}
}
return x;
}
cl_object
ecl_last(cl_object l, cl_index n)
{
/* The algorithm is very simple. We run over the list with
* two pointers, "l" and "r". The separation between both
* must be "n", so that when "l" finds no more conses, "r"
* contains the output. */
cl_object r;
for (r = l; n && CONSP(r); n--, r = ECL_CONS_CDR(r))
;
/* If "l" has not moved, we have to ensure that it is a list */
if (r == l) {
if (!LISTP(r)) FEtype_error_list(l);
while (CONSP(r)) {
r = ECL_CONS_CDR(r);
}
return r;
} else if (n == 0) {
while (CONSP(r)) {
r = ECL_CONS_CDR(r);
l = ECL_CONS_CDR(l);
}
return l;
} else {
return l;
}
}
@(defun last (l &optional (k ecl_make_fixnum(1)))
@
if (ecl_t_of(k) == t_bignum) {
@(return l);
}
@(return ecl_last(l, ecl_to_size(k)));
@)
@(defun make_list (size &key initial_element &aux x)
cl_fixnum i;
@
/* INV: ecl_to_size() signals a type-error if SIZE is not a integer >=0 */
i = ecl_to_size(size);
while (i-- > 0)
x = CONS(initial_element, x);
@(return x);
@)
cl_object
cl_copy_list(cl_object x)
{
cl_object copy;
if (ecl_unlikely(!LISTP(x))) {
FEwrong_type_only_arg(@[copy-list], x, @[list]);
}
copy = ECL_NIL;
if (!Null(x)) {
cl_object tail = copy = ecl_list1(CAR(x));
while (x = ECL_CONS_CDR(x), CONSP(x)) {
cl_object cons = ecl_list1(ECL_CONS_CAR(x));
ECL_RPLACD(tail, cons);
tail = cons;
}
ECL_RPLACD(tail, x);
}
@(return copy);
}
static cl_object
duplicate_pairs(cl_object x)
{
cl_object p = ECL_CONS_CAR(x);
if (CONSP(p))
p = CONS(ECL_CONS_CAR(p), ECL_CONS_CDR(p));
return ecl_list1(p);
}
cl_object
cl_copy_alist(cl_object x)
{
cl_object copy;
if (ecl_unlikely(!LISTP(x))) {
FEwrong_type_only_arg(@[copy-alist], x, @[list]);
}
copy = ECL_NIL;
if (!Null(x)) {
cl_object tail = copy = duplicate_pairs(x);
while (x = ECL_CONS_CDR(x), !Null(x)) {
if (!LISTP(x)) {
FEtype_error_list(x);
} else {
cl_object cons = duplicate_pairs(x);
tail = ECL_RPLACD(tail, cons);
tail = cons;
}
}
}
@(return copy);
}
static cl_object
do_copy_tree(cl_object x)
{
if (CONSP(x)) {
x = CONS(do_copy_tree(ECL_CONS_CAR(x)),
do_copy_tree(ECL_CONS_CDR(x)));
}
return x;
}
cl_object
cl_copy_tree(cl_object x)
{
@(return do_copy_tree(x));
}
cl_object
cl_revappend(cl_object x, cl_object y)
{
loop_for_in(x) {
y = CONS(ECL_CONS_CAR(x),y);
} end_loop_for_in;
@(return y);
}
@(defun nconc (&rest lists)
cl_object head = ECL_NIL, tail = ECL_NIL;
@
while (narg--) {
cl_object new_tail, other = ecl_va_arg(lists);
if (Null(other)) {
new_tail = tail;
} else if (CONSP(other)) {
new_tail = ecl_last(other, 1);
} else {
if (narg) FEtype_error_list(other);
new_tail = tail;
}
if (Null(head)) {
head = other;
} else {
ECL_RPLACD(tail, other);
}
tail = new_tail;
}
@(return head);
@)
cl_object
ecl_nconc(cl_object l, cl_object y)
{
if (Null(l)) {
return y;
} else {
ECL_RPLACD(ecl_last(l, 1), y);
return l;
}
}
cl_object
cl_nreconc(cl_object l, cl_object y)
{
cl_object x, z;
/* INV: when a circular list is "reconc'ed", the pointer ends
up at the beginning of the original list, hence we need no
slow pointer */
for (x = l; !Null(x); ) {
if (!LISTP(x)) FEtype_error_list(x);
z = x;
x = ECL_CONS_CDR(x);
if (x == l) FEcircular_list(l);
ECL_RPLACD(z, y);
y = z;
}
@(return y);
}
cl_object
ecl_butlast(cl_object l, cl_index n)
{
/* See LAST for details on this algorithm */
cl_object r;
for (r = l; n && CONSP(r); n--, r = ECL_CONS_CDR(r))
;
if (Null(r)) {
return ECL_NIL;
} else if (!LISTP(r)) {
/* We reach here either because l is shorter than n conses,
* or because it is not a list */
if (r == l) FEtype_error_list(r);
return ECL_NIL;
} else {
/* We reach here because l has at least n conses and
* thus we can take CAR(l) */
cl_object head, tail;
head = tail = ecl_list1(CAR(l));
while (l = ECL_CONS_CDR(l), r = ECL_CONS_CDR(r), CONSP(r)) {
cl_object cons = ecl_list1(ECL_CONS_CAR(l));
ECL_RPLACD(tail, cons);
tail = cons;
}
return head;
}
}
@(defun butlast (lis &optional (nn ecl_make_fixnum(1)))
@
/* INV: No list has more than MOST_POSITIVE_FIXNUM elements */
if (ecl_t_of(nn) == t_bignum) {
@(return ECL_NIL);
}
/* INV: ecl_to_size() signals a type-error if NN is not an integer >=0 */
@(return ecl_butlast(lis, ecl_to_size(nn)));
@)
cl_object
ecl_nbutlast(cl_object l, cl_index n)
{
cl_object r;
if (ecl_unlikely(!LISTP(l)))
FEwrong_type_only_arg(@[nbutlast], l, @[list]);
for (n++, r = l; n && CONSP(r); n--, r = ECL_CONS_CDR(r))
;
if (n == 0) {
cl_object tail = l;
while (CONSP(r)) {
tail = ECL_CONS_CDR(tail);
r = ECL_CONS_CDR(r);
}
ECL_RPLACD(tail, ECL_NIL);
return l;
}
return ECL_NIL;
}
@(defun nbutlast (lis &optional (nn ecl_make_fixnum(1)))
@
/* INV: No list has more than MOST_POSITIVE_FIXNUM elements */
if (ecl_t_of(nn) == t_bignum) {
@(return ECL_NIL);
}
/* INV: ecl_to_size() signas a type-error if NN is not an integer >=0 */
@(return ecl_nbutlast(lis, ecl_to_size(nn)));
@)
cl_object
cl_ldiff(cl_object x, cl_object y)
{
cl_object head = ECL_NIL;
if (ecl_unlikely(!LISTP(x))) {
FEwrong_type_only_arg(@[ldiff], x, @[list]);
}
/* Here we use that, if X or Y are CONS, then (EQL X Y)
* only when X == Y */
if (!Null(x) && (x != y)) {
cl_object tail = head = ecl_list1(ECL_CONS_CAR(x));
while (1) {
x = ECL_CONS_CDR(x);
if (!CONSP(x)) {
if (!ecl_eql(x, y)) {
ECL_RPLACD(tail, x);
}
break;
} else if (x == y) {
break;
} else {
cl_object cons = ecl_list1(ECL_CONS_CAR(x));
ECL_RPLACD(tail, cons);
tail = cons;
}
}
}
@(return head);
}
cl_object
cl_rplaca(cl_object x, cl_object v)
{
if (ecl_unlikely(!ECL_CONSP(x)))
FEwrong_type_nth_arg(@[rplaca], 1, x, @[cons]);
ECL_RPLACA(x, v);
@(return x);
}
cl_object
cl_rplacd(cl_object x, cl_object v)
{
if (ecl_unlikely(!ECL_CONSP(x)))
FEwrong_type_nth_arg(@[rplacd], 1, x, @[cons]);
ECL_RPLACD(x, v);
@(return x);
}
#ifdef ECL_THREADS
cl_object
mp_compare_and_swap_car(cl_object x, cl_object old, cl_object new)
{
if (ecl_unlikely(!ECL_CONSP(x)))
FEwrong_type_nth_arg(@[mp::compare-and-swap-car], 1, x, @[cons]);
return ecl_compare_and_swap(&ECL_CONS_CAR(x), old, new);
}
cl_object
mp_atomic_incf_car(cl_object x, cl_object increment)
{
if (ecl_unlikely(!ECL_CONSP(x)))
FEwrong_type_nth_arg(@[mp::atomic-incf-car], 1, x, @[cons]);
return ecl_atomic_incf(&ECL_CONS_CAR(x), increment);
}
cl_object
mp_compare_and_swap_cdr(cl_object x, cl_object old, cl_object new)
{
if (ecl_unlikely(!ECL_CONSP(x)))
FEwrong_type_nth_arg(@[mp::compare-and-swap-cdr], 1, x, @[cons]);
return ecl_compare_and_swap(&ECL_CONS_CDR(x), old, new);
}
cl_object
mp_atomic_incf_cdr(cl_object x, cl_object increment)
{
if (ecl_unlikely(!ECL_CONSP(x)))
FEwrong_type_nth_arg(@[mp::atomic-incf-cdr], 1, x, @[cons]);
return ecl_atomic_incf(&ECL_CONS_CDR(x), increment);
}
#endif /* ECL_THREADS */
@(defun subst (new_obj old_obj tree &key test test_not key)
struct cl_test t;
cl_object output;
@
setup_test(&t, old_obj, test, test_not, key);
output = subst(&t, new_obj, tree);
close_test(&t);
@(return output);
@)
static cl_object
subst(struct cl_test *t, cl_object new_obj, cl_object tree)
{
if (TEST(t, tree)) {
return new_obj;
} else if (ECL_ATOM(tree)) {
return tree;
} else {
cl_object head, tail = ECL_NIL;
do {
cl_object cons = subst(t, new_obj, ECL_CONS_CAR(tree));
cons = ecl_cons(cons, tree = ECL_CONS_CDR(tree));
if (Null(tail)) {
head = cons;
} else {
ECL_RPLACD(tail, cons);
}
tail = cons;
if (TEST(t, tree)) {
ECL_RPLACD(tail, new_obj);
return head;
}
} while (CONSP(tree));
return head;
}
}
@(defun nsubst (new_obj old_obj tree &key test test_not key)
struct cl_test t;
@
setup_test(&t, old_obj, test, test_not, key);
tree = nsubst(&t, new_obj, tree);
close_test(&t);
@(return tree);
@)
static cl_object
nsubst_cons(struct cl_test *t, cl_object new_obj, cl_object tree)
{
cl_object l = tree;
do {
cl_object o = ECL_CONS_CAR(l);
if (TEST(t, o)) {
ECL_RPLACA(l, new_obj);
} else if (CONSP(o)) {
nsubst_cons(t, new_obj, o);
}
o = ECL_CONS_CDR(l);
if (TEST(t, o)) {
ECL_RPLACD(l, new_obj);
return tree;
}
l = o;
} while (CONSP(l));
return tree;
}
static cl_object
nsubst(struct cl_test *t, cl_object new_obj, cl_object tree)
{
if (TEST(t, tree))
return new_obj;
if (CONSP(tree))
return nsubst_cons(t, new_obj, tree);
return tree;
}
@(defun sublis (alist tree &key test test_not key)
/* t[0] is the test for the objects in the tree, configured
with test, test_not and key. t[1] is the test for searching
in the association list.
*/
struct cl_test t[2];
@
setup_test(t, ECL_NIL, ECL_NIL, ECL_NIL, key);
setup_test(t+1, ECL_NIL, test, test_not, ECL_NIL);
tree = sublis(t, alist, tree);
close_test(t+1);
close_test(t);
@(return tree);
@)
/*
Sublis(alist, tree) returns
result of substituting tree by alist.
*/
static cl_object
sublis(struct cl_test *t, cl_object alist, cl_object tree)
{
cl_object node;
t[1].item_compared = KEY(t, tree);
node = do_assoc(t+1, alist);
if (!Null(node)) {
return ECL_CONS_CDR(node);
}
if (CONSP(tree)) {
tree = CONS(sublis(t, alist, ECL_CONS_CAR(tree)),
sublis(t, alist, ECL_CONS_CDR(tree)));
}
return tree;
}
@(defun nsublis (alist tree &key test test_not key)
/* t[0] is the test for the objects in the tree, configured
with test, test_not and key. t[1] is the test for searching
in the association list.
*/
struct cl_test t[2];
@
setup_test(t, ECL_NIL, ECL_NIL, ECL_NIL, key);
setup_test(t+1, ECL_NIL, test, test_not, ECL_NIL);
tree = nsublis(t, alist, tree);
close_test(t+1);
close_test(t);
@(return tree);
@)
/*
Nsublis(alist, treep) stores
the result of substiting *treep by alist
to *treep.
*/
static cl_object
nsublis(struct cl_test *t, cl_object alist, cl_object tree)
{
cl_object node;
t[1].item_compared = KEY(t, tree);
node = do_assoc(t+1, alist);
if (!Null(node)) {
return ECL_CONS_CDR(node);
}
if (CONSP(tree)) {
ECL_RPLACA(tree, nsublis(t, alist, ECL_CONS_CAR(tree)));
ECL_RPLACD(tree, nsublis(t, alist, ECL_CONS_CDR(tree)));
}
return tree;
}
@(defun member (item list &key test test_not key)
struct cl_test t;
@
setup_test(&t, item, test, test_not, key);
loop_for_in(list) {
if (TEST(&t, ECL_CONS_CAR(list)))
break;
} end_loop_for_in;
close_test(&t);
@(return list);
@)
bool
ecl_member_eq(cl_object x, cl_object l)
{
loop_for_in(l) {
if (x == ECL_CONS_CAR(l))
return(TRUE);
} end_loop_for_in;
return(FALSE);
}
cl_object
si_memq(cl_object x, cl_object l)
{
loop_for_in(l) {
if (x == ECL_CONS_CAR(l)) {
@(return l);
}
} end_loop_for_in;
@(return ECL_NIL);
}
/* Added for use by the compiler, instead of open coding them. Beppe */
cl_object
ecl_memql(cl_object x, cl_object l)
{
loop_for_in(l) {
if (ecl_eql(x, ECL_CONS_CAR(l)))
return(l);
} end_loop_for_in;
return(ECL_NIL);
}
cl_object
ecl_member(cl_object x, cl_object l)
{
loop_for_in(l) {
if (ecl_equal(x, ECL_CONS_CAR(l)))
return(l);
} end_loop_for_in;
return(ECL_NIL);
}
/* End of addition. Beppe */
cl_object
si_member1(cl_object item, cl_object list, cl_object test, cl_object test_not, cl_object key)
{
struct cl_test t;
if (key != ECL_NIL)
item = funcall(2, key, item);
setup_test(&t, item, test, test_not, key);
loop_for_in(list) {
if (TEST(&t, ECL_CONS_CAR(list)))
break;
} end_loop_for_in;
close_test(&t);
@(return list);
}
cl_object
cl_tailp(cl_object y, cl_object x)
{
loop_for_on(x) {
if (ecl_eql(x, y)) {
@(return ECL_T);
}
} end_loop_for_on(x);
return cl_eql(x, y);
}
@(defun adjoin (item list &key test test_not key)
cl_object output;
@
if (narg < 2)
FEwrong_num_arguments(@[adjoin]);
output = @si::member1(item, list, test, test_not, key);
if (Null(output))
output = CONS(item, list);
else
output = list;
@(return output);
@)
cl_object
cl_cons(cl_object x, cl_object y)
{
@(return CONS(x, y));
}
cl_object
cl_acons(cl_object x, cl_object y, cl_object z)
{
@(return CONS(CONS(x, y), z));
}
@(defun pairlis (keys data &optional a_list)
cl_object k, d;
@
k = keys;
d = data;
loop_for_in(k) {
if (ecl_endp(d))
goto error;
a_list = CONS(CONS(ECL_CONS_CAR(k), ECL_CONS_CAR(d)), a_list);
d = CDR(d);
} end_loop_for_in;
if (!ecl_endp(d))
error:
FEerror("The keys ~S and the data ~S are not of the same length",
2, keys, data);
@(return a_list);
@)
@(defun assoc (item a_list &key test test_not key)
struct cl_test t;
@
setup_test(&t, item, test, test_not, key);
a_list = do_assoc(&t, a_list);
close_test(&t);
@(return a_list);
@)
static cl_object
do_assoc(struct cl_test *t, cl_object a_list)
{
loop_for_in(a_list) {
cl_object pair = ECL_CONS_CAR(a_list);
if (!Null(pair)) {
if (!LISTP(pair))
FEtype_error_list(pair);
if (TEST(t, ECL_CONS_CAR(pair)))
return pair;
}
} end_loop_for_in;
return ECL_NIL;
}
@(defun rassoc (item a_list &key test test_not key)
struct cl_test t;
@
setup_test(&t, item, test, test_not, key);
loop_for_in(a_list) {
cl_object pair = ECL_CONS_CAR(a_list);
if (!Null(pair)) {
if (!LISTP(pair))
FEtype_error_list(pair);
if (TEST(&t, ECL_CONS_CDR(pair))) {
a_list = pair;
break;
}
}
} end_loop_for_in;
close_test(&t);
@(return a_list);
@)
cl_object
ecl_remove_eq(cl_object x, cl_object l)
{
cl_object head = ECL_NIL, tail = ECL_NIL;
loop_for_on_unsafe(l) {
if (ECL_CONS_CAR(l) != x) {
cl_object cons = ecl_list1(ECL_CONS_CAR(l));
if (Null(tail)) {
head = tail = cons;
} else {
ECL_RPLACD(tail, cons);
tail = cons;
}
}
} end_loop_for_on_unsafe(l);
return head;
}
cl_object
ecl_delete_eq(cl_object x, cl_object l)
{
cl_object head = l;
cl_object *p = &head;
while (!ECL_ATOM(l)) {
if (ECL_CONS_CAR(l) == x) {
*p = l = ECL_CONS_CDR(l);
} else {
p = &ECL_CONS_CDR(l);
l = *p;
}
}
return head;
}
/* Added for use by the compiler, instead of open coding them. Beppe */
cl_object
ecl_assq(cl_object x, cl_object l)
{
loop_for_in(l) {
cl_object pair = ECL_CONS_CAR(l);
if (pair==ECL_NIL) continue;
if (ecl_unlikely(ECL_ATOM(pair))) {
FEtype_error_cons(pair);
}
if (x == CAR(pair))
return pair;
} end_loop_for_in;
return(ECL_NIL);
}
cl_object
ecl_assql(cl_object x, cl_object l)
{
loop_for_in(l) {
cl_object pair = ECL_CONS_CAR(l);
if (pair==ECL_NIL) continue;
if (ecl_unlikely(ECL_ATOM(pair))) {
FEtype_error_cons(pair);
}
if (ecl_eql(x, CAR(pair)))
return pair;
} end_loop_for_in;
return(ECL_NIL);
}
cl_object
ecl_assoc(cl_object x, cl_object l)
{
loop_for_in(l) {
cl_object pair = ECL_CONS_CAR(l);
if (pair==ECL_NIL) continue;
if (ecl_unlikely(ECL_ATOM(pair))) {
FEtype_error_cons(pair);
}
if (ecl_equal(x, CAR(pair)))
return pair;
} end_loop_for_in;
return(ECL_NIL);
}
cl_object
ecl_assqlp(cl_object x, cl_object l)
{
loop_for_in(l) {
cl_object pair = ECL_CONS_CAR(l);
if (pair==ECL_NIL) continue;
if (ecl_unlikely(ECL_ATOM(pair))) {
FEtype_error_cons(pair);
}
if (ecl_equalp(x, CAR(pair)))
return pair;
} end_loop_for_in;
return(ECL_NIL);
}
/* End of addition. Beppe */