summaryrefslogtreecommitdiffstats
path: root/generic/tclBasic.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclBasic.c')
-rw-r--r--generic/tclBasic.c162
1 files changed, 148 insertions, 14 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index b83afe5..d97194c 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -16,7 +16,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclBasic.c,v 1.401 2009/08/12 16:06:41 dgp Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.402 2009/08/25 21:03:25 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -522,6 +522,7 @@ Tcl_CreateInterp(void)
Tcl_InitHashTable(iPtr->lineBCPtr, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(iPtr->lineLAPtr, TCL_ONE_WORD_KEYS);
Tcl_InitHashTable(iPtr->lineLABCPtr, TCL_ONE_WORD_KEYS);
+ iPtr->scriptCLLocPtr = NULL;
iPtr->activeVarTracePtr = NULL;
@@ -4767,7 +4768,8 @@ Tcl_EvalTokensStandard(
int count) /* Number of tokens to consider at tokenPtr.
* Must be at least 1. */
{
- return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL, 1);
+ return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL, 1,
+ NULL, NULL);
}
/*
@@ -4851,7 +4853,7 @@ Tcl_EvalEx(
* evaluation of the script. Only
* TCL_EVAL_GLOBAL is currently supported. */
{
- return TclEvalEx(interp, script, numBytes, flags, 1);
+ return TclEvalEx(interp, script, numBytes, flags, 1, NULL, script);
}
int
@@ -4865,7 +4867,24 @@ TclEvalEx(
int flags, /* Collection of OR-ed bits that control the
* evaluation of the script. Only
* TCL_EVAL_GLOBAL is currently supported. */
- int line) /* The line the script starts on. */
+ int line, /* The line the script starts on. */
+ int* clNextOuter, /* Information about an outer context for */
+ CONST char* outerScript) /* continuation line data. This is set only in
+ * EvalTokensStandard(), to properly handle
+ * [...]-nested commands. The 'outerScript'
+ * refers to the most-outer script containing the
+ * embedded command, which is refered to by
+ * 'script'. The 'clNextOuter' refers to the
+ * current entry in the table of continuation
+ * lines in this "master script", and the
+ * character offsets are relative to the
+ * 'outerScript' as well.
+ *
+ * If outerScript == script, then this call is
+ * for the outer-most script/command. See
+ * Tcl_EvalEx() and TclEvalObjEx() for places
+ * generating arguments for which this is true.
+ */
{
Interp *iPtr = (Interp *) interp;
const char *p, *next;
@@ -4891,6 +4910,23 @@ TclEvalEx(
int *linesStack = TclStackAlloc(interp, minObjs * sizeof(int));
/* TIP #280 Structures for tracking of command
* locations. */
+ /*
+ * Pointer for the tracking of invisible continuation lines. Initialized
+ * only if the caller gave us a table of locations to track, via
+ * scriptCLLocPtr. It always refers to the table entry holding the
+ * location of the next invisible continuation line to look for, while
+ * parsing the script.
+ */
+
+ int* clNext = NULL;
+
+ if (iPtr->scriptCLLocPtr) {
+ if (clNextOuter) {
+ clNext = clNextOuter;
+ } else {
+ clNext = &iPtr->scriptCLLocPtr->loc[0];
+ }
+ }
if (numBytes < 0) {
numBytes = strlen(script);
@@ -4916,12 +4952,12 @@ TclEvalEx(
/*
* TIP #280 Initialize tracking. Do not push on the frame stack yet.
*
- * We may cont. counting based on a specific context (CTX), or open a new
- * context, either for a sourced script, or 'eval'. For sourced files we
- * always have a path object, even if nothing was specified in the interp
- * itself. That makes code using it simpler as NULL checks can be left
- * out. Sourced file without path in the 'scriptFile' is possible during
- * Tcl initialization.
+ * We may continue counting based on a specific context (CTX), or open a
+ * new context, either for a sourced script, or 'eval'. For sourced files
+ * we always have a path object, even if nothing was specified in the
+ * interp itself. That makes code using it simpler as NULL checks can be
+ * left out. Sourced file without path in the 'scriptFile' is possible
+ * during Tcl initialization.
*/
eeFramePtr->level = iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level + 1 : 1;
@@ -4989,19 +5025,25 @@ TclEvalEx(
/*
* TIP #280 Track lines. The parser may have skipped text till it
* found the command we are now at. We have to count the lines in this
- * block.
+ * block, and do not forget invisible continuation lines.
*/
TclAdvanceLines(&line, p, parsePtr->commandStart);
+ TclAdvanceContinuations (&line, &clNext,
+ parsePtr->commandStart - outerScript);
gotParse = 1;
if (parsePtr->numWords > 0) {
/*
- * TIP #280. Track lines within the words of the current command.
+ * TIP #280. Track lines within the words of the current
+ * command. We use a separate pointer into the table of
+ * continuation line locations to not lose our position for the
+ * per-command parsing.
*/
int wordLine = line;
const char *wordStart = parsePtr->commandStart;
+ int* wordCLNext = clNext;
/*
* Generate an array of objects for the words of the command.
@@ -5033,6 +5075,8 @@ TclEvalEx(
*/
TclAdvanceLines(&wordLine, wordStart, tokenPtr->start);
+ TclAdvanceContinuations (&wordLine, &wordCLNext,
+ tokenPtr->start - outerScript);
wordStart = tokenPtr->start;
lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL)
@@ -5043,7 +5087,8 @@ TclEvalEx(
}
code = TclSubstTokens(interp, tokenPtr+1,
- tokenPtr->numComponents, NULL, wordLine);
+ tokenPtr->numComponents, NULL, wordLine,
+ wordCLNext, outerScript);
iPtr->evalFlags = 0;
@@ -5075,6 +5120,11 @@ TclEvalEx(
expand[objectsUsed] = 0;
objectsNeeded++;
}
+
+ if (wordCLNext) {
+ TclContinuationsEnterDerived (objv[objectsUsed],
+ wordStart - outerScript, wordCLNext);
+ }
} /* for loop */
iPtr->cmdFramePtr = eeFramePtr;
if (code != TCL_OK) {
@@ -5302,6 +5352,53 @@ TclAdvanceLines(
/*
*----------------------------------------------------------------------
+ *
+ * TclAdvanceContinuations --
+ *
+ * This procedure is a helper which counts the number of continuation
+ * lines (CL) in a block of text using a table of CL locations and
+ * advances an external counter, and the pointer into the table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The specified counter is advanced per the number of continuation lines
+ * found.
+ *
+ * TIP #280
+ *----------------------------------------------------------------------
+ */
+
+void
+TclAdvanceContinuations (line,clNextPtrPtr,loc)
+ int* line;
+ int** clNextPtrPtr;
+ int loc;
+{
+ /*
+ * Track the invisible continuation lines embedded in a script, if
+ * any. Here they are just spaces (already). They were removed by
+ * EvalTokensStandard() via Tcl_UtfBackslash().
+ *
+ * *clNextPtrPtr <=> We have continuation lines to track.
+ * **clNextPtrPtr >= 0 <=> We are not beyond the last possible location.
+ * loc >= **clNextPtrPtr <=> We stepped beyond the current cont. line.
+ */
+
+ while (*clNextPtrPtr && (**clNextPtrPtr >= 0) && (loc >= **clNextPtrPtr)) {
+ /*
+ * We just stepped over an invisible continuation line. Adjust the
+ * line counter and step to the table entry holding the location of
+ * the next continuation line to track.
+ */
+ (*line) ++;
+ (*clNextPtrPtr) ++;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
* Note: The whole data structure access for argument location tracking is
* hidden behind these three functions. The only parts open are the lineLAPtr
* field in the Interp structure. The CFWord definition is internal to here.
@@ -5919,6 +6016,33 @@ TclNREvalObjEx(
const char *script;
int numSrcBytes;
+ /*
+ * Now we check if we have data about invisible continuation lines for
+ * the script, and make it available to the direct script parser and
+ * evaluator we are about to call, if so.
+ *
+ * It may be possible that the script Tcl_Obj* can be free'd while the
+ * evaluator is using it, leading to the release of the associated
+ * ContLineLoc structure as well. To ensure that the latter doesn't
+ * happen we set a lock on it. We release this lock later in this
+ * function, after the evaluator is done. The relevant "lineCLPtr"
+ * hashtable is managed in the file "tclObj.c".
+ *
+ * Another important action is to save (and later restore) the
+ * continuation line information of the caller, in case we are
+ * executing nested commands in the eval/direct path.
+ */
+
+ ContLineLoc* saveCLLocPtr = iPtr->scriptCLLocPtr;
+ ContLineLoc* clLocPtr = TclContinuationsGet (objPtr);
+
+ if (clLocPtr) {
+ iPtr->scriptCLLocPtr = clLocPtr;
+ Tcl_Preserve (iPtr->scriptCLLocPtr);
+ } else {
+ iPtr->scriptCLLocPtr = NULL;
+ }
+
Tcl_IncrRefCount(objPtr);
if (invoker == NULL) {
/*
@@ -5974,7 +6098,7 @@ TclNREvalObjEx(
iPtr->evalFlags |= TCL_EVAL_CTX;
result = TclEvalEx(interp, script, numSrcBytes, flags,
- ctxPtr->line[word]);
+ ctxPtr->line[word], NULL, script);
if (pc) {
/*
@@ -5985,6 +6109,16 @@ TclNREvalObjEx(
}
}
TclStackFree(interp, ctxPtr);
+
+ /*
+ * Now release the lock on the continuation line information, if
+ * any, and restore the caller's settings.
+ */
+
+ if (iPtr->scriptCLLocPtr) {
+ Tcl_Release (iPtr->scriptCLLocPtr);
+ }
+ iPtr->scriptCLLocPtr = saveCLLocPtr;
}
TclDecrRefCount(objPtr);
return result;