@@ -1575,6 +1575,12 @@ static SgObject merge_winders(SgObject, SgObject);
15751575static SgObject take_prompt_winders (SgPrompt * , SgObject );
15761576static 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+
15781584static 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