klisp

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

commit 8fa3393714a288ee551677ffc56f3283012a2ec3
parent 45618126610235b0c40d2cb2000345b9f0c59965
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Tue, 15 Mar 2011 16:52:23 -0300

Added interception code for continuations. Basic functionality working but it isn't tested yet.

Diffstat:
Msrc/Makefile | 2+-
Msrc/kgcontinuations.c | 9+++++----
Msrc/kstate.c | 173++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 177 insertions(+), 7 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -51,7 +51,7 @@ kwrite.o: kwrite.c kwrite.h kobject.h kpair.h kstring.h kstate.h kerror.h \ klisp.h kstate.o: kstate.c kstate.h klisp.h kobject.h kmem.h kstring.h klisp.h \ kground.h kenvironment.h kpair.h keval.h koperative.h kground.h \ - krepl.h + krepl.h kcontinuation.h kmem.o: kmem.c kmem.h klisp.h kerror.h klisp.h kerror.o: kerror.c kerror.h klisp.h kstate.h klisp.h kmem.h kstring.h kauxlib.o: kauxlib.c kauxlib.h klisp.h kstate.h klisp.h diff --git a/src/kgcontinuations.c b/src/kgcontinuations.c @@ -116,7 +116,9 @@ inline TValue check_copy_single_entry(klisp_State *K, char *name, } /* GC: save intermediate pair */ - return kcons(K, cont, kcons(K, app, KNIL)); + /* save the operative directly, don't waste space/time + with a list, use just a pair */ + return kcons(K, cont, kunwrap(app)); } /* the guards are probably generated on the spot so we don't check @@ -156,7 +158,6 @@ TValue check_copy_guards(klisp_State *K, char *name, TValue obj) void guard_continuation(klisp_State *K, TValue *xparams, TValue ptree, TValue denv) { - UNUSED(denv); UNUSED(xparams); bind_3tp(K, "guard-continuation", ptree, "any", anytype, entry_guards, @@ -169,11 +170,11 @@ void guard_continuation(klisp_State *K, TValue *xparams, TValue ptree, exit_guards); TValue outer_cont = kmake_continuation(K, cont, KNIL, KNIL, pass_value, - 1, entry_guards); + 2, entry_guards, denv); /* mark it as an outer continuation */ kset_outer_cont(outer_cont); TValue inner_cont = kmake_continuation(K, outer_cont, KNIL, KNIL, - pass_value, 1, exit_guards); + pass_value, 2, exit_guards, denv); /* mark it as an outer continuation */ kset_inner_cont(inner_cont); kapply_cc(K, inner_cont); diff --git a/src/kstate.c b/src/kstate.c @@ -19,6 +19,7 @@ #include "kmem.h" #include "keval.h" #include "koperative.h" +#include "kcontinuation.h" #include "kenvironment.h" #include "kground.h" #include "krepl.h" @@ -120,13 +121,181 @@ klisp_State *klisp_newstate (klisp_Alloc f, void *ud) { return K; } +/* +** +** This is for handling interceptions +** TODO: move to a different file +** +*/ + +/* +** This is used to determine if cont is in the dynamic extent of +** some other continuation. That's the case iff that continuation +** was marked by the call to mark_iancestors(cont) +*/ + +/* TODO: maybe add some inlines here, profile first and check size difference */ +void mark_iancestors(TValue cont) +{ + while(!ttisnil(cont)) { + kmark(cont); + cont = tv2cont(cont)->parent; + } +} + +void unmark_iancestors(TValue cont) +{ + while(!ttisnil(cont)) { + kunmark(cont); + cont = tv2cont(cont)->parent; + } +} + +/* +** Returns the first interceptor whose dynamic extent includes cont +** or nil if there isn't any. The cont is implicitly passed because +** all of its improper ancestors are marked. +*/ +TValue select_interceptor(TValue guard_ls) +{ + /* the guard list can't be cyclic, that case is + replaced by a simple list while copyng guards */ + while(!ttisnil(guard_ls)) { + /* entry is (selector . interceptor-op) */ + TValue entry = kcar(guard_ls); + TValue selector = kcar(entry); + if (kis_marked(selector)) + return kcdr(entry); /* only interceptor is important */ + guard_ls = kcdr(guard_ls); + } + return KNIL; +} + +/* +** Returns a list of entries like the following: +** (interceptor-op outer_cont . denv) +*/ +/* TODO: should inline this one, is only called from one place */ +TValue create_interception_list(klisp_State *K, TValue src_cont, + TValue dst_cont) +{ + /* GC: root intermediate pairs */ + mark_iancestors(dst_cont); + TValue dummy = kcons(K, KINERT, KNIL); + TValue tail = dummy; + TValue cont = src_cont; + + /* exit guards are from the inside to the outside, and + selected by destination */ + + /* the loop is until we find the common ancestor, that has to be marked */ + while(!kis_marked(cont)) { + /* only inner conts have exit guards */ + if (kis_inner_cont(cont)) { + TValue entries = tv2cont(cont)->extra[0]; /* TODO make a macro */ + + TValue interceptor = select_interceptor(entries); + if (!ttisnil(interceptor)) { + /* TODO make macros */ + TValue denv = tv2cont(cont)->extra[1]; + TValue outer = tv2cont(cont)->parent; + TValue new_entry = kcons(K, interceptor, + kcons(K, outer, denv)); + TValue new_pair = kcons(K, new_entry, KNIL); + kset_cdr(tail, new_pair); + tail = new_pair; + } + } + cont = tv2cont(cont)->parent; + } + unmark_iancestors(dst_cont); + + /* entry guards are from the outside to the inside, and + selected by source, we create the list from the outside + by cons and then append it to the exit list to avoid + reversing */ + mark_iancestors(src_cont); + + cont = dst_cont; + TValue entry_int = KNIL; + + while(!kis_marked(cont)) { + /* only outer conts have entry guards */ + if (kis_outer_cont(cont)) { + TValue entries = tv2cont(cont)->extra[0]; /* TODO make a macro */ + TValue interceptor = select_interceptor(entries); + if (!ttisnil(interceptor)) { + /* TODO make macros */ + TValue denv = tv2cont(cont)->extra[1]; + TValue outer = cont; + TValue new_entry = kcons(K, interceptor, + kcons(K, outer, denv)); + entry_int = kcons(K, new_entry, entry_int); + } + } + cont = tv2cont(cont)->parent; + } + + unmark_iancestors(src_cont); + + /* all interceptions collected, append the two lists and return */ + kset_cdr(tail, entry_int); + return kcdr(dummy); +} + +void do_interception(klisp_State *K, TValue *xparams, TValue obj) +{ + /* + ** xparams[0]: + ** xparams[1]: dst cont + */ + TValue ls = xparams[0]; + if (ttisnil(ls)) { + /* all interceptors returned normally */ + TValue dst_cont = xparams[1]; + /* TODO: this is the same code as the standard case of + call_cont, merge */ + Continuation *cont = tv2cont(dst_cont); + K->next_func = cont->fn; + K->next_value = obj; + /* NOTE: this is needed to differentiate a return from a tail call */ + K->next_env = KNIL; + K->next_xparams = cont->extra; + K->curr_cont = cont->parent; + + longjmp(K->error_jb, 1); + } else { + TValue first = kcar(ls); + TValue op = kcar(first); + TValue outer = kcadr(first); + TValue denv = kcddr(first); + /* call the operative with the passed obj as ptree in + the dynamic environment of the corresponding call + to guard-continuation in the dynamic extent of the + associated outer continuation */ + kset_cc(K, outer); + ktail_call(K, op, obj, denv); + } +} + /* GC: should probably save the cont to retain the objects in xparams in case of gc (Also useful for source code info) probably a new field in K called active_cont */ void kcall_cont(klisp_State *K, TValue dst_cont, TValue obj) { - /* TODO: interceptions */ - Continuation *cont = tv2cont(dst_cont); + TValue src_cont = kget_cc(K); + TValue int_ls = create_interception_list(K, src_cont, dst_cont); + TValue new_cont; + if (ttisnil(int_ls)) { + new_cont = dst_cont; /* no interceptions */ + } else { + /* we have to contruct a continuation to do the interceptions + in order and finally call dst_cont if no divert occurs */ + new_cont = kmake_continuation(K, kget_cc(K), KNIL, KNIL, + do_interception, 2, int_ls, dst_cont); + + } + Continuation *cont = tv2cont(new_cont); K->next_func = cont->fn; K->next_value = obj; /* NOTE: this is needed to differentiate a return from a tail call */