Skip to content

Commit 0195778

Browse files
committed
Initial call/delimited-cc
1 parent 7d053f3 commit 0195778

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

src/lib_sagittarius.stub

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,8 @@
18531853

18541854
(define-c-proc %abort/cc (tag args) :no-export Sg_VMAbortCC)
18551855
(define-c-proc %call/comp (proc::<procedure> tag) :no-export Sg_VMCallComp)
1856+
(define-c-proc %call/delimited-cc (proc::<procedure> tag) :no-export
1857+
Sg_VMCallDelimitedCC)
18561858
(define-c-proc continuation? (o) ::<boolean> :no-side-effect Sg_ContinuationP)
18571859

18581860
(define (make-continuation-prompt-tag :optional (name (gensym))) (list name))
@@ -1874,8 +1876,13 @@
18741876
proc :optional ((tag continuation-prompt-tag?)
18751877
(default-continuation-prompt-tag)))
18761878
(%call/comp proc tag))
1879+
(define (call-with-delimited-current-continuation
1880+
proc :optional ((tag continuation-prompt-tag?)
1881+
(default-continuation-prompt-tag)))
1882+
(%call/delimited-cc proc tag))
18771883

18781884
(define call/prompt call-with-continuation-prompt)
18791885
(define abort/cc abort-current-continuation)
18801886
(define call/comp call-with-composable-continuation)
1887+
(define call/delimited-cc call-with-delimited-current-continuation)
18811888
)

src/sagittarius/private/vm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ SG_EXTERN SgObject Sg_VMCallCC(SgObject proc);
407407
SG_EXTERN SgObject Sg_VMCallCP(SgObject proc, SgObject tag,
408408
SgObject handler, SgObject args);
409409
SG_EXTERN SgObject Sg_VMCallComp(SgObject proc, SgObject tag);
410+
SG_EXTERN SgObject Sg_VMCallDelimitedCC(SgObject proc, SgObject tag);
410411
SG_EXTERN SgObject Sg_VMAbortCC(SgObject tag, SgObject args);
411412
SG_EXTERN int Sg_ContinuationP(SgObject o);
412413
SG_EXTERN SgVM* Sg_VM(); /* get vm */

src/vm.c

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,12 @@ static SgObject merge_winders(SgObject, SgObject);
15751575
static SgObject take_prompt_winders(SgPrompt *, SgObject);
15761576
static SgObject capture_prompt_winders(SgPrompt *, SgObject);
15771577

1578+
/* Forward declarations for delimited call/cc support */
1579+
static SgPrompt *make_prompt(SgObject tag, SgObject handler, SgVM *vm);
1580+
static void install_prompt(SgVM *vm, SgPrompt *prompt);
1581+
static void remove_prompt(SgVM *vm, SgPrompt *prompt);
1582+
static SgObject abort_body(SgPromptNode *node, SgObject winders, SgObject args);
1583+
15781584
static SgObject throw_continuation_body(SgObject handlers,
15791585
SgContinuation *c,
15801586
SgObject args,
@@ -1915,6 +1921,188 @@ SgObject Sg_VMCallComp(SgObject proc, SgObject tag)
19151921
return SG_UNDEF; /* dummy */
19161922
}
19171923

1924+
/*
1925+
Delimited call/cc - Racket-like call/cc with prompt support
1926+
1927+
This creates a non-composable continuation that:
1928+
1. When invoked, aborts to the prompt (running post-thunks)
1929+
2. After abort, reinstalls a new prompt and applies the composable continuation
1930+
1931+
The continuation procedure has data: (cont . tag)
1932+
where cont is the composable continuation and tag identifies the boundary.
1933+
*/
1934+
static SgObject throw_delimited_continuation_cc(SgObject, void **);
1935+
1936+
static SgObject throw_delimited_continuation_body(SgObject handlers,
1937+
SgContinuation *c,
1938+
SgPrompt *capture_prompt,
1939+
SgObject args,
1940+
SgObject tag)
1941+
{
1942+
SgVM *vm = Sg_VM();
1943+
1944+
/* Process abort handlers (post-thunks for unwinding) */
1945+
if (SG_PAIRP(handlers)) {
1946+
SgObject handler, chain;
1947+
void *data[5];
1948+
handler = SG_CAAR(handlers);
1949+
chain = SG_CDAR(handlers);
1950+
data[0] = (void*)SG_CDR(handlers);
1951+
data[1] = (void*)c;
1952+
data[2] = (void*)capture_prompt;
1953+
data[3] = (void*)args;
1954+
data[4] = (void*)tag;
1955+
Sg_VMPushCC(throw_delimited_continuation_cc, data, 5);
1956+
1957+
/* For abort/post-thunks, directly SET winders to the chain (not merge)
1958+
This is like abort_body which also sets vm->dynamicWinders = chain */
1959+
vm->dynamicWinders = chain;
1960+
return Sg_VMApply0(handler);
1961+
} else {
1962+
/* All abort handlers processed. Now we need to:
1963+
1. Set up the new prompt
1964+
2. Compute wind-in handlers (pre-thunks) to restore the continuation's dynamic extent
1965+
3. Run those handlers, then apply the continuation */
1966+
1967+
SgPrompt *new_prompt;
1968+
SgPromptNode *node = search_prompt_node_by_tag(vm, tag);
1969+
1970+
if (!node) {
1971+
Sg_Error(UC("Stale prompt in delimited continuation: %S"), tag);
1972+
}
1973+
1974+
/* Pop continuation frames up to and including the prompt frame */
1975+
SgContFrame *cont_frame = vm->cont;
1976+
while (!cont_prompt_match_p(cont_frame, node->prompt)) {
1977+
if (PROMPT_FRAME_MARK_P(cont_frame)) {
1978+
remove_prompt(vm, (SgPrompt *)cont_frame->pc);
1979+
}
1980+
cont_frame = cont_frame->prev;
1981+
}
1982+
1983+
/* Remove the original prompt and set continuation to just before the prompt */
1984+
remove_prompt(vm, node->prompt);
1985+
vm->cont = node->frame->prev;
1986+
1987+
/* Create a new prompt for the continuation */
1988+
new_prompt = make_prompt(tag, SG_FALSE, vm);
1989+
1990+
CHECK_STACK(CONT_FRAME_SIZE, vm);
1991+
PUSH_PROMPT_CONT(vm, new_prompt);
1992+
FP(vm) = SP(vm);
1993+
install_prompt(vm, new_prompt);
1994+
1995+
/* Now compute and run wind-in handlers using the composable continuation path.
1996+
We've already aborted to the prompt, so current vm->dynamicWinders is at
1997+
the prompt's level. We need to wind into c's winders. */
1998+
{
1999+
SgObject wind_handlers = throw_cont_compute_handlers(c, capture_prompt, vm);
2000+
/* Use the regular composable continuation body to process these handlers */
2001+
return throw_continuation_body(wind_handlers, c, args, capture_prompt);
2002+
}
2003+
}
2004+
}
2005+
2006+
static SgObject throw_delimited_continuation_cc(SgObject result, void **data)
2007+
{
2008+
SgObject handlers = SG_OBJ(data[0]);
2009+
SgContinuation *c = (SgContinuation*)data[1];
2010+
SgPrompt *capture_prompt = (SgPrompt*)data[2];
2011+
SgObject args = SG_OBJ(data[3]);
2012+
SgObject tag = SG_OBJ(data[4]);
2013+
return throw_delimited_continuation_body(handlers, c, capture_prompt, args, tag);
2014+
}
2015+
2016+
static SgObject throw_delimited_continuation(SgObject *argv, int argc, void *data)
2017+
{
2018+
SgContinuation *c = SG_CONTINUATION(SG_CAAR(data));
2019+
SgPrompt *capture_prompt = (SgPrompt *)SG_CDAR(data);
2020+
SgObject tag = SG_CDR(data);
2021+
SgVM *vm = Sg_VM();
2022+
SgPromptNode *node = search_prompt_node_by_tag(vm, tag);
2023+
SgObject h, args;
2024+
2025+
if (!node) {
2026+
Sg_Error(UC("Continuation application: no corresponding prompt for tag ~S"), tag);
2027+
}
2028+
2029+
/* argv[0] is already a list of arguments (folded by optional arg processing) */
2030+
args = argv[0];
2031+
2032+
/* Check for cross-cstack jumps */
2033+
if (node->prompt->cstack != vm->cstack) {
2034+
SgCStack *cs;
2035+
for (cs = vm->cstack; cs; cs = cs->prev) {
2036+
if (node->prompt->cstack == cs) break;
2037+
}
2038+
if (cs != NULL) {
2039+
/* Need to longjmp */
2040+
vm->escapeReason = SG_VM_ESCAPE_CONT;
2041+
vm->escapeData[0] = c;
2042+
vm->escapeData[1] = Sg_Cons(Sg_Cons(args, (SgObject)capture_prompt), tag);
2043+
longjmp(vm->cstack->jbuf, 1);
2044+
}
2045+
}
2046+
2047+
/* call/delimited-cc (call/cc-like): ALWAYS abort to the prompt and reinstall.
2048+
This is the key difference from composable continuations which just
2049+
continue from where they are.
2050+
2051+
Run post-thunks to unwind from current to prompt. */
2052+
{
2053+
SgContinuation abort_c;
2054+
abort_c.winders = node->prompt->winders;
2055+
abort_c.cstack = node->prompt->cstack; /* Non-null to enable post-thunks */
2056+
abort_c.cont = NULL;
2057+
abort_c.prev = NULL;
2058+
abort_c.ehandler = SG_FALSE;
2059+
h = throw_cont_compute_handlers(&abort_c, node->prompt, vm);
2060+
}
2061+
2062+
return throw_delimited_continuation_body(h, c, capture_prompt, args, tag);
2063+
}
2064+
2065+
static SgObject make_delimited_cont_subr(SgContinuation *cont, SgPrompt *prompt, SgObject tag)
2066+
{
2067+
/* data = ((cont . prompt) . tag) */
2068+
return Sg_MakeSubr(throw_delimited_continuation,
2069+
Sg_Cons(Sg_Cons((SgObject)cont, (SgObject)prompt), tag),
2070+
0, 1,
2071+
sym_continuation);
2072+
}
2073+
2074+
/* call-with-current-continuation with prompt delimiting (Racket-like call/cc) */
2075+
SgObject Sg_VMCallDelimitedCC(SgObject proc, SgObject tag)
2076+
{
2077+
SgContinuation *cont;
2078+
SgObject contproc;
2079+
SgVM *vm = Sg_VM();
2080+
SgPromptNode *node = search_prompt_node_by_tag(vm, tag);
2081+
2082+
if (!node) goto err;
2083+
2084+
save_cont(vm);
2085+
2086+
cont = SG_NEW(SgContinuation);
2087+
/* Use capture_prompt_winders to only include winders installed AFTER the prompt.
2088+
Unlike take_prompt_winders which returns a tail, this returns a NEW list
2089+
containing only the winders not in prompt->winders, stopping at the first
2090+
common winder. */
2091+
cont->winders = capture_prompt_winders(node->prompt, vm->dynamicWinders);
2092+
cont->cont = vm->cont;
2093+
cont->prev = NULL;
2094+
cont->ehandler = SG_FALSE;
2095+
cont->cstack = NULL;
2096+
2097+
/* Pass the capture-time prompt so we know where to stop when splicing */
2098+
contproc = make_delimited_cont_subr(cont, node->prompt, tag);
2099+
2100+
return Sg_VMApply1(proc, contproc);
2101+
err:
2102+
Sg_Error(UC("No continuation tag: %S"), tag);
2103+
return SG_UNDEF;
2104+
}
2105+
19182106
/* call-with-continuation-prompt
19192107
19202108
This is basically just put a boundary continuation

0 commit comments

Comments
 (0)