summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authordgp <dgp@users.sourceforge.net>2003-11-14 20:44:43 (GMT)
committerdgp <dgp@users.sourceforge.net>2003-11-14 20:44:43 (GMT)
commit17f540b256d78b8a6fc8bd9121a633dac6c23b19 (patch)
tree1abdc7a020d4095171e8cb7f16def9be025cb664 /generic
parentf745c9aa31bbdf8f71589fa25d30ce50cad94652 (diff)
downloadtcl-17f540b256d78b8a6fc8bd9121a633dac6c23b19.zip
tcl-17f540b256d78b8a6fc8bd9121a633dac6c23b19.tar.gz
tcl-17f540b256d78b8a6fc8bd9121a633dac6c23b19.tar.bz2
* doc/ParseCmd.3: Implementation of TIP 157. Adds recognition
* doc/Tcl.n: of the new leading {expand} syntax on words. * generic/tcl.h: Parses such words as the new Tcl_Token type * generic/tclBasic.c: TCL_TOKEN_EXPAND_WORD. Updated Tcl_EvalEx * generic/tclCompile.c: and the bytecode compiler/execution engine * generic/tclCompile.h: to recognize the new token type. New opcodes * generic/tclExecute.c: INST_LIST_VERIFY and INST_INVOKE_EXP and a new * generic/tclParse.c: operand type OPERAND_ULIST1 are defined. Docs * generic/tclTest.c: and tests are included. * tests/basic.test: * tests/compile.test: * tests/parse.test: * library/auto.tcl: Replaced several [eval]s used to perform * library/package.tcl: argument expansion with the new syntax. * library/safe.tcl: In the test files lindex.test and lset.test, * tests/cmdInfo.test: replaced use of [eval] to force direct * tests/encoding.test: string evaluation with use of [testevalex] * tests/execute.test: which more directly and robustly serves the * tests/fCmd.test: same purpose. * tests/http.test: * tests/init.test: * tests/interp.test: * tests/io.test: * tests/ioUtil.test: * tests/iogt.test: * tests/lindex.test: * tests/lset.test: * tests/namespace-old.test: * tests/namespace.test: * tests/pkg.test: * tests/pkgMkIndex.test: * tests/proc.test: * tests/reg.test: * tests/trace.test: * tests/upvar.test: * tests/winConsole.test: * tests/winFCmd.test:
Diffstat (limited to 'generic')
-rw-r--r--generic/tcl.h3
-rw-r--r--generic/tclBasic.c111
-rw-r--r--generic/tclCompile.c117
-rw-r--r--generic/tclCompile.h29
-rw-r--r--generic/tclExecute.c126
-rw-r--r--generic/tclParse.c42
-rw-r--r--generic/tclTest.c5
7 files changed, 376 insertions, 57 deletions
diff --git a/generic/tcl.h b/generic/tcl.h
index e89690d..7a9a7dd 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tcl.h,v 1.166 2003/10/13 16:48:06 vincentdarley Exp $
+ * RCS: @(#) $Id: tcl.h,v 1.167 2003/11/14 20:44:44 dgp Exp $
*/
#ifndef _TCL
@@ -2078,6 +2078,7 @@ typedef struct Tcl_Token {
#define TCL_TOKEN_VARIABLE 32
#define TCL_TOKEN_SUB_EXPR 64
#define TCL_TOKEN_OPERATOR 128
+#define TCL_TOKEN_EXPAND_WORD 256
/*
* Parsing error types. On any parsing error, one of these values
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index 7f89d7e..ec4bb19 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -13,7 +13,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.92 2003/10/14 15:44:52 dgp Exp $
+ * RCS: @(#) $Id: tclBasic.c,v 1.93 2003/11/14 20:44:44 dgp Exp $
*/
#include "tclInt.h"
@@ -3499,9 +3499,10 @@ Tcl_EvalEx(interp, script, numBytes, flags)
CONST char *p, *next;
Tcl_Parse parse;
#define NUM_STATIC_OBJS 20
- Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv;
+ Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv, **objvSpace;
+ int expandStatic[NUM_STATIC_OBJS], *expand;
Tcl_Token *tokenPtr;
- int i, code, commandLength, bytesLeft;
+ int i, code, commandLength, bytesLeft, expandRequested;
CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
* in case TCL_EVAL_GLOBAL was set. */
int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS);
@@ -3529,7 +3530,8 @@ Tcl_EvalEx(interp, script, numBytes, flags)
* command from the script and then executes it.
*/
- objv = staticObjArray;
+ objv = objvSpace = staticObjArray;
+ expand = expandStatic;
p = script;
bytesLeft = numBytes;
iPtr->evalFlags = 0;
@@ -3544,24 +3546,88 @@ Tcl_EvalEx(interp, script, numBytes, flags)
/*
* Generate an array of objects for the words of the command.
*/
+ int objectsNeeded = 0;
- if (parse.numWords <= NUM_STATIC_OBJS) {
- objv = staticObjArray;
- } else {
- objv = (Tcl_Obj **) ckalloc((unsigned)
+ if (parse.numWords > NUM_STATIC_OBJS) {
+ expand = (int *) ckalloc((unsigned)
+ (parse.numWords * sizeof (int)));
+ objvSpace = (Tcl_Obj **) ckalloc((unsigned)
(parse.numWords * sizeof (Tcl_Obj *)));
}
+ expandRequested = 0;
+ objv = objvSpace;
for (objectsUsed = 0, tokenPtr = parse.tokenPtr;
objectsUsed < parse.numWords;
objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) {
code = TclSubstTokens(interp, tokenPtr+1,
tokenPtr->numComponents, NULL);
- if (code == TCL_OK) {
- objv[objectsUsed] = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(objv[objectsUsed]);
- } else {
+ if (code != TCL_OK) {
goto error;
}
+ objv[objectsUsed] = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(objv[objectsUsed]);
+ if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
+ int numElements;
+
+ code = Tcl_ListObjLength(interp,
+ objv[objectsUsed], &numElements);
+ if (code == TCL_ERROR) {
+ /* Attempt to expand a non-list */
+ Tcl_Obj *msg =
+ Tcl_NewStringObj("\n (expanding word ", -1);
+ Tcl_Obj *wordNum = Tcl_NewIntObj(objectsUsed);
+ Tcl_IncrRefCount(wordNum);
+ Tcl_IncrRefCount(msg);
+ Tcl_AppendObjToObj(msg, wordNum);
+ Tcl_DecrRefCount(wordNum);
+ Tcl_AppendToObj(msg, ")", -1);
+ TclAppendObjToErrorInfo(interp, msg);
+ Tcl_DecrRefCount(msg);
+ goto error;
+ }
+ expandRequested = 1;
+ expand[objectsUsed] = 1;
+ objectsNeeded += (numElements ? numElements : 1);
+ } else {
+ expand[objectsUsed] = 0;
+ objectsNeeded++;
+ }
+ }
+ if (expandRequested) {
+ /* Some word expansion was requested. Check for objv resize */
+ Tcl_Obj **copy = objvSpace;
+ int wordIdx = parse.numWords;
+ int objIdx = objectsNeeded - 1;
+
+ if ((parse.numWords > NUM_STATIC_OBJS)
+ || (objectsNeeded > NUM_STATIC_OBJS)) {
+ objv = objvSpace = (Tcl_Obj **) ckalloc((unsigned)
+ (objectsNeeded * sizeof (Tcl_Obj *)));
+ }
+
+ objectsUsed = 0;
+ while (wordIdx--) {
+ if (expand[wordIdx]) {
+ int numElements;
+ Tcl_Obj **elements, *temp = copy[wordIdx];
+ Tcl_ListObjGetElements(NULL, temp,
+ &numElements, &elements);
+ objectsUsed += numElements;
+ while (numElements--) {
+ objv[objIdx--] = elements[numElements];
+ Tcl_IncrRefCount(elements[numElements]);
+ }
+ Tcl_DecrRefCount(temp);
+ } else {
+ objv[objIdx--] = copy[wordIdx];
+ objectsUsed++;
+ }
+ }
+ objv += objIdx+1;
+
+ if (copy != staticObjArray) {
+ ckfree((char *) copy);
+ }
}
/*
@@ -3589,9 +3655,17 @@ Tcl_EvalEx(interp, script, numBytes, flags)
Tcl_DecrRefCount(objv[i]);
}
objectsUsed = 0;
- if (objv != staticObjArray) {
- ckfree((char *) objv);
- objv = staticObjArray;
+ if (objvSpace != staticObjArray) {
+ ckfree((char *) objvSpace);
+ objvSpace = staticObjArray;
+ }
+ /*
+ * Free expand separately since objvSpace could have been
+ * reallocated above.
+ */
+ if (expand != expandStatic) {
+ ckfree((char *) expand);
+ expand = expandStatic;
}
}
@@ -3637,8 +3711,11 @@ Tcl_EvalEx(interp, script, numBytes, flags)
if (gotParse) {
Tcl_FreeParse(&parse);
}
- if (objv != staticObjArray) {
- ckfree((char *) objv);
+ if (objvSpace != staticObjArray) {
+ ckfree((char *) objvSpace);
+ }
+ if (expand != expandStatic) {
+ ckfree((char *) expand);
}
iPtr->varFramePtr = savedVarFramePtr;
return code;
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index 66d4bea..ee1a8a9 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompile.c,v 1.51 2003/10/14 15:44:52 dgp Exp $
+ * RCS: @(#) $Id: tclCompile.c,v 1.52 2003/11/14 20:44:44 dgp Exp $
*/
#include "tclInt.h"
@@ -273,6 +273,10 @@ InstructionDesc tclInstructionTable[] = {
/* return TCL_RETURN code. */
{"expon", 1, -1, 0, {OPERAND_NONE}},
/* Binary exponentiation operator: push (stknext ** stktop) */
+ {"listverify", 1, 0, 0, {OPERAND_NONE}},
+ /* Test that top of stack is a valid list; error if not */
+ {"invokeExp", INT_MIN, INT_MIN, 2, {OPERAND_UINT4, OPERAND_ULIST1}},
+ /* Invoke with expansion: <objc,objv> = expanded <op1,top op1> */
{0}
};
@@ -843,6 +847,10 @@ TclCompileScript(interp, script, numBytes, envPtr)
}
gotParse = 1;
if (parse.numWords > 0) {
+ int expand = 0;
+ unsigned char delta = 1;
+ Tcl_DString deltaList;
+
/*
* If not the first command, pop the previous command's result
* and, if we're compiling a top level command, update the last
@@ -883,28 +891,57 @@ TclCompileScript(interp, script, numBytes, envPtr)
fprintf(stdout, "\n");
}
#endif
+
/*
- * Each iteration of the following loop compiles one word
- * from the command.
+ * Check whether expansion has been requested for any of
+ * the words
*/
-
+
+ for (wordIdx = 0, tokenPtr = parse.tokenPtr;
+ wordIdx < parse.numWords;
+ wordIdx++, tokenPtr += (tokenPtr->numComponents + 1)) {
+ if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
+ expand = 1;
+ Tcl_DStringInit(&deltaList);
+ break;
+ }
+ }
+
envPtr->numCommands++;
currCmdIndex = (envPtr->numCommands - 1);
lastTopLevelCmdIndex = currCmdIndex;
startCodeOffset = (envPtr->codeNext - envPtr->codeStart);
EnterCmdStartData(envPtr, currCmdIndex,
(parse.commandStart - envPtr->source), startCodeOffset);
+
+ /*
+ * Each iteration of the following loop compiles one word
+ * from the command.
+ */
for (wordIdx = 0, tokenPtr = parse.tokenPtr;
- wordIdx < parse.numWords;
- wordIdx++, tokenPtr += (tokenPtr->numComponents + 1)) {
+ wordIdx < parse.numWords; delta++, wordIdx++,
+ tokenPtr += (tokenPtr->numComponents + 1)) {
+
+ if ((delta == 255)
+ && (tokenPtr->type != TCL_TOKEN_EXPAND_WORD)) {
+ /*
+ * Push an empty list for expansion so our delta
+ * between expanded words doesn't overflow a byte
+ */
+ objIndex = TclRegisterNewLiteral(envPtr, "", 0);
+ TclEmitPush(objIndex, envPtr);
+ Tcl_DStringAppend(&deltaList, &delta, 1);
+ delta = 1;
+ }
+
if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
/*
* If this is the first word and the command has a
* compile procedure, let it compile the command.
*/
- if (wordIdx == 0) {
+ if ((wordIdx == 0) && !expand) {
if (envPtr->procPtr != NULL) {
cmdNsPtr = envPtr->procPtr->cmdPtr->nsPtr;
} else {
@@ -987,20 +1024,55 @@ TclCompileScript(interp, script, numBytes, envPtr)
goto log;
}
}
+ if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) {
+
+ if ((tokenPtr->numComponents == 1)
+ && (tokenPtr[1].type == TCL_TOKEN_TEXT)) {
+ /*
+ * The value to be expanded is fully known
+ * now at compile time. We can check list
+ * validity, so we do not have to do so at
+ * runtime
+ */
+ int length;
+ Tcl_Obj *testObj = Tcl_NewStringObj(tokenPtr[1].start,
+ tokenPtr[1].size);
+ if (TCL_OK !=
+ Tcl_ListObjLength(NULL, testObj, &length)) {
+ /*
+ * Not a valid list, so emit instructions to
+ * test list validity (and fail) at runtime
+ */
+ TclEmitOpcode(INST_LIST_VERIFY, envPtr);
+ }
+ } else {
+ /*
+ * Value to expand unknown until runtime, so
+ * include a runtime check for valid list
+ */
+ TclEmitOpcode(INST_LIST_VERIFY, envPtr);
+ }
+ Tcl_DStringAppend(&deltaList, (char *)&delta, 1);
+ delta = 0;
+ }
}
/*
* Emit an invoke instruction for the command. We skip this
* if a compile procedure was found for the command.
*/
-
- if (wordIdx > 0) {
+
+ if (expand) {
+ TclEmitInstInt4(INST_INVOKE_EXP, wordIdx, envPtr);
+ TclEmitImmDeltaList1(&deltaList, envPtr);
+ Tcl_DStringFree(&deltaList);
+ } else if (wordIdx > 0) {
if (wordIdx <= 255) {
TclEmitInstInt1(INST_INVOKE_STK1, wordIdx, envPtr);
} else {
TclEmitInstInt4(INST_INVOKE_STK4, wordIdx, envPtr);
}
- }
+ }
/*
* Update the compilation environment structure and record the
@@ -3146,13 +3218,13 @@ TclPrintInstruction(codePtr, pc)
register InstructionDesc *instDesc = &tclInstructionTable[opCode];
unsigned char *codeStart = codePtr->codeStart;
unsigned int pcOffset = (pc - codeStart);
- int opnd, i, j;
+ int opnd, i, j, numBytes = 1;
fprintf(stdout, "(%u) %s ", pcOffset, instDesc->name);
for (i = 0; i < instDesc->numOperands; i++) {
switch (instDesc->opTypes[i]) {
case OPERAND_INT1:
- opnd = TclGetInt1AtPtr(pc+1+i);
+ opnd = TclGetInt1AtPtr(pc+numBytes); numBytes++;
if ((i == 0) && ((opCode == INST_JUMP1)
|| (opCode == INST_JUMP_TRUE1)
|| (opCode == INST_JUMP_FALSE1))) {
@@ -3162,7 +3234,7 @@ TclPrintInstruction(codePtr, pc)
}
break;
case OPERAND_INT4:
- opnd = TclGetInt4AtPtr(pc+1+i);
+ opnd = TclGetInt4AtPtr(pc+numBytes); numBytes += 4;
if ((i == 0) && ((opCode == INST_JUMP4)
|| (opCode == INST_JUMP_TRUE4)
|| (opCode == INST_JUMP_FALSE4))) {
@@ -3172,7 +3244,7 @@ TclPrintInstruction(codePtr, pc)
}
break;
case OPERAND_UINT1:
- opnd = TclGetUInt1AtPtr(pc+1+i);
+ opnd = TclGetUInt1AtPtr(pc+numBytes); numBytes++;
if ((i == 0) && (opCode == INST_PUSH1)) {
fprintf(stdout, "%u # ", (unsigned int) opnd);
TclPrintObject(stdout, codePtr->objArrayPtr[opnd], 40);
@@ -3185,7 +3257,6 @@ TclPrintInstruction(codePtr, pc)
if (opnd >= localCt) {
panic("TclPrintInstruction: bad local var index %u (%u locals)\n",
(unsigned int) opnd, localCt);
- return instDesc->numBytes;
}
for (j = 0; j < opnd; j++) {
localPtr = localPtr->nextPtr;
@@ -3202,7 +3273,7 @@ TclPrintInstruction(codePtr, pc)
}
break;
case OPERAND_UINT4:
- opnd = TclGetUInt4AtPtr(pc+1+i);
+ opnd = TclGetUInt4AtPtr(pc+numBytes); numBytes += 4;
if (opCode == INST_PUSH4) {
fprintf(stdout, "%u # ", opnd);
TclPrintObject(stdout, codePtr->objArrayPtr[opnd], 40);
@@ -3215,7 +3286,6 @@ TclPrintInstruction(codePtr, pc)
if (opnd >= localCt) {
panic("TclPrintInstruction: bad local var index %u (%u locals)\n",
(unsigned int) opnd, localCt);
- return instDesc->numBytes;
}
for (j = 0; j < opnd; j++) {
localPtr = localPtr->nextPtr;
@@ -3231,13 +3301,24 @@ TclPrintInstruction(codePtr, pc)
fprintf(stdout, "%u ", (unsigned int) opnd);
}
break;
+
+ case OPERAND_ULIST1:
+ opnd = TclGetUInt1AtPtr(pc+numBytes); numBytes++;
+ fprintf(stdout, "{");
+ while (opnd) {
+ fprintf(stdout, "%u ", opnd);
+ opnd = TclGetUInt1AtPtr(pc+numBytes); numBytes++;
+ }
+ fprintf(stdout, "0}");
+ break;
+
case OPERAND_NONE:
default:
break;
}
}
fprintf(stdout, "\n");
- return instDesc->numBytes;
+ return numBytes;
}
/*
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index 89f27a5..869c7ad 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -8,7 +8,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclCompile.h,v 1.38 2003/09/15 09:46:22 dkf Exp $
+ * RCS: @(#) $Id: tclCompile.h,v 1.39 2003/11/14 20:44:44 dgp Exp $
*/
#ifndef _TCLCOMPILATION
@@ -526,8 +526,11 @@ typedef struct ByteCode {
#define INST_EXPON 99 /* TIP#123 - exponentiation */
+#define INST_LIST_VERIFY 100
+#define INST_INVOKE_EXP 101
+
/* The last opcode */
-#define LAST_INST_OPCODE 99
+#define LAST_INST_OPCODE 101
/*
* Table describing the Tcl bytecode instructions: their name (for
@@ -545,7 +548,8 @@ typedef enum InstOperandType {
OPERAND_INT1, /* One byte signed integer. */
OPERAND_INT4, /* Four byte signed integer. */
OPERAND_UINT1, /* One byte unsigned integer. */
- OPERAND_UINT4 /* Four byte unsigned integer. */
+ OPERAND_UINT4, /* Four byte unsigned integer. */
+ OPERAND_ULIST1 /* List of one byte unsigned integers. */
} InstOperandType;
typedef struct InstructionDesc {
@@ -927,6 +931,25 @@ EXTERN int TclCompileVariableCmd _ANSI_ARGS_((
*(envPtr)->codeNext++ = \
(unsigned char) ((unsigned int) (i) );\
TclUpdateStackReqs(op, i, envPtr)
+
+/*
+ * Macro to emit an immediate list of index deltas in the code stream.
+ * The ANSI C "prototypes" for this macro is:
+ *
+ * EXTERN void TclEmitImmList1 _ANSI_ARGS_((Tcl_Obj *listPtr,
+ * CompileEnv *envPtr));
+ */
+
+#define TclEmitImmDeltaList1(listPtr, envPtr) \
+ { \
+ int numBytes = Tcl_DStringLength(listPtr) + 1; \
+ while (((envPtr)->codeNext + numBytes) > (envPtr)->codeEnd) { \
+ TclExpandCodeArray(envPtr); \
+ } \
+ memcpy((VOID *) (envPtr)->codeNext, \
+ (VOID *)Tcl_DStringValue(listPtr), (size_t) numBytes); \
+ (envPtr)->codeNext += numBytes; \
+ }
/*
* Macro to push a Tcl object onto the Tcl evaluation stack. It emits the
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index 25a5cdc..c642112 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclExecute.c,v 1.113 2003/10/28 22:06:14 msofer Exp $
+ * RCS: @(#) $Id: tclExecute.c,v 1.114 2003/11/14 20:44:44 dgp Exp $
*/
#include "tclInt.h"
@@ -1078,7 +1078,9 @@ TclExecuteByteCode(interp, codePtr)
Tcl_WideInt w;
int isWide;
register int cleanup;
+ int objc = 0;
Tcl_Obj *objResultPtr;
+ Tcl_Obj **objv = NULL, **stackObjArray = NULL;
char *part1, *part2;
Var *varPtr, *arrayPtr;
CallFrame *varFramePtr = iPtr->varFramePtr;
@@ -1304,21 +1306,121 @@ TclExecuteByteCode(interp, codePtr)
TRACE_WITH_OBJ(("%u => ", opnd), objResultPtr);
NEXT_INST_V(2, opnd, 1);
}
-
+
+ case INST_LIST_VERIFY:
+ {
+ int numElements = 0;
+ valuePtr = *tosPtr;
+
+ result = Tcl_ListObjLength(interp, valuePtr, &numElements);
+ if (result != TCL_OK) {
+ TRACE_WITH_OBJ(("%.30s => ERROR: ", O2S(valuePtr)),
+ Tcl_GetObjResult(interp));
+ goto checkForCatch;
+ }
+ NEXT_INST_F(1, 0, 0);
+ }
+
+ case INST_INVOKE_EXP:
+ {
+ int numWords = TclGetUInt4AtPtr(pc+1);
+ int spaceAvailable = eePtr->endPtr - tosPtr;
+ unsigned char *deltaPtr, *deltaPtrStart = pc+5;
+ Tcl_Obj **wordv = tosPtr - (numWords - 1);
+ int objIdx, wordIdx, wordToExpand = -1;
+
+ /*
+ * Compute number of objects needed to store the
+ * command after expansion is complete.
+ */
+
+ opnd = objc = numWords;
+ for (deltaPtr = deltaPtrStart; *deltaPtr; deltaPtr++) {
+ int numElements;
+ wordToExpand += TclGetUInt1AtPtr(deltaPtr);
+ Tcl_ListObjLength(NULL, wordv[wordToExpand], &numElements);
+ objc += numElements - 1;
+ }
+
+ /*
+ * We'll store the expanded command in the stack expansion
+ * space just above tosPtr, assuming there is room. Otherwise,
+ * allocate enough heap storage to store the expanded command.
+ */
+
+ objv = stackObjArray = tosPtr + 1;
+ if (objc > spaceAvailable) {
+ objv = (Tcl_Obj **) ckalloc((unsigned)
+ (objc * sizeof(Tcl_Obj *)));
+ } else {
+ tosPtr += objc;
+ }
+
+ objIdx = 0;
+ deltaPtr = deltaPtrStart;
+ wordToExpand = TclGetUInt1AtPtr(deltaPtr) - 1;
+ for (wordIdx = 0; wordIdx < numWords; wordIdx++) {
+
+ /*
+ * Copy words (expanding some) from wordv to objv.
+ * Note that we do not increment refCounts. We
+ * rely on the references in wordv (on the execution
+ * stack) to be sufficient to keep the values around
+ * as long as we need them.
+ */
+
+ if (wordIdx == wordToExpand) {
+ int i, numElements;
+ Tcl_Obj **elements, *temp = wordv[wordIdx];
+
+ /*
+ * Make sure the list we expand is unshared.
+ * If it is not shared, then the stack holds the
+ * only reference to it, and there is no danger
+ * the list will shimmer to another type (and
+ * possibly free the elements of the list) before
+ * we are done with the command evaluation.
+ */
+
+ if (Tcl_IsShared(temp)) {
+ Tcl_DecrRefCount(temp);
+ temp = Tcl_DuplicateObj(temp);
+ Tcl_IncrRefCount(temp);
+ wordv[wordIdx] = temp;
+ }
+ Tcl_ListObjGetElements(NULL, temp, &numElements, &elements);
+ for (i=0; i<numElements; i++) {
+ objv[objIdx++] = elements[i];
+ }
+ ++deltaPtr;
+ if (*deltaPtr) {
+ wordToExpand += TclGetUInt1AtPtr(deltaPtr);
+ } else {
+ wordToExpand = -1;
+ }
+ } else {
+ objv[objIdx++] = wordv[wordIdx];
+ }
+ }
+ pcAdjustment = (deltaPtr - pc) + 1;
+ goto doInvocation;
+ }
+
case INST_INVOKE_STK4:
opnd = TclGetUInt4AtPtr(pc+1);
+ objc = opnd;
+ objv = stackObjArray = (tosPtr - (objc-1));
pcAdjustment = 5;
goto doInvocation;
case INST_INVOKE_STK1:
opnd = TclGetUInt1AtPtr(pc+1);
+ objc = opnd;
+ objv = stackObjArray = (tosPtr - (objc-1));
pcAdjustment = 2;
doInvocation:
{
- int objc = opnd; /* The number of arguments. */
- Tcl_Obj **objv; /* The array of argument objects. */
-
/*
* We keep the stack reference count as a (char *), as that
* works nicely as a portable pointer-sized counter.
@@ -1326,14 +1428,6 @@ TclExecuteByteCode(interp, codePtr)
char **preservedStackRefCountPtr;
- /*
- * Reference to memory block containing
- * objv array (must be kept live throughout
- * trace and command invokations.)
- */
-
- objv = (tosPtr - (objc-1));
-
#ifdef TCL_COMPILE_DEBUG
if (tclTraceExec >= 2) {
if (traceInstructions) {
@@ -1418,6 +1512,12 @@ TclExecuteByteCode(interp, codePtr)
ckfree((VOID *) preservedStackRefCountPtr);
}
+ if (objv != stackObjArray) {
+ ckfree((char *) objv);
+ } else if (*pc == INST_INVOKE_EXP) {
+ tosPtr -= objc;
+ }
+
if (result == TCL_OK) {
/*
* Push the call's object result and continue execution
diff --git a/generic/tclParse.c b/generic/tclParse.c
index 4dd2fcb..475f1e9 100644
--- a/generic/tclParse.c
+++ b/generic/tclParse.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclParse.c,v 1.28 2003/11/02 18:57:35 dkf Exp $
+ * RCS: @(#) $Id: tclParse.c,v 1.29 2003/11/14 20:44:45 dgp Exp $
*/
#include "tclInt.h"
@@ -287,6 +287,8 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
parsePtr->commandStart = src;
while (1) {
+ int expandWord = 0;
+
/*
* Create the token for the word.
*/
@@ -319,11 +321,12 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
parsePtr->numWords++;
/*
- * At this point the word can have one of three forms: something
- * enclosed in quotes, something enclosed in braces, or an
- * unquoted word (anything else).
+ * At this point the word can have one of four forms: something
+ * enclosed in quotes, something enclosed in braces, and
+ * expanding word, or an unquoted word (anything else).
*/
+parseWord:
if (*src == '"') {
if (Tcl_ParseQuotedString(interp, src, numBytes,
parsePtr, 1, &termPtr) != TCL_OK) {
@@ -331,11 +334,39 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
}
src = termPtr; numBytes = parsePtr->end - src;
} else if (*src == '{') {
+ static char expPfx[] = "expand";
+ CONST size_t expPfxLen = sizeof(expPfx) - 1;
+ int expIdx = wordIndex + 1;
+ Tcl_Token *expPtr;
+
if (Tcl_ParseBraces(interp, src, numBytes,
parsePtr, 1, &termPtr) != TCL_OK) {
goto error;
}
src = termPtr; numBytes = parsePtr->end - src;
+
+ /*
+ * Check whether the braces contained
+ * the word expansion prefix.
+ */
+
+ expPtr = &parsePtr->tokenPtr[expIdx];
+ if ( (expPfxLen == expPtr->size)
+ /* Same length as prefix */
+ && (0 == expandWord)
+ /* Haven't seen prefix already */
+ && (1 == parsePtr->numTokens - expIdx)
+ /* Only one token */
+ && (0 == strncmp(expPfx,expPtr->start,expPfxLen))
+ /* Is the prefix */
+ && (numBytes > 0)
+ && (0 == TclParseWhiteSpace(termPtr, 1, parsePtr, &type))
+ /* Non-whitespace follows */
+ ) {
+ expandWord = 1;
+ parsePtr->numTokens--;
+ goto parseWord;
+ }
} else {
/*
* This is an unquoted word. Call ParseTokens and let it do
@@ -362,6 +393,9 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
&& (tokenPtr[1].type == TCL_TOKEN_TEXT)) {
tokenPtr->type = TCL_TOKEN_SIMPLE_WORD;
}
+ if (expandWord) {
+ tokenPtr->type = TCL_TOKEN_EXPAND_WORD;
+ }
/*
* Do two additional checks: (a) make sure we're really at the
diff --git a/generic/tclTest.c b/generic/tclTest.c
index effa8a3..c9ff8cb 100644
--- a/generic/tclTest.c
+++ b/generic/tclTest.c
@@ -14,7 +14,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclTest.c,v 1.69 2003/10/13 16:48:06 vincentdarley Exp $
+ * RCS: @(#) $Id: tclTest.c,v 1.70 2003/11/14 20:44:45 dgp Exp $
*/
#define TCL_TEST
@@ -3045,6 +3045,9 @@ PrintParse(interp, parsePtr)
for (i = 0; i < parsePtr->numTokens; i++) {
tokenPtr = &parsePtr->tokenPtr[i];
switch (tokenPtr->type) {
+ case TCL_TOKEN_EXPAND_WORD:
+ typeString = "expand";
+ break;
case TCL_TOKEN_WORD:
typeString = "word";
break;