diff options
author | Miguel Sofer <miguel.sofer@gmail.com> | 2015-03-23 13:46:08 (GMT) |
---|---|---|
committer | Miguel Sofer <miguel.sofer@gmail.com> | 2015-03-23 13:46:08 (GMT) |
commit | 176fe9c104f50d0b560621ed09aef70f71b0422f (patch) | |
tree | f6f94a4407b773de5ab6811edcbc114c7c25835e | |
parent | 2593df952706439a96046069b2e1ce0184a9b6a3 (diff) | |
parent | 2b90e3f5416c14e18289019c872d1b654778f63f (diff) | |
download | tcl-176fe9c104f50d0b560621ed09aef70f71b0422f.zip tcl-176fe9c104f50d0b560621ed09aef70f71b0422f.tar.gz tcl-176fe9c104f50d0b560621ed09aef70f71b0422f.tar.bz2 |
fix comments describing tailcall implementation
-rw-r--r-- | generic/tclBasic.c | 85 |
1 files changed, 61 insertions, 24 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 361ed49..16e7a5d 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4142,8 +4142,9 @@ TclNREvalObjv( /* * data[1] stores a marker for use by tailcalls; it will be set to 1 by - * command redirectors (imports, alias, ensembles) so that tailcalls - * finishes the source command and not just the target. + * command redirectors (imports, alias, ensembles) so that tailcall skips + * this callback (that marks the end of the target command) and goes back + * to the end of the source command. */ if (iPtr->deferredCallbacks) { @@ -4406,7 +4407,7 @@ NRCommand( iPtr->numLevels--; /* - * If there is a tailcall, schedule it + * If there is a tailcall, schedule it next */ if (data[1] && (data[1] != INT2PTR(1))) { @@ -8170,27 +8171,24 @@ Tcl_NRCmdSwap( } /***************************************************************************** - * Stuff for tailcalls + * Tailcall related code ***************************************************************************** * - * Just to show that IT CAN BE DONE! The precise semantics are not simple, - * require more thought. Possibly need a new Tcl return code to do it right? - * Questions include: - * (1) How is the objc/objv tailcall to be run? My current thinking is that - * it should essentially be - * [tailcall a b c] <=> [uplevel 1 [list a b c]] - * with two caveats - * (a) the current frame is dropped first, after running all pending - * cleanup tasks and saving its namespace - * (b) 'a' is looked up in the returning frame's namespace, but the - * command is run in the context to which we are returning - * Current implementation does this if [tailcall] is called from within - * a proc, errors otherwise. - * (2) Should a tailcall bypass [catch] in the returning frame? Current - * implementation does not (or does it? Changed, test!) - it causes an - * error. - * - * FIXME NRE! + * The steps of the tailcall dance are as follows: + * + * 1. when [tailcall] is invoked, it stores the corresponding callback in + * the current CallFrame and returns TCL_RETURN + * 2. when the CallFrame is popped, it calls TclSetTailcall to store the + * callback in the proper NRCommand callback - the spot where the command + * that pushed the CallFrame is completely cleaned up + * 3. when the NRCommand callback runs, it schedules the tailcall callback + * to run immediately after it returns + * + * One delicate point is to properly define the NRCommand where the tailcall + * will execute. There are functions whose purpose is to help define the + * precise spot: TclMarkTailcall ("this is the spot") and TclSkipTailcall + * ("skip the next command: we are redirecting to it, tailcalls should run + * after WE return"), TclPushTailcallPoint (special for OO). */ void @@ -8224,6 +8222,18 @@ TclPushTailcallPoint( ((Interp *) interp)->numLevels++; } + +/* + *---------------------------------------------------------------------- + * + * TclSetTailcall -- + * + * Splice a tailcall command in the proper spot of the NRE callback + * stack, so that it runs at the right time. + * + *---------------------------------------------------------------------- + */ + void TclSetTailcall( Tcl_Interp *interp, @@ -8248,6 +8258,23 @@ TclSetTailcall( runPtr->data[1] = listPtr; } + +/* + *---------------------------------------------------------------------- + * + * TclNRTailcallObjCmd -- + * + * Prepare the tailcall as a list and store it in the current + * varFrame. When the frame is later popped the tailcall will be spliced + * at the proper place. + * + * Results: + * The first NRCommand callback that is not marked to be skipped is + * updated so that its data[1] field contains the tailcall list. + * + *---------------------------------------------------------------------- + */ + int TclNRTailcallObjCmd( ClientData clientData, @@ -8282,8 +8309,7 @@ TclNRTailcallObjCmd( /* * Create the callback to actually evaluate the tailcalled * command, then set it in the varFrame so that PopCallFrame can use it - * at the proper time. Being lazy: exploit the TclNRAddCallBack macro to - * build the callback. + * at the proper time. */ if (objc > 1) { @@ -8308,6 +8334,17 @@ TclNRTailcallObjCmd( return TCL_RETURN; } + +/* + *---------------------------------------------------------------------- + * + * TclNRTailcallEval -- + * + * This NREcallback actually causes the tailcall to be evaluated. + * + *---------------------------------------------------------------------- + */ + int TclNRTailcallEval( ClientData data[], |