commit f5084f890c92f547b2c2e0970d68b11c222d4451
parent b92ab2d6f19f19944961ab508c32bd8b22cc6452
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Tue, 29 Mar 2011 18:50:07 -0300
Added $cond to the ground environment.
Diffstat:
3 files changed, 147 insertions(+), 3 deletions(-)
diff --git a/src/kgcontrol.c b/src/kgcontrol.c
@@ -106,5 +106,143 @@ void do_seq(klisp_State *K, TValue *xparams, TValue obj)
     ktail_eval(K, first, denv);
 }
 
+/* Helpers for cond */
+
+/*
+** Check the clauses structure.
+** Each should be a list of at least 1 element.
+** Return both a copied list of tests (only list structure is copied)
+** and a copied list of bodies (only list structure is copied, see comment
+** on $sequence, cf. $let, $vau and $lambda)
+** Throw errors if any of the above mentioned checks fail.
+*/
+TValue split_check_cond_clauses(klisp_State *K, TValue clauses, 
+				TValue *bodies)
+{
+    TValue dummy_cars = kcons(K, KNIL, KNIL);
+    TValue last_car_pair = dummy_cars;
+    TValue dummy_cdrs = kcons(K, KNIL, KNIL);
+    TValue last_cdr_pair = dummy_cdrs;
+
+    TValue tail = clauses;
+    int32_t count = 0;
+
+    while(ttispair(tail) && !kis_marked(tail)) {
+	count++;
+	TValue first = kcar(tail);
+	if (!ttispair(first)) {
+	    unmark_list(K, clauses);
+	    klispE_throw(K, "$cond: bad structure in clauses");
+	    return KNIL;
+	}
+	
+	TValue new_car = kcons(K, kcar(first), KNIL);
+	kset_cdr(last_car_pair, new_car);
+	last_car_pair = new_car;
+	/* bodies have to be checked later */
+	TValue new_cdr = kcons(K, kcdr(first), KNIL);
+	kset_cdr(last_cdr_pair, new_cdr);
+	last_cdr_pair = new_cdr;
+
+	kset_mark(tail, kcons(K, new_car, new_cdr));
+	tail = kcdr(tail);
+    }
+
+    /* complete the cycles before unmarking */
+    if (ttispair(tail)) {
+	TValue mark = kget_mark(tail);
+	kset_cdr(last_car_pair, kcar(mark));
+	kset_cdr(last_cdr_pair, kcdr(mark));
+    }
+
+    unmark_list(K, clauses);
+
+    if (!ttispair(tail) && !ttisnil(tail)) {
+	klispE_throw(K, "$cond: expected list (clauses)");
+	return KNIL;
+    } else {
+
+	tail = kcdr(dummy_cdrs);
+	/* 
+	   check all the bodies (should be lists), and
+	   make a copy of the list structure.
+	   couldn't be done before because this uses
+	   marks, count is used because it may be a cyclic list
+	*/
+	while(count--) {
+	    TValue first = kcar(tail);
+	    TValue copy = check_copy_list(K, "$cond", first);
+	    kset_car(tail, copy);
+	    tail = kcdr(tail);
+	}
+	*bodies = kcdr(dummy_cdrs);
+	return kcdr(dummy_cars);
+    }
+}
+
+/* Helper for the $cond continuation */
+void do_cond(klisp_State *K, TValue *xparams, TValue obj)
+{
+    /* 
+    ** xparams[0]: the body corresponding to obj
+    ** xparams[1]: remaining tests
+    ** xparams[2]: remaining bodies
+    ** xparams[3]: dynamic environment
+    */
+    TValue this_body = xparams[0];
+    TValue tests = xparams[1];
+    TValue bodies = xparams[2];
+    TValue denv = xparams[3];
+
+    if (!ttisboolean(obj)) {
+	klispE_throw(K, "$cond: test evaluated to a non boolean value");
+	return;
+    } else if (bvalue(obj)) {
+	if (ttisnil(this_body)) {
+	    kapply_cc(K, KINERT);
+	} else {
+	    TValue tail = kcdr(this_body);
+	    if (ttispair(tail)) {
+		TValue new_cont = kmake_continuation(K, kget_cc(K), KNIL, KNIL,
+						     do_seq, 2, tail, denv);
+		kset_cc(K, new_cont);
+	    }
+	    ktail_eval(K, kcar(this_body), denv);
+	}
+    } else {
+	/* check next clause if there is any*/
+	if (ttisnil(tests)) {
+	    kapply_cc(K, KINERT);
+	} else {
+	    TValue new_cont = 
+		kmake_continuation(K, kget_cc(K), KNIL, KNIL, do_cond, 4,
+				   kcar(bodies), kcdr(tests), kcdr(bodies), 
+				   denv);
+	    kset_cc(K, new_cont);
+	    ktail_eval(K, kcar(tests), denv);
+	}
+    }
+}
+
 /* 5.6.1 $cond */
-/* TODO */
+void Scond(klisp_State *K, TValue *xparams, TValue ptree, TValue denv)
+{
+    (void) xparams;
+
+    TValue bodies;
+    TValue tests = split_check_cond_clauses(K, ptree, &bodies);
+
+    TValue obj;
+    if (ttisnil(tests)) {
+	obj = KINERT;
+    } else {
+	/* pass a dummy body and a #f to the $cond continuation to 
+	   avoid code repetition here */
+	TValue new_cont = 
+	    kmake_continuation(K, kget_cc(K), KNIL, KNIL, do_cond, 4, 
+			       KNIL, tests, bodies, denv);
+	kset_cc(K, new_cont);
+	obj = KFALSE; 
+    }
+    kapply_cc(K, obj);
+}
diff --git a/src/kgcontrol.h b/src/kgcontrol.h
@@ -31,7 +31,13 @@ void Sif(klisp_State *K, TValue *xparams, TValue ptree, TValue denv);
 /* 5.1.1 $sequence */
 void Ssequence(klisp_State *K, TValue *xparams, TValue ptree, TValue denv);
 
+/* Helpers for $cond */
+TValue split_check_cond_clauses(klisp_State *K, TValue clauses, 
+				TValue *bodies);
+
+void do_cond(klisp_State *K, TValue *xparams, TValue obj);
+
 /* 5.6.1 $cond */
-/* TODO */
+void Scond(klisp_State *K, TValue *xparams, TValue ptree, TValue denv);
 
 #endif
diff --git a/src/kground.c b/src/kground.c
@@ -306,7 +306,7 @@ void kinit_ground_env(klisp_State *K)
     */
 
     /* 5.6.1 $cond */
-    /* TODO */
+    add_operative(K, ground_env, "$cond", Scond, 0);
 
     /*
     ** 5.7 Pairs and lists