klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit 2d709d86555c0874c0aea1422b29a0645895f583
parent 408eb9dbbe43962ac70d2c31f5c84be2fd717bb0
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Mon, 28 Nov 2011 21:23:30 -0300

Added $when and $unless to the ground environment. Added test for these.

Diffstat:
MTODO | 6+++---
Msrc/kgcontrol.c | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/kghelpers.h | 6------
Msrc/kgports.c | 7+++++--
Msrc/kobject.h | 5+++++
Msrc/tests/control.k | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 208 insertions(+), 13 deletions(-)

diff --git a/TODO b/TODO @@ -20,14 +20,13 @@ typedef like lua) * fix: ** fix/test the tty detection in the interpreter +** fix char-ready? and u8-ready? (r7rs) * documentation ** fix some inconsistencies between the man page and the interpreter behaviour. ** update the manual with the current features ** add a section to the manual with the interpreter usage * operatives: -** $when (r7rs) -** $unless (r7rs) ** $case (r7rs) ** $case-lambda (r7rs) ** $case-vau (r7rs) @@ -35,9 +34,10 @@ ** $do (r7rs) ** $define-record-type (r7rs) * applicatives: -** read-line (r7rs) ** number->string (r7rs) ** string->number (r7rs) +** read-line (r7rs) +** write-simple (r7rs) * reader ** symbol escapes (r7rs) ** string escapes (r7rs) diff --git a/src/kgcontrol.c b/src/kgcontrol.c @@ -23,6 +23,7 @@ void do_select_clause(klisp_State *K); void do_cond(klisp_State *K); void do_for_each(klisp_State *K); +void do_Swhen_Sunless(klisp_State *K); /* 4.5.1 inert? */ /* uses typep */ @@ -36,8 +37,8 @@ void Sif(klisp_State *K) TValue ptree = K->next_value; TValue denv = K->next_env; klisp_assert(ttisenvironment(K->next_env)); - (void) denv; - (void) xparams; + UNUSED(denv); + UNUSED(xparams); bind_3p(K, ptree, test, cons_c, alt_c); @@ -441,6 +442,105 @@ void array_for_each(klisp_State *K) kapply_cc(K, KINERT); } +/* Helper for $when and $unless */ +void do_Swhen_Sunless(klisp_State *K) +{ + TValue *xparams = K->next_xparams; + TValue obj = K->next_value; + klisp_assert(ttisnil(K->next_env)); + + /* + ** xparams[0]: bool condition + ** xparams[1]: body + ** xparams[2]: denv + ** xparams[3]: si for whole form + */ + bool cond = bvalue(xparams[0]); + TValue ls = xparams[1]; + TValue denv = xparams[2]; +#if KTRACK_SI + TValue si = xparams[3]; +#endif + + if (!ttisboolean(obj)) { + klispE_throw_simple(K, "test is not a boolean"); + return; + } + + if (bvalue(obj) == cond && !ttisnil(ls)) { + /* only contruct the #inert returning continuation if the + current continuation is not of the same type */ + if (!kis_inert_ret_cont(kget_cc(K))) { + TValue new_cont = + kmake_continuation(K, kget_cc(K), do_return_value, 1, KINERT); + /* mark it, so that it can be detected as inert throwing cont */ + kset_inert_ret_cont(new_cont); + kset_cc(K, new_cont); +#if KTRACK_SI + /* put the source info of the whole form */ + kset_source_info(K, new_cont, si); +#endif + } + /* this is needed because seq continuation doesn't check for + nil sequence */ + /* TODO this could be at least in an inlineable function to + allow used from $lambda, $vau, $let family, load, etc */ + TValue tail = kcdr(ls); + if (ttispair(tail)) { + krooted_tvs_push(K, ls); + TValue new_cont = kmake_continuation(K, kget_cc(K), do_seq, 2, + tail, denv); + kset_cc(K, new_cont); +#if KTRACK_SI + /* put the source info of the list including the element + that we are about to evaluate */ + kset_source_info(K, new_cont, ktry_get_si(K, ls)); +#endif + krooted_tvs_pop(K); + } + ktail_eval(K, kcar(ls), denv); + } else { + /* either the test failed or the body was nil */ + kapply_cc(K, KINERT); + } +} + +/* ASK JOHN: list is copied here (like in $sequence) */ +void Swhen_Sunless(klisp_State *K) +{ + TValue *xparams = K->next_xparams; + TValue ptree = K->next_value; + TValue denv = K->next_env; + klisp_assert(ttisenvironment(K->next_env)); + + bind_al1p(K, ptree, test, body); + + /* + ** xparams[0]: bool condition + */ + TValue tv_cond = xparams[0]; + + /* the list of instructions is copied to avoid mutation */ + /* MAYBE: copy the evaluation structure, ASK John */ + TValue ls = check_copy_list(K, body, false, NULL, NULL); + krooted_tvs_push(K, ls); + /* prepare the continuation that will check the test result + and do the evaluation */ + TValue si = K->next_si; /* this is the source info of the whole + $when/$unless form */ + TValue new_cont = kmake_continuation(K, kget_cc(K), do_Swhen_Sunless, + 4, tv_cond, ls, denv, si); + krooted_tvs_pop(K); + /* + ** Mark as a bool checking cont, not necessary but avoids a continuation + ** in the last evaluation in the common use of + ** ($when/$unless ($or?/$and? ...) ...) + */ + kset_bool_check_cont(new_cont); + kset_cc(K, new_cont); + ktail_eval(K, test, denv); +} + /* init ground */ void kinit_control_ground_env(klisp_State *K) { @@ -465,6 +565,11 @@ void kinit_control_ground_env(klisp_State *K) p2tv(vector_to_list_h)); add_applicative(K, ground_env, "bytevector-for-each", array_for_each, 1, p2tv(bytevector_to_list_h)); + /* ?.? */ + add_operative(K, ground_env, "$when", Swhen_Sunless, 1, + b2tv(true)); + add_operative(K, ground_env, "$unless", Swhen_Sunless, 1, + b2tv(false)); } /* init continuation names */ @@ -473,6 +578,8 @@ void kinit_control_cont_names(klisp_State *K) Table *t = tv2table(K->cont_name_table); add_cont_name(K, t, do_select_clause, "select-clause"); + add_cont_name(K, t, do_Swhen_Sunless, "conditional-eval-sequence"); + add_cont_name(K, t, do_cond, "eval-cond-list"); add_cont_name(K, t, do_for_each, "for-each"); } diff --git a/src/kghelpers.h b/src/kghelpers.h @@ -426,12 +426,6 @@ TValue make_bind_continuation(klisp_State *K, TValue key, TValue check_copy_guards(klisp_State *K, char *name, TValue obj); void guard_dynamic_extent(klisp_State *K); -/* GC: assumes parent & obj are rooted */ -inline TValue make_return_value_cont(klisp_State *K, TValue parent, TValue obj) -{ - return kmake_continuation(K, parent, do_return_value, 1, obj); -} - /* Some helpers for working with fixints (signed 32 bits) */ inline int32_t kabs32(int32_t a) { return a < 0? -a : a; } inline int64_t kabs64(int64_t a) { return a < 0? -a : a; } diff --git a/src/kgports.c b/src/kgports.c @@ -697,7 +697,9 @@ void load(klisp_State *K) TValue port = kmake_fport(K, filename, false, false); krooted_tvs_push(K, port); - TValue inert_cont = make_return_value_cont(K, kget_cc(K), KINERT); + TValue inert_cont = kmake_continuation(K, kget_cc(K), do_return_value, 1, + KINERT); + krooted_tvs_push(K, inert_cont); TValue guarded_cont = make_guarded_read_cont(K, kget_cc(K), port); @@ -760,7 +762,8 @@ void get_module(klisp_State *K) kadd_binding(K, env, K->module_params_sym, maybe_env); } - TValue ret_env_cont = make_return_value_cont(K, kget_cc(K), env); + TValue ret_env_cont = kmake_continuation(K, kget_cc(K), do_return_value, + 1, env); krooted_tvs_pop(K); /* env alread in cont */ krooted_tvs_push(K, ret_env_cont); diff --git a/src/kobject.h b/src/kobject.h @@ -822,17 +822,22 @@ int32_t kmark_count; #define K_FLAG_INNER 0x02 #define K_FLAG_DYNAMIC 0x04 #define K_FLAG_BOOL_CHECK 0x08 +/* this is the same as immutable, but there is no problem + with continuations */ +#define K_FLAG_INERT_RET 0x10 /* evaluate c_ more than once */ #define kset_inner_cont(c_) (tv_get_kflags(c_) |= K_FLAG_INNER) #define kset_outer_cont(c_) (tv_get_kflags(c_) |= K_FLAG_OUTER) #define kset_dyn_cont(c_) (tv_get_kflags(c_) |= K_FLAG_DYNAMIC) #define kset_bool_check_cont(c_) (tv_get_kflags(c_) |= K_FLAG_BOOL_CHECK) +#define kset_inert_ret_cont(c_) (tv_get_kflags(c_) |= K_FLAG_INERT_RET) #define kis_inner_cont(c_) ((tv_get_kflags(c_) & K_FLAG_INNER) != 0) #define kis_outer_cont(c_) ((tv_get_kflags(c_) & K_FLAG_OUTER) != 0) #define kis_dyn_cont(c_) ((tv_get_kflags(c_) & K_FLAG_DYNAMIC) != 0) #define kis_bool_check_cont(c_) ((tv_get_kflags(c_) & K_FLAG_BOOL_CHECK) != 0) +#define kis_inert_ret_cont(c_) ((tv_get_kflags(c_) & K_FLAG_INERT_RET) != 0) #define K_FLAG_OUTPUT_PORT 0x01 #define K_FLAG_INPUT_PORT 0x02 diff --git a/src/tests/control.k b/src/tests/control.k @@ -268,6 +268,76 @@ (car p)) #f)) +;; $when +($check-predicate (operative? $when)) +($check-predicate (inert? ($when #t))) +($check-predicate (inert? ($when #f))) +($check-predicate (inert? ($when #t 1))) +($check-predicate (inert? ($when #f 1))) +($check-predicate (inert? ($when #t 1 2))) +($check-predicate (inert? ($when #f 1 2))) + +($let ((p (cons () ()))) + ($check equal? ($sequence ($when #f (set-car! p 1)) + (car p)) + ())) + +($let ((p (cons () ()))) + ($check eq? ($sequence ($when ($sequence + (set-car! p (get-current-environment)) + #f)) + (car p)) + (get-current-environment))) + +($let ((p (cons () ()))) + ($check eq? ($sequence ($when #t (set-car! p (get-current-environment))) + (car p)) + (get-current-environment))) + +;; check tail recursiveness +($let ((p (cons 1 2))) + ($check-predicate ($sequence ($when #t ($let/cc cont1 + (set-car! p cont1) + ($when #t + ($let/cc cont2 + (set-cdr! p cont2))))) + (eq? (car p) (cdr p))))) + +;; $unless +($check-predicate (operative? $unless)) +($check-predicate (inert? ($unless #t))) +($check-predicate (inert? ($unless #f))) +($check-predicate (inert? ($unless #t 1))) +($check-predicate (inert? ($unless #f 1))) +($check-predicate (inert? ($unless #t 1 2))) +($check-predicate (inert? ($unless #f 1 2))) + +($let ((p (cons () ()))) + ($check equal? ($sequence ($unless #t (set-car! p 1)) + (car p)) + ())) + +($let ((p (cons () ()))) + ($check eq? ($sequence ($unless ($sequence + (set-car! p (get-current-environment)) + #t)) + (car p)) + (get-current-environment))) + +($let ((p (cons () ()))) + ($check eq? ($sequence ($unless #f (set-car! p (get-current-environment))) + (car p)) + (get-current-environment))) + +;; check tail recursiveness +($let ((p (cons 1 2))) + ($check-predicate ($sequence ($unless #f ($let/cc cont1 + (set-car! p cont1) + ($unless #f + ($let/cc cont2 + (set-cdr! p cont2))))) + (eq? (car p) (cdr p))))) + ;;; ;;; Error Checking and Robustness ;;; @@ -361,3 +431,19 @@ ($check-error (bytevector-for-each <? (bytevector 1 2) #inert)) ($check-error (bytevector-for-each cons (bytevector 1 2 3))) + + +;; $when +($check-error ($when)) +($check-error ($when #t . 3)) +($check-error ($when #f . 3)) +($check-error ($when #inert 1)) + +;; $unless +($check-error ($unless)) +($check-error ($unless #t . 3)) +($check-error ($unless #f . 3)) +($check-error ($unless #inert 1)) + + +