diff options
author | dgp <dgp@users.sourceforge.net> | 2003-11-14 20:44:43 (GMT) |
---|---|---|
committer | dgp <dgp@users.sourceforge.net> | 2003-11-14 20:44:43 (GMT) |
commit | 17f540b256d78b8a6fc8bd9121a633dac6c23b19 (patch) | |
tree | 1abdc7a020d4095171e8cb7f16def9be025cb664 /generic | |
parent | f745c9aa31bbdf8f71589fa25d30ce50cad94652 (diff) | |
download | tcl-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.h | 3 | ||||
-rw-r--r-- | generic/tclBasic.c | 111 | ||||
-rw-r--r-- | generic/tclCompile.c | 117 | ||||
-rw-r--r-- | generic/tclCompile.h | 29 | ||||
-rw-r--r-- | generic/tclExecute.c | 126 | ||||
-rw-r--r-- | generic/tclParse.c | 42 | ||||
-rw-r--r-- | generic/tclTest.c | 5 |
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; |