summaryrefslogtreecommitdiffstats
path: root/generic/tclBasic.c
diff options
context:
space:
mode:
authorMiguel Sofer <miguel.sofer@gmail.com>2015-03-23 13:46:08 (GMT)
committerMiguel Sofer <miguel.sofer@gmail.com>2015-03-23 13:46:08 (GMT)
commit176fe9c104f50d0b560621ed09aef70f71b0422f (patch)
treef6f94a4407b773de5ab6811edcbc114c7c25835e /generic/tclBasic.c
parent2593df952706439a96046069b2e1ce0184a9b6a3 (diff)
parent2b90e3f5416c14e18289019c872d1b654778f63f (diff)
downloadtcl-176fe9c104f50d0b560621ed09aef70f71b0422f.zip
tcl-176fe9c104f50d0b560621ed09aef70f71b0422f.tar.gz
tcl-176fe9c104f50d0b560621ed09aef70f71b0422f.tar.bz2
fix comments describing tailcall implementation
Diffstat (limited to 'generic/tclBasic.c')
-rw-r--r--generic/tclBasic.c85
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[],