Import Tcl 8.6.10

This commit is contained in:
Steve Dower
2020-09-24 22:53:56 +01:00
parent 0343d03b22
commit 3bb8e3e086
1005 changed files with 593700 additions and 41637 deletions

View File

@@ -107,58 +107,6 @@ const AuxDataType tclJumptableInfoType = {
#define INVOKE(name) \
TclEmitInvoke(envPtr,INST_##name)
#define INDEX_END (-2)
/*
*----------------------------------------------------------------------
*
* GetIndexFromToken --
*
* Parse a token and get the encoded version of the index (as understood
* by TEBC), assuming it is at all knowable at compile time. Only handles
* indices that are integers or 'end' or 'end-integer'.
*
* Returns:
* TCL_OK if parsing succeeded, and TCL_ERROR if it failed.
*
* Side effects:
* Sets *index to the index value if successful.
*
*----------------------------------------------------------------------
*/
static inline int
GetIndexFromToken(
Tcl_Token *tokenPtr,
int *index)
{
Tcl_Obj *tmpObj = Tcl_NewObj();
int result, idx;
if (!TclWordKnownAtCompileTime(tokenPtr, tmpObj)) {
Tcl_DecrRefCount(tmpObj);
return TCL_ERROR;
}
result = TclGetIntFromObj(NULL, tmpObj, &idx);
if (result == TCL_OK) {
if (idx < 0) {
result = TCL_ERROR;
}
} else {
result = TclGetIntForIndexM(NULL, tmpObj, INDEX_END, &idx);
if (result == TCL_OK && idx > INDEX_END) {
result = TCL_ERROR;
}
}
Tcl_DecrRefCount(tmpObj);
if (result == TCL_OK) {
*index = idx;
}
return result;
}
/*
*----------------------------------------------------------------------
@@ -982,22 +930,48 @@ TclCompileStringRangeCmd(
fromTokenPtr = TokenAfter(stringTokenPtr);
toTokenPtr = TokenAfter(fromTokenPtr);
/* Every path must push the string argument */
CompileWord(envPtr, stringTokenPtr, interp, 1);
/*
* Parse the two indices.
*/
if (GetIndexFromToken(fromTokenPtr, &idx1) != TCL_OK) {
if (TclGetIndexFromToken(fromTokenPtr, TCL_INDEX_START, TCL_INDEX_AFTER,
&idx1) != TCL_OK) {
goto nonConstantIndices;
}
if (GetIndexFromToken(toTokenPtr, &idx2) != TCL_OK) {
/*
* Token parsed as an index expression. We treat all indices before
* the string the same as the start of the string.
*/
if (idx1 == TCL_INDEX_AFTER) {
/* [string range $s end+1 $last] must be empty string */
OP( POP);
PUSH( "");
return TCL_OK;
}
if (TclGetIndexFromToken(toTokenPtr, TCL_INDEX_BEFORE, TCL_INDEX_END,
&idx2) != TCL_OK) {
goto nonConstantIndices;
}
/*
* Token parsed as an index expression. We treat all indices after
* the string the same as the end of the string.
*/
if (idx2 == TCL_INDEX_BEFORE) {
/* [string range $s $first -1] must be empty string */
OP( POP);
PUSH( "");
return TCL_OK;
}
/*
* Push the operand onto the stack and then the substring operation.
*/
CompileWord(envPtr, stringTokenPtr, interp, 1);
OP44( STR_RANGE_IMM, idx1, idx2);
return TCL_OK;
@@ -1006,7 +980,6 @@ TclCompileStringRangeCmd(
*/
nonConstantIndices:
CompileWord(envPtr, stringTokenPtr, interp, 1);
CompileWord(envPtr, fromTokenPtr, interp, 2);
CompileWord(envPtr, toTokenPtr, interp, 3);
OP( STR_RANGE);
@@ -1022,124 +995,197 @@ TclCompileStringReplaceCmd(
* compiled. */
CompileEnv *envPtr) /* Holds the resulting instructions. */
{
Tcl_Token *tokenPtr, *valueTokenPtr, *replacementTokenPtr = NULL;
Tcl_Token *tokenPtr, *valueTokenPtr;
DefineLineInformation; /* TIP #280 */
int idx1, idx2;
int first, last;
if (parsePtr->numWords < 4 || parsePtr->numWords > 5) {
return TCL_ERROR;
}
/* Bytecode to compute/push string argument being replaced */
valueTokenPtr = TokenAfter(parsePtr->tokenPtr);
if (parsePtr->numWords == 5) {
tokenPtr = TokenAfter(valueTokenPtr);
tokenPtr = TokenAfter(tokenPtr);
replacementTokenPtr = TokenAfter(tokenPtr);
}
CompileWord(envPtr, valueTokenPtr, interp, 1);
/*
* Parse the indices. Will only compile special cases if both are
* constants and not an _integer_ less than zero (since we reserve
* negative indices here for end-relative indexing) or an end-based index
* greater than 'end' itself.
* Check for first index known and useful at compile time.
*/
tokenPtr = TokenAfter(valueTokenPtr);
if (GetIndexFromToken(tokenPtr, &idx1) != TCL_OK) {
goto genericReplace;
}
tokenPtr = TokenAfter(tokenPtr);
if (GetIndexFromToken(tokenPtr, &idx2) != TCL_OK) {
if (TclGetIndexFromToken(tokenPtr, TCL_INDEX_BEFORE, TCL_INDEX_AFTER,
&first) != TCL_OK) {
goto genericReplace;
}
/*
* We handle these replacements specially: first character (where
* idx1=idx2=0) and last character (where idx1=idx2=INDEX_END). Anything
* else and the semantics get rather screwy.
* Check for last index known and useful at compile time.
*/
tokenPtr = TokenAfter(tokenPtr);
if (TclGetIndexFromToken(tokenPtr, TCL_INDEX_BEFORE, TCL_INDEX_AFTER,
&last) != TCL_OK) {
goto genericReplace;
}
/*
* [string replace] is an odd bird. For many arguments it is
* a conventional substring replacer. However it also goes out
* of its way to become a no-op for many cases where it would be
* replacing an empty substring. Precisely, it is a no-op when
*
* (last < first) OR
* (last < 0) OR
* (end < first)
*
* For some compile-time values we can detect these cases, and
* compile direct to bytecode implementing the no-op.
*/
if (idx1 == 0 && idx2 == 0) {
int notEq, end;
if ((last == TCL_INDEX_BEFORE) /* Know (last < 0) */
|| (first == TCL_INDEX_AFTER) /* Know (first > end) */
/*
* Just working with the first character.
* Tricky to determine when runtime (last < first) can be
* certainly known based on the encoded values. Consider the
* cases...
*
* (first <= TCL_INDEX_END) &&
* (last == TCL_INDEX_AFTER) => cannot tell REJECT
* (last <= TCL_INDEX END) && (last < first) => ACCEPT
* else => cannot tell REJECT
*/
CompileWord(envPtr, valueTokenPtr, interp, 1);
if (replacementTokenPtr == NULL) {
/* Drop first */
OP44( STR_RANGE_IMM, 1, INDEX_END);
return TCL_OK;
}
/* Replace first */
CompileWord(envPtr, replacementTokenPtr, interp, 4);
OP4( OVER, 1);
PUSH( "");
OP( STR_EQ);
JUMP1( JUMP_FALSE, notEq);
OP( POP);
JUMP1( JUMP, end);
FIXJUMP1(notEq);
TclAdjustStackDepth(1, envPtr);
OP4( REVERSE, 2);
OP44( STR_RANGE_IMM, 1, INDEX_END);
OP1( STR_CONCAT1, 2);
FIXJUMP1(end);
return TCL_OK;
} else if (idx1 == INDEX_END && idx2 == INDEX_END) {
int notEq, end;
|| ((first <= TCL_INDEX_END) && (last <= TCL_INDEX_END)
&& (last < first)) /* Know (last < first) */
/*
* Just working with the last character.
* (first == TCL_INDEX_BEFORE) &&
* (last == TCL_INDEX_AFTER) => (first < last) REJECT
* (last <= TCL_INDEX_END) => cannot tell REJECT
* else => (first < last) REJECT
*
* else [[first >= TCL_INDEX_START]] &&
* (last == TCL_INDEX_AFTER) => cannot tell REJECT
* (last <= TCL_INDEX_END) => cannot tell REJECT
* else [[last >= TCL_INDEX START]] && (last < first) => ACCEPT
*/
CompileWord(envPtr, valueTokenPtr, interp, 1);
if (replacementTokenPtr == NULL) {
/* Drop last */
OP44( STR_RANGE_IMM, 0, INDEX_END-1);
return TCL_OK;
|| ((first >= TCL_INDEX_START) && (last >= TCL_INDEX_START)
&& (last < first))) { /* Know (last < first) */
if (parsePtr->numWords == 5) {
tokenPtr = TokenAfter(tokenPtr);
CompileWord(envPtr, tokenPtr, interp, 4);
OP( POP); /* Pop newString */
}
/* Replace last */
CompileWord(envPtr, replacementTokenPtr, interp, 4);
OP4( OVER, 1);
PUSH( "");
OP( STR_EQ);
JUMP1( JUMP_FALSE, notEq);
OP( POP);
JUMP1( JUMP, end);
FIXJUMP1(notEq);
TclAdjustStackDepth(1, envPtr);
OP4( REVERSE, 2);
OP44( STR_RANGE_IMM, 0, INDEX_END-1);
OP4( REVERSE, 2);
OP1( STR_CONCAT1, 2);
FIXJUMP1(end);
/* Original string argument now on TOS as result */
return TCL_OK;
}
if (parsePtr->numWords == 5) {
/*
* When we have a string replacement, we have to take care about
* not replacing empty substrings that [string replace] promises
* not to replace
*
* The remaining index values might be suitable for conventional
* string replacement, but only if they cannot possibly meet the
* conditions described above at runtime. If there's a chance they
* might, we would have to emit bytecode to check and at that point
* we're paying more in bytecode execution time than would make
* things worthwhile. Trouble is we are very limited in
* how much we can detect that at compile time. After decoding,
* we need, first:
*
* (first <= end)
*
* The encoded indices (first <= TCL_INDEX END) and
* (first == TCL_INDEX_BEFORE) always meets this condition, but
* any other encoded first index has some list for which it fails.
*
* We also need, second:
*
* (last >= 0)
*
* The encoded indices (last >= TCL_INDEX_START) and
* (last == TCL_INDEX_AFTER) always meet this condition but any
* other encoded last index has some list for which it fails.
*
* Finally we need, third:
*
* (first <= last)
*
* Considered in combination with the constraints we already have,
* we see that we can proceed when (first == TCL_INDEX_BEFORE)
* or (last == TCL_INDEX_AFTER). These also permit simplification
* of the prefix|replace|suffix construction. The other constraints,
* though, interfere with getting a guarantee that first <= last.
*/
if ((first == TCL_INDEX_BEFORE) && (last >= TCL_INDEX_START)) {
/* empty prefix */
tokenPtr = TokenAfter(tokenPtr);
CompileWord(envPtr, tokenPtr, interp, 4);
OP4( REVERSE, 2);
if (last == TCL_INDEX_AFTER) {
OP( POP); /* Pop original */
} else {
OP44( STR_RANGE_IMM, last + 1, TCL_INDEX_END);
OP1( STR_CONCAT1, 2);
}
return TCL_OK;
}
if ((last == TCL_INDEX_AFTER) && (first <= TCL_INDEX_END)) {
OP44( STR_RANGE_IMM, 0, first-1);
tokenPtr = TokenAfter(tokenPtr);
CompileWord(envPtr, tokenPtr, interp, 4);
OP1( STR_CONCAT1, 2);
return TCL_OK;
}
/* FLOW THROUGH TO genericReplace */
} else {
/*
* Need to process indices at runtime. This could be because the
* indices are not constants, or because we need to resolve them to
* absolute indices to work out if a replacement is going to happen.
* In any case, to runtime it is.
* When we have no replacement string to worry about, we may
* have more luck, because the forbidden empty string replacements
* are harmless when they are replaced by another empty string.
*/
if ((first == TCL_INDEX_BEFORE) || (first == TCL_INDEX_START)) {
/* empty prefix - build suffix only */
if ((last == TCL_INDEX_END) || (last == TCL_INDEX_AFTER)) {
/* empty suffix too => empty result */
OP( POP); /* Pop original */
PUSH ( "");
return TCL_OK;
}
OP44( STR_RANGE_IMM, last + 1, TCL_INDEX_END);
return TCL_OK;
} else {
if ((last == TCL_INDEX_END) || (last == TCL_INDEX_AFTER)) {
/* empty suffix - build prefix only */
OP44( STR_RANGE_IMM, 0, first-1);
return TCL_OK;
}
OP( DUP);
OP44( STR_RANGE_IMM, 0, first-1);
OP4( REVERSE, 2);
OP44( STR_RANGE_IMM, last + 1, TCL_INDEX_END);
OP1( STR_CONCAT1, 2);
return TCL_OK;
}
}
genericReplace:
CompileWord(envPtr, valueTokenPtr, interp, 1);
tokenPtr = TokenAfter(valueTokenPtr);
CompileWord(envPtr, tokenPtr, interp, 2);
tokenPtr = TokenAfter(tokenPtr);
CompileWord(envPtr, tokenPtr, interp, 3);
if (replacementTokenPtr != NULL) {
CompileWord(envPtr, replacementTokenPtr, interp, 4);
if (parsePtr->numWords == 5) {
tokenPtr = TokenAfter(tokenPtr);
CompileWord(envPtr, tokenPtr, interp, 4);
} else {
PUSH( "");
}
OP( STR_REPLACE);
return TCL_OK;
}
}
int
@@ -1450,7 +1496,7 @@ TclSubstCompile(
for (endTokenPtr = tokenPtr + parse.numTokens;
tokenPtr < endTokenPtr; tokenPtr = TokenAfter(tokenPtr)) {
int length, literal, catchRange, breakJump;
char buf[TCL_UTF_MAX];
char buf[TCL_UTF_MAX] = "";
JumpFixup startFixup, okFixup, returnFixup, breakFixup;
JumpFixup continueFixup, otherFixup, endFixup;