From 992b51fc822addcd91ae1ea44e0df3486e654c3d Mon Sep 17 00:00:00 2001 From: dkf Date: Thu, 22 Nov 2007 22:16:06 +0000 Subject: Rebuild [dict] as a full compiled ensemble. --- ChangeLog | 5 + generic/tclBasic.c | 4 +- generic/tclCompCmds.c | 976 +++++++++++++++++++++++++++++--------------------- generic/tclDictObj.c | 378 ++++++++++--------- generic/tclInt.h | 28 +- tests/dict.test | 6 +- 6 files changed, 785 insertions(+), 612 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4674f95..d4de9f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-11-22 Donal K. Fellows + + * generic/tclDictObj.c (Dict*Cmd,TclInitDictCmd): Rebuilt the [dict] + * generic/tclCompCmds.c (TclCompileDict*Cmd): command as an ensemble. + 2007-11-22 Donal K. Fellows * generic/tclCmdMZ.c (Tcl_StringObjCmd): Rewrote the [string] and diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 09eecc1..531dc42 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.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: tclBasic.c,v 1.281 2007/11/12 22:12:05 msofer Exp $ + * RCS: @(#) $Id: tclBasic.c,v 1.282 2007/11/22 22:16:07 dkf Exp $ */ #include "tclInt.h" @@ -140,7 +140,6 @@ static const CmdInfo builtInCmds[] = { {"catch", Tcl_CatchObjCmd, TclCompileCatchCmd, 1}, {"concat", Tcl_ConcatObjCmd, NULL, 1}, {"continue", Tcl_ContinueObjCmd, TclCompileContinueCmd, 1}, - {"dict", Tcl_DictObjCmd, TclCompileDictCmd, 1}, {"encoding", Tcl_EncodingObjCmd, NULL, 0}, {"error", Tcl_ErrorObjCmd, NULL, 1}, {"eval", Tcl_EvalObjCmd, NULL, 1}, @@ -670,6 +669,7 @@ Tcl_CreateInterp(void) NULL, NULL); } + TclInitDictCmd(interp); TclInitInfoCmd(interp); /* TIP #208 */ diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c index 2f203e74..2d616c5 100644 --- a/generic/tclCompCmds.c +++ b/generic/tclCompCmds.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclCompCmds.c,v 1.129 2007/11/21 23:30:39 dkf Exp $ + * RCS: @(#) $Id: tclCompCmds.c,v 1.130 2007/11/22 22:16:08 dkf Exp $ */ #include "tclInt.h" @@ -588,24 +588,43 @@ TclCompileContinueCmd( /* *---------------------------------------------------------------------- * - * TclCompileDictCmd -- + * TclCompileDict*Cmd -- * - * Procedure called to compile the "dict" command. + * Functions called to compile "dict" sucommands. * * Results: - * Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer + * All return TCL_OK for a successful compile, and TCL_ERROR to defer * evaluation to runtime. * * Side effects: - * Instructions are added to envPtr to execute the "dict" command at + * Instructions are added to envPtr to execute the "dict" subcommand at * runtime. * + * Notes: + * The following commands are in fairly common use and are possibly worth + * bytecoding: + * dict append + * dict create [*] + * dict exists [*] + * dict for + * dict get [*] + * dict incr + * dict keys [*] + * dict lappend + * dict set + * dict unset + * + * In practice, those that are pure-value operators (marked with [*]) can + * probably be left alone (except perhaps [dict get] which is very very + * common) and [dict update] should be considered instead (really big + * win!) + * *---------------------------------------------------------------------- */ int -TclCompileDictCmd( - Tcl_Interp *interp, /* Used for error reporting. */ +TclCompileDictSetCmd( + Tcl_Interp *interp, /* Used for looking up stuff. */ Tcl_Parse *parsePtr, /* Points to a parse structure for the command * created by Tcl_ParseCommand. */ Command *cmdPtr, /* Points to defintion of command being @@ -613,496 +632,633 @@ TclCompileDictCmd( CompileEnv *envPtr) /* Holds resulting instructions. */ { Tcl_Token *tokenPtr; - int numWords, size, i; - const char *cmd; + int numWords, i; Proc *procPtr = envPtr->procPtr; DefineLineInformation; /* TIP #280 */ + Tcl_Token *varTokenPtr; + int dictVarIndex, nameChars; + const char *name; /* * There must be at least one argument after the command. */ - if (parsePtr->numWords < 2) { + if (parsePtr->numWords < 4 || procPtr == NULL) { return TCL_ERROR; } - tokenPtr = TokenAfter(parsePtr->tokenPtr); - numWords = parsePtr->numWords-2; - if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + /* + * The dictionary variable must be a local scalar that is knowable at + * compile time; anything else exceeds the complexity of the opcode. So + * discover what the index is. + */ + + varTokenPtr = TokenAfter(parsePtr->tokenPtr); + if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + return TCL_ERROR; + } + name = varTokenPtr[1].start; + nameChars = varTokenPtr[1].size; + if (!TclIsLocalScalar(name, nameChars)) { return TCL_ERROR; } + dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); /* - * The following commands are in fairly common use and are possibly worth - * bytecoding: - * dict append - * dict create [*] - * dict exists [*] - * dict for - * dict get [*] - * dict incr - * dict keys [*] - * dict lappend - * dict set - * dict unset - * In practice, those that are pure-value operators (marked with [*]) can - * probably be left alone (except perhaps [dict get] which is very very - * common) and [dict update] should be considered instead (really big - * win!) - */ - - size = tokenPtr[1].size; - cmd = tokenPtr[1].start; - if (size==3 && strncmp(cmd, "set", 3)==0) { - Tcl_Token *varTokenPtr; - int dictVarIndex, nameChars; - const char *name; - - if (numWords < 3 || procPtr == NULL) { - return TCL_ERROR; - } - varTokenPtr = TokenAfter(tokenPtr); - tokenPtr = TokenAfter(varTokenPtr); - if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { - return TCL_ERROR; - } - name = varTokenPtr[1].start; - nameChars = varTokenPtr[1].size; - if (!TclIsLocalScalar(name, nameChars)) { - return TCL_ERROR; - } - dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); - for (i=1 ; i 3 || procPtr == NULL) { - return TCL_ERROR; - } - varTokenPtr = TokenAfter(tokenPtr); - keyTokenPtr = TokenAfter(varTokenPtr); - if (numWords == 3) { - const char *word; - int numBytes, code; - Tcl_Obj *intObj; - - incrTokenPtr = TokenAfter(keyTokenPtr); - if (incrTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { - return TCL_ERROR; - } - word = incrTokenPtr[1].start; - numBytes = incrTokenPtr[1].size; + tokenPtr = TokenAfter(varTokenPtr); + numWords = parsePtr->numWords-1; + for (i=1 ; itype != TCL_TOKEN_SIMPLE_WORD) { - return TCL_ERROR; - } - name = varTokenPtr[1].start; - nameChars = varTokenPtr[1].size; - if (!TclIsLocalScalar(name, nameChars)) { - return TCL_ERROR; - } - dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); - CompileWord(envPtr, keyTokenPtr, interp, 3); - TclEmitInstInt4( INST_DICT_INCR_IMM, incrAmount, envPtr); - TclEmitInt4( dictVarIndex, envPtr); - return TCL_OK; - } else if (size==3 && strncmp(cmd, "get", 3)==0) { - /* - * Only compile this because we need INST_DICT_GET anyway. - */ + /* + * Now emit the instruction to do the dict manipulation. + */ - if (numWords < 2) { - return TCL_ERROR; - } - for (i=0 ; icurrStackDepth; - DefineLineInformation; /* TIP #280 */ + TclEmitInstInt4( INST_DICT_SET, numWords-2, envPtr); + TclEmitInt4( dictVarIndex, envPtr); + return TCL_OK; +} + +int +TclCompileDictIncrCmd( + Tcl_Interp *interp, /* Used for looking up stuff. */ + Tcl_Parse *parsePtr, /* Points to a parse structure for the command + * created by Tcl_ParseCommand. */ + Command *cmdPtr, /* Points to defintion of command being + * compiled. */ + CompileEnv *envPtr) /* Holds resulting instructions. */ +{ + Proc *procPtr = envPtr->procPtr; + DefineLineInformation; /* TIP #280 */ + Tcl_Token *varTokenPtr, *keyTokenPtr; + int dictVarIndex, nameChars, incrAmount; + const char *name; + + /* + * There must be at least two arguments after the command. + */ + + if (parsePtr->numWords < 3 || parsePtr->numWords > 4 || procPtr == NULL) { + return TCL_ERROR; + } + varTokenPtr = TokenAfter(parsePtr->tokenPtr); + keyTokenPtr = TokenAfter(varTokenPtr); - if (numWords != 3 || procPtr == NULL) { + /* + * Parse the increment amount, if present. + */ + + if (parsePtr->numWords == 4) { + const char *word; + int numBytes, code; + Tcl_Token *incrTokenPtr; + Tcl_Obj *intObj; + + incrTokenPtr = TokenAfter(keyTokenPtr); + if (incrTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { return TCL_ERROR; } + word = incrTokenPtr[1].start; + numBytes = incrTokenPtr[1].size; - varsTokenPtr = TokenAfter(tokenPtr); - dictTokenPtr = TokenAfter(varsTokenPtr); - bodyTokenPtr = TokenAfter(dictTokenPtr); - if (varsTokenPtr->type != TCL_TOKEN_SIMPLE_WORD || - bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + intObj = Tcl_NewStringObj(word, numBytes); + Tcl_IncrRefCount(intObj); + code = TclGetIntFromObj(NULL, intObj, &incrAmount); + TclDecrRefCount(intObj); + if (code != TCL_OK) { return TCL_ERROR; } + } else { + incrAmount = 1; + } - /* - * Check we've got a pair of variables and that they are local - * variables. Then extract their indices in the LVT. - */ + /* + * The dictionary variable must be a local scalar that is knowable at + * compile time; anything else exceeds the complexity of the opcode. So + * discover what the index is. + */ - Tcl_DStringInit(&buffer); - Tcl_DStringAppend(&buffer, varsTokenPtr[1].start, - varsTokenPtr[1].size); - if (Tcl_SplitList(NULL, Tcl_DStringValue(&buffer), &numWords, - &argv) != TCL_OK) { - Tcl_DStringFree(&buffer); - return TCL_ERROR; - } + if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + return TCL_ERROR; + } + name = varTokenPtr[1].start; + nameChars = varTokenPtr[1].size; + if (!TclIsLocalScalar(name, nameChars)) { + return TCL_ERROR; + } + dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); + + /* + * Emit the key and the code to actually do the increment. + */ + + CompileWord(envPtr, keyTokenPtr, interp, 3); + TclEmitInstInt4( INST_DICT_INCR_IMM, incrAmount, envPtr); + TclEmitInt4( dictVarIndex, envPtr); + return TCL_OK; +} + +int +TclCompileDictGetCmd( + Tcl_Interp *interp, /* Used for looking up stuff. */ + Tcl_Parse *parsePtr, /* Points to a parse structure for the command + * created by Tcl_ParseCommand. */ + Command *cmdPtr, /* Points to defintion of command being + * compiled. */ + CompileEnv *envPtr) /* Holds resulting instructions. */ +{ + Tcl_Token *tokenPtr; + int numWords, i; + DefineLineInformation; /* TIP #280 */ + + /* + * There must be at least two arguments after the command (the single-arg + * case is legal, but too special and magic for us to deal with here). + */ + + if (parsePtr->numWords < 3) { + return TCL_ERROR; + } + tokenPtr = TokenAfter(parsePtr->tokenPtr); + numWords = parsePtr->numWords-1; + + /* + * Only compile this because we need INST_DICT_GET anyway. + */ + + for (i=0 ; iprocPtr; + DefineLineInformation; /* TIP #280 */ + Tcl_Token *varsTokenPtr, *dictTokenPtr, *bodyTokenPtr; + int keyVarIndex, valueVarIndex, nameChars, loopRange, catchRange; + int infoIndex, jumpDisplacement, bodyTargetOffset, emptyTargetOffset; + int numVars, endTargetOffset; + int savedStackDepth = envPtr->currStackDepth; /* is this necessary? */ + const char **argv; + Tcl_DString buffer; + + /* + * There must be at least three argument after the command. + */ + + if (parsePtr->numWords != 4 || procPtr == NULL) { + return TCL_ERROR; + } + + varsTokenPtr = TokenAfter(parsePtr->tokenPtr); + dictTokenPtr = TokenAfter(varsTokenPtr); + bodyTokenPtr = TokenAfter(dictTokenPtr); + if (varsTokenPtr->type != TCL_TOKEN_SIMPLE_WORD || + bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + return TCL_ERROR; + } + + /* + * Check we've got a pair of variables and that they are local variables. + * Then extract their indices in the LVT. + */ + + Tcl_DStringInit(&buffer); + Tcl_DStringAppend(&buffer, varsTokenPtr[1].start, varsTokenPtr[1].size); + if (Tcl_SplitList(NULL, Tcl_DStringValue(&buffer), &numVars, + &argv) != TCL_OK) { Tcl_DStringFree(&buffer); - if (numWords != 2) { - ckfree((char *) argv); - return TCL_ERROR; - } - nameChars = strlen(argv[0]); - if (!TclIsLocalScalar(argv[0], nameChars)) { - ckfree((char *) argv); - return TCL_ERROR; - } - keyVarIndex = TclFindCompiledLocal(argv[0], nameChars, 1, procPtr); - nameChars = strlen(argv[1]); - if (!TclIsLocalScalar(argv[1], nameChars)) { - ckfree((char *) argv); - return TCL_ERROR; - } - valueVarIndex = TclFindCompiledLocal(argv[1], nameChars, 1, procPtr); + return TCL_ERROR; + } + Tcl_DStringFree(&buffer); + if (numVars != 2) { ckfree((char *) argv); + return TCL_ERROR; + } - /* - * Allocate a temporary variable to store the iterator reference. The - * variable will contain a Tcl_DictSearch reference which will be - * allocated by INST_DICT_FIRST and disposed when the variable is - * unset (at which point it should also have been finished with). - */ + nameChars = strlen(argv[0]); + if (!TclIsLocalScalar(argv[0], nameChars)) { + ckfree((char *) argv); + return TCL_ERROR; + } + keyVarIndex = TclFindCompiledLocal(argv[0], nameChars, 1, procPtr); - infoIndex = TclFindCompiledLocal(NULL, 0, 1, procPtr); + nameChars = strlen(argv[1]); + if (!TclIsLocalScalar(argv[1], nameChars)) { + ckfree((char *) argv); + return TCL_ERROR; + } + valueVarIndex = TclFindCompiledLocal(argv[1], nameChars, 1, procPtr); + ckfree((char *) argv); - /* - * Preparation complete; issue instructions. Note that this code - * issues fixed-sized jumps. That simplifies things a lot! - * - * First up, get the dictionary and start the iteration. No catching - * of errors at this point. - */ + /* + * Allocate a temporary variable to store the iterator reference. The + * variable will contain a Tcl_DictSearch reference which will be + * allocated by INST_DICT_FIRST and disposed when the variable is unset + * (at which point it should also have been finished with). + */ - CompileWord(envPtr, dictTokenPtr, interp, 3); - TclEmitInstInt4( INST_DICT_FIRST, infoIndex, envPtr); - emptyTargetOffset = CurrentOffset(envPtr); - TclEmitInstInt4( INST_JUMP_TRUE4, 0, envPtr); + infoIndex = TclFindCompiledLocal(NULL, 0, 1, procPtr); - /* - * Now we catch errors from here on so that we can finalize the search - * started by Tcl_DictObjFirst above. - */ + /* + * Preparation complete; issue instructions. Note that this code issues + * fixed-sized jumps. That simplifies things a lot! + * + * First up, get the dictionary and start the iteration. No catching of + * errors at this point. + */ - catchRange = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE); - TclEmitInstInt4( INST_BEGIN_CATCH4, catchRange, envPtr); - ExceptionRangeStarts(envPtr, catchRange); + CompileWord(envPtr, dictTokenPtr, interp, 3); + TclEmitInstInt4( INST_DICT_FIRST, infoIndex, envPtr); + emptyTargetOffset = CurrentOffset(envPtr); + TclEmitInstInt4( INST_JUMP_TRUE4, 0, envPtr); - /* - * Inside the iteration, write the loop variables. - */ + /* + * Now we catch errors from here on so that we can finalize the search + * started by Tcl_DictObjFirst above. + */ - bodyTargetOffset = CurrentOffset(envPtr); - TclEmitInstInt4( INST_STORE_SCALAR4, keyVarIndex, envPtr); - TclEmitOpcode( INST_POP, envPtr); - TclEmitInstInt4( INST_STORE_SCALAR4, valueVarIndex, envPtr); - TclEmitOpcode( INST_POP, envPtr); + catchRange = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE); + TclEmitInstInt4( INST_BEGIN_CATCH4, catchRange, envPtr); + ExceptionRangeStarts(envPtr, catchRange); - /* - * Set up the loop exception targets. - */ + /* + * Inside the iteration, write the loop variables. + */ - loopRange = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE); - ExceptionRangeStarts(envPtr, loopRange); + bodyTargetOffset = CurrentOffset(envPtr); + TclEmitInstInt4( INST_STORE_SCALAR4, keyVarIndex, envPtr); + TclEmitOpcode( INST_POP, envPtr); + TclEmitInstInt4( INST_STORE_SCALAR4, valueVarIndex, envPtr); + TclEmitOpcode( INST_POP, envPtr); - /* - * Compile the loop body itself. It should be stack-neutral. - */ + /* + * Set up the loop exception targets. + */ - envPtr->line = mapPtr->loc[eclIndex].line[4]; - CompileBody(envPtr, bodyTokenPtr, interp); - envPtr->currStackDepth = savedStackDepth + 1; - TclEmitOpcode( INST_POP, envPtr); - envPtr->currStackDepth = savedStackDepth; + loopRange = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE); + ExceptionRangeStarts(envPtr, loopRange); - /* - * Both exception target ranges (error and loop) end here. - */ + /* + * Compile the loop body itself. It should be stack-neutral. + */ - ExceptionRangeEnds(envPtr, loopRange); - ExceptionRangeEnds(envPtr, catchRange); + envPtr->line = mapPtr->loc[eclIndex].line[4]; + CompileBody(envPtr, bodyTokenPtr, interp); + envPtr->currStackDepth = savedStackDepth + 1; + TclEmitOpcode( INST_POP, envPtr); + envPtr->currStackDepth = savedStackDepth; - /* - * Continue (or just normally process) by getting the next pair of - * items from the dictionary and jumping back to the code to write - * them into variables if there is another pair. - */ + /* + * Both exception target ranges (error and loop) end here. + */ - ExceptionRangeTarget(envPtr, loopRange, continueOffset); - TclEmitInstInt4( INST_DICT_NEXT, infoIndex, envPtr); - jumpDisplacement = bodyTargetOffset - CurrentOffset(envPtr); - TclEmitInstInt4( INST_JUMP_FALSE4, jumpDisplacement, envPtr); - TclEmitOpcode( INST_POP, envPtr); - TclEmitOpcode( INST_POP, envPtr); + ExceptionRangeEnds(envPtr, loopRange); + ExceptionRangeEnds(envPtr, catchRange); - /* - * Now do the final cleanup for the no-error case (this is where we - * break out of the loop to) by force-terminating the iteration (if - * not already terminated), ditching the exception info and jumping to - * the last instruction for this command. In theory, this could be - * done using the "finally" clause (next generated) but this is - * faster. - */ + /* + * Continue (or just normally process) by getting the next pair of items + * from the dictionary and jumping back to the code to write them into + * variables if there is another pair. + */ - ExceptionRangeTarget(envPtr, loopRange, breakOffset); - TclEmitInstInt4( INST_DICT_DONE, infoIndex, envPtr); - TclEmitOpcode( INST_END_CATCH, envPtr); - endTargetOffset = CurrentOffset(envPtr); - TclEmitInstInt4( INST_JUMP4, 0, envPtr); + ExceptionRangeTarget(envPtr, loopRange, continueOffset); + TclEmitInstInt4( INST_DICT_NEXT, infoIndex, envPtr); + jumpDisplacement = bodyTargetOffset - CurrentOffset(envPtr); + TclEmitInstInt4( INST_JUMP_FALSE4, jumpDisplacement, envPtr); + TclEmitOpcode( INST_POP, envPtr); + TclEmitOpcode( INST_POP, envPtr); - /* - * Error handler "finally" clause, which force-terminates the - * iteration and rethrows the error. - */ + /* + * Now do the final cleanup for the no-error case (this is where we break + * out of the loop to) by force-terminating the iteration (if not already + * terminated), ditching the exception info and jumping to the last + * instruction for this command. In theory, this could be done using the + * "finally" clause (next generated) but this is faster. + */ - ExceptionRangeTarget(envPtr, catchRange, catchOffset); - TclEmitOpcode( INST_PUSH_RETURN_OPTIONS, envPtr); - TclEmitOpcode( INST_PUSH_RESULT, envPtr); - TclEmitInstInt4( INST_DICT_DONE, infoIndex, envPtr); - TclEmitOpcode( INST_END_CATCH, envPtr); - TclEmitOpcode( INST_RETURN_STK, envPtr); + ExceptionRangeTarget(envPtr, loopRange, breakOffset); + TclEmitInstInt4( INST_DICT_DONE, infoIndex, envPtr); + TclEmitOpcode( INST_END_CATCH, envPtr); + endTargetOffset = CurrentOffset(envPtr); + TclEmitInstInt4( INST_JUMP4, 0, envPtr); - /* - * Otherwise we're done (the jump after the DICT_FIRST points here) - * and we need to pop the bogus key/value pair (pushed to keep stack - * calculations easy!) Note that we skip the END_CATCH. [Bug 1382528] - */ + /* + * Error handler "finally" clause, which force-terminates the iteration + * and rethrows the error. + */ + + ExceptionRangeTarget(envPtr, catchRange, catchOffset); + TclEmitOpcode( INST_PUSH_RETURN_OPTIONS, envPtr); + TclEmitOpcode( INST_PUSH_RESULT, envPtr); + TclEmitInstInt4( INST_DICT_DONE, infoIndex, envPtr); + TclEmitOpcode( INST_END_CATCH, envPtr); + TclEmitOpcode( INST_RETURN_STK, envPtr); + + /* + * Otherwise we're done (the jump after the DICT_FIRST points here) and we + * need to pop the bogus key/value pair (pushed to keep stack calculations + * easy!) Note that we skip the END_CATCH. [Bug 1382528] + */ + + jumpDisplacement = CurrentOffset(envPtr) - emptyTargetOffset; + TclUpdateInstInt4AtPc(INST_JUMP_TRUE4, jumpDisplacement, + envPtr->codeStart + emptyTargetOffset); + TclEmitOpcode( INST_POP, envPtr); + TclEmitOpcode( INST_POP, envPtr); + TclEmitInstInt4( INST_DICT_DONE, infoIndex, envPtr); + + /* + * Final stage of the command (normal case) is that we push an empty + * object. This is done last to promote peephole optimization when it's + * dropped immediately. + */ + + jumpDisplacement = CurrentOffset(envPtr) - endTargetOffset; + TclUpdateInstInt4AtPc(INST_JUMP4, jumpDisplacement, + envPtr->codeStart + endTargetOffset); + PushLiteral(envPtr, "", 0); + return TCL_OK; +} + +int +TclCompileDictUpdateCmd( + Tcl_Interp *interp, /* Used for looking up stuff. */ + Tcl_Parse *parsePtr, /* Points to a parse structure for the command + * created by Tcl_ParseCommand. */ + Command *cmdPtr, /* Points to defintion of command being + * compiled. */ + CompileEnv *envPtr) /* Holds resulting instructions. */ +{ + Proc *procPtr = envPtr->procPtr; + DefineLineInformation; /* TIP #280 */ + const char *name; + int i, nameChars, dictIndex, numVars, range, infoIndex; + Tcl_Token **keyTokenPtrs, *dictVarTokenPtr, *bodyTokenPtr, *tokenPtr; + DictUpdateInfo *duiPtr; + JumpFixup jumpFixup; + + /* + * There must be at least one argument after the command. + */ + + if (parsePtr->numWords < 5 || procPtr == NULL) { + return TCL_ERROR; + } + + /* + * Parse the command. Expect the following: + * dict update ? ...? + */ + + if ((parsePtr->numWords - 1) & 1) { + return TCL_ERROR; + } + numVars = (parsePtr->numWords - 3) / 2; + + /* + * The dictionary variable must be a local scalar that is knowable at + * compile time; anything else exceeds the complexity of the opcode. So + * discover what the index is. + */ + + dictVarTokenPtr = TokenAfter(parsePtr->tokenPtr); + if (dictVarTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + return TCL_ERROR; + } + name = dictVarTokenPtr[1].start; + nameChars = dictVarTokenPtr[1].size; + if (!TclIsLocalScalar(name, nameChars)) { + return TCL_ERROR; + } + dictIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); + + /* + * Assemble the instruction metadata. This is complex enough that it is + * represented as auxData; it holds an ordered list of variable indices + * that are to be used. + */ - jumpDisplacement = CurrentOffset(envPtr) - emptyTargetOffset; - TclUpdateInstInt4AtPc(INST_JUMP_TRUE4, jumpDisplacement, - envPtr->codeStart + emptyTargetOffset); - TclEmitOpcode( INST_POP, envPtr); - TclEmitOpcode( INST_POP, envPtr); - TclEmitInstInt4( INST_DICT_DONE, infoIndex, envPtr); + duiPtr = (DictUpdateInfo *) + ckalloc(sizeof(DictUpdateInfo) + sizeof(int) * (numVars - 1)); + duiPtr->length = numVars; + keyTokenPtrs = (Tcl_Token **) TclStackAlloc(interp, + sizeof(Tcl_Token *) * numVars); + tokenPtr = TokenAfter(dictVarTokenPtr); + for (i=0 ; icodeStart + endTargetOffset); - PushLiteral(envPtr, "", 0); - return TCL_OK; - } else if (size==6 && strncmp(cmd, "update", 6)==0) { - const char *name; - int nameChars, dictIndex, numVars, range, infoIndex; - Tcl_Token **keyTokenPtrs, *dictVarTokenPtr, *bodyTokenPtr; - DictUpdateInfo *duiPtr; - JumpFixup jumpFixup; + keyTokenPtrs[i] = tokenPtr; /* - * Parse the command. Expect the following: - * dict update ? ...? + * Variables first need to be checked for sanity. */ - if (numWords < 4 || numWords & 1 || procPtr == NULL) { - return TCL_ERROR; - } - numVars = numWords/2 - 1; - dictVarTokenPtr = TokenAfter(tokenPtr); - if (dictVarTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + tokenPtr = TokenAfter(tokenPtr); + if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + ckfree((char *) duiPtr); + TclStackFree(interp, keyTokenPtrs); return TCL_ERROR; } - name = dictVarTokenPtr[1].start; - nameChars = dictVarTokenPtr[1].size; + name = tokenPtr[1].start; + nameChars = tokenPtr[1].size; if (!TclIsLocalScalar(name, nameChars)) { - return TCL_ERROR; - } - dictIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); - - duiPtr = (DictUpdateInfo *) - ckalloc(sizeof(DictUpdateInfo) + sizeof(int) * (numVars - 1)); - duiPtr->length = numVars; - keyTokenPtrs = (Tcl_Token **) TclStackAlloc(interp, - sizeof(Tcl_Token *) * numVars); - tokenPtr = TokenAfter(dictVarTokenPtr); - for (i=0 ; itype != TCL_TOKEN_SIMPLE_WORD) { - ckfree((char *) duiPtr); - TclStackFree(interp, keyTokenPtrs); - return TCL_ERROR; - } - name = tokenPtr[1].start; - nameChars = tokenPtr[1].size; - if (!TclIsLocalScalar(name, nameChars)) { - ckfree((char *) duiPtr); - TclStackFree(interp, keyTokenPtrs); - return TCL_ERROR; - } - duiPtr->varIndices[i] = - TclFindCompiledLocal(name, nameChars, 1, procPtr); - tokenPtr = TokenAfter(tokenPtr); - } - if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { ckfree((char *) duiPtr); TclStackFree(interp, keyTokenPtrs); return TCL_ERROR; } - bodyTokenPtr = tokenPtr; /* - * The list of variables to bind is stored in auxiliary data so that - * it can't be snagged by literal sharing and forced to shimmer - * dangerously. + * Stash the index in the auxiliary data. */ - infoIndex = TclCreateAuxData(duiPtr, &tclDictUpdateInfoType, envPtr); + duiPtr->varIndices[i] = + TclFindCompiledLocal(name, nameChars, 1, procPtr); + tokenPtr = TokenAfter(tokenPtr); + } + if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + ckfree((char *) duiPtr); + TclStackFree(interp, keyTokenPtrs); + return TCL_ERROR; + } + bodyTokenPtr = tokenPtr; - for (i=0 ; i 100 || procPtr == NULL) { - return TCL_ERROR; - } - varTokenPtr = TokenAfter(tokenPtr); - tokenPtr = TokenAfter(varTokenPtr); - if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { - return TCL_ERROR; - } - name = varTokenPtr[1].start; - nameChars = varTokenPtr[1].size; - if (!TclIsLocalScalar(name, nameChars)) { - return TCL_ERROR; - } - dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); - for (i=1 ; i 3) { - TclEmitInstInt1( INST_CONCAT1, numWords-2, envPtr); - } - TclEmitInstInt4( INST_DICT_APPEND, dictVarIndex, envPtr); - return TCL_OK; - } else if (size==7 && strncmp(cmd, "lappend", 7) == 0) { - Tcl_Token *varTokenPtr, *keyTokenPtr, *valueTokenPtr; - int dictVarIndex, nameChars; - const char *name; + if (TclFixupForwardJumpToHere(envPtr, &jumpFixup, 127)) { + Tcl_Panic("TclCompileDictCmd(update): bad jump distance %d", + CurrentOffset(envPtr) - jumpFixup.codeOffset); + } + TclStackFree(interp, keyTokenPtrs); + return TCL_OK; +} - if (numWords != 3 || procPtr == NULL) { - return TCL_ERROR; - } - varTokenPtr = TokenAfter(tokenPtr); - keyTokenPtr = TokenAfter(varTokenPtr); - valueTokenPtr = TokenAfter(keyTokenPtr); - if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { - return TCL_ERROR; - } - name = varTokenPtr[1].start; - nameChars = varTokenPtr[1].size; - if (!TclIsLocalScalar(name, nameChars)) { - return TCL_ERROR; - } - dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); - CompileWord(envPtr, keyTokenPtr, interp, 3); - CompileWord(envPtr, valueTokenPtr, interp, 4); - TclEmitInstInt4( INST_DICT_LAPPEND, dictVarIndex, envPtr); - return TCL_OK; +int +TclCompileDictAppendCmd( + Tcl_Interp *interp, /* Used for looking up stuff. */ + Tcl_Parse *parsePtr, /* Points to a parse structure for the command + * created by Tcl_ParseCommand. */ + Command *cmdPtr, /* Points to defintion of command being + * compiled. */ + CompileEnv *envPtr) /* Holds resulting instructions. */ +{ + Proc *procPtr = envPtr->procPtr; + DefineLineInformation; /* TIP #280 */ + Tcl_Token *tokenPtr, *varTokenPtr; + int numWords, i, dictVarIndex, nameChars; + const char *name; + + /* + * There must be at least two argument after the command. + */ + + if (parsePtr->numWords < 3) { + return TCL_ERROR; } + tokenPtr = TokenAfter(parsePtr->tokenPtr); + numWords = parsePtr->numWords-1; /* - * Something we do not know how to compile. + * Arbirary safe limit; anyone exceeding it should stop worrying about + * speed quite so much. ;-) */ - return TCL_ERROR; + if (parsePtr->numWords > 100 || procPtr == NULL) { + return TCL_ERROR; + } + + varTokenPtr = TokenAfter(tokenPtr); + if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + return TCL_ERROR; + } + name = varTokenPtr[1].start; + nameChars = varTokenPtr[1].size; + if (!TclIsLocalScalar(name, nameChars)) { + return TCL_ERROR; + } + dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); + + tokenPtr = TokenAfter(varTokenPtr); + for (i=1 ; inumWords-1 ; i++) { + CompileWord(envPtr, tokenPtr, interp, i); + tokenPtr = TokenAfter(tokenPtr); + } + if (parsePtr->numWords > 3) { + TclEmitInstInt1( INST_CONCAT1, parsePtr->numWords-2, envPtr); + } + TclEmitInstInt4( INST_DICT_APPEND, dictVarIndex, envPtr); + return TCL_OK; +} + +int +TclCompileDictLappendCmd( + Tcl_Interp *interp, /* Used for looking up stuff. */ + Tcl_Parse *parsePtr, /* Points to a parse structure for the command + * created by Tcl_ParseCommand. */ + Command *cmdPtr, /* Points to defintion of command being + * compiled. */ + CompileEnv *envPtr) /* Holds resulting instructions. */ +{ + Proc *procPtr = envPtr->procPtr; + DefineLineInformation; /* TIP #280 */ + Tcl_Token *varTokenPtr, *keyTokenPtr, *valueTokenPtr; + int dictVarIndex, nameChars; + const char *name; + + /* + * There must be three arguments after the command. + */ + + if (parsePtr->numWords != 4 || procPtr == NULL) { + return TCL_ERROR; + } + + varTokenPtr = TokenAfter(parsePtr->tokenPtr); + keyTokenPtr = TokenAfter(varTokenPtr); + valueTokenPtr = TokenAfter(keyTokenPtr); + if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) { + return TCL_ERROR; + } + name = varTokenPtr[1].start; + nameChars = varTokenPtr[1].size; + if (!TclIsLocalScalar(name, nameChars)) { + return TCL_ERROR; + } + dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr); + CompileWord(envPtr, keyTokenPtr, interp, 3); + CompileWord(envPtr, valueTokenPtr, interp, 4); + TclEmitInstInt4( INST_DICT_LAPPEND, dictVarIndex, envPtr); + return TCL_OK; } /* diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index 734b57b..a37d701 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclDictObj.c,v 1.54 2007/11/22 16:39:58 dkf Exp $ + * RCS: @(#) $Id: tclDictObj.c,v 1.55 2007/11/22 22:16:08 dkf Exp $ */ #include "tclInt.h" @@ -80,25 +80,25 @@ static inline int DeleteChainEntry(struct Dict *dict, Tcl_Obj *keyPtr); */ static const EnsembleImplMap implementationMap[] = { - {"append", DictAppendCmd, NULL/*TclCompileDictAppendCmd*/}, - {"create", DictCreateCmd, NULL}, - {"exists", DictExistsCmd, NULL}, - {"filter", DictFilterCmd, NULL}, - {"for", DictForCmd, NULL/*TclCompileDictForCmd*/}, - {"get", DictGetCmd, NULL/*TclCompileDictGetCmd*/}, - {"incr", DictIncrCmd, NULL/*TclCompileDictIncrCmd*/}, - {"info", DictInfoCmd, NULL}, - {"keys", DictKeysCmd, NULL}, - {"lappend", DictLappendCmd, NULL/*TclCompileDictLappendCmd*/}, - {"merge", DictMergeCmd, NULL}, - {"remove", DictRemoveCmd, NULL}, - {"replace", DictReplaceCmd, NULL}, - {"set", DictSetCmd, NULL/*TclCompileDictSetCmd*/}, - {"size", DictSizeCmd, NULL}, - {"unset", DictUnsetCmd, NULL}, - {"update", DictUpdateCmd, NULL/*TclCompileDictUpdateCmd*/}, - {"values", DictValuesCmd, NULL}, - {"with", DictWithCmd, NULL}, + {"append", DictAppendCmd, TclCompileDictAppendCmd }, + {"create", DictCreateCmd, NULL }, + {"exists", DictExistsCmd, NULL }, + {"filter", DictFilterCmd, NULL }, + {"for", DictForCmd, TclCompileDictForCmd }, + {"get", DictGetCmd, TclCompileDictGetCmd }, + {"incr", DictIncrCmd, TclCompileDictIncrCmd }, + {"info", DictInfoCmd, NULL }, + {"keys", DictKeysCmd, NULL }, + {"lappend", DictLappendCmd, TclCompileDictLappendCmd }, + {"merge", DictMergeCmd, NULL }, + {"remove", DictRemoveCmd, NULL }, + {"replace", DictReplaceCmd, NULL }, + {"set", DictSetCmd, TclCompileDictSetCmd }, + {"size", DictSizeCmd, NULL }, + {"unset", DictUnsetCmd, NULL }, + {"update", DictUpdateCmd, TclCompileDictUpdateCmd }, + {"values", DictValuesCmd, NULL }, + {"with", DictWithCmd, NULL }, {NULL} }; @@ -1503,13 +1503,13 @@ DictCreateCmd( * easier.) */ - if (objc & 1) { - Tcl_WrongNumArgs(interp, 2, objv, "?key value ...?"); + if ((objc & 1) == 0) { + Tcl_WrongNumArgs(interp, 1, objv, "?key value ...?"); return TCL_ERROR; } dictObj = Tcl_NewDictObj(); - for (i=2 ; itypePtr != &tclDictType) { + if (SetDictFromAny(interp, targetObj) != TCL_OK) { + return TCL_ERROR; + } + } + + if (objc == 2) { /* - * Single argument, make sure it is a dictionary, but otherwise return - * it. + * Single argument, return it. */ - if (objv[2]->typePtr != &tclDictType) { - if (SetDictFromAny(interp, objv[2]) != TCL_OK) { - return TCL_ERROR; - } - } - Tcl_SetObjResult(interp, objv[2]); + Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } @@ -1771,12 +1776,11 @@ DictMergeCmd( * Normal behaviour: combining two (or more) dictionaries. */ - targetObj = objv[2]; if (Tcl_IsShared(targetObj)) { targetObj = Tcl_DuplicateObj(targetObj); allocatedDict = 1; } - for (i=3 ; itypePtr != &tclDictType) { - int result = SetDictFromAny(interp, objv[2]); + if (objv[1]->typePtr != &tclDictType) { + int result = SetDictFromAny(interp, objv[1]); if (result != TCL_OK) { return result; } } - if (objc == 4) { - pattern = TclGetString(objv[3]); + if (objc == 3) { + pattern = TclGetString(objv[2]); } listPtr = Tcl_NewListObj(0, NULL); if ((pattern != NULL) && TclMatchIsTrivial(pattern)) { Tcl_Obj *valuePtr = NULL; - Tcl_DictObjGet(interp, objv[2], objv[3], &valuePtr); + Tcl_DictObjGet(interp, objv[1], objv[2], &valuePtr); if (valuePtr != NULL) { - Tcl_ListObjAppendElement(NULL, listPtr, objv[3]); + Tcl_ListObjAppendElement(NULL, listPtr, objv[2]); } } else { Tcl_DictSearch search; @@ -1870,12 +1873,13 @@ DictKeysCmd( * can start the iteration process without checking for failures. */ - Tcl_DictObjFirst(NULL, objv[2], &search, &keyPtr, NULL, &done); + Tcl_DictObjFirst(NULL, objv[1], &search, &keyPtr, NULL, &done); for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) { if (!pattern || Tcl_StringMatch(TclGetString(keyPtr), pattern)) { Tcl_ListObjAppendElement(NULL, listPtr, keyPtr); } } + Tcl_DictObjDone(&search); } Tcl_SetObjResult(interp, listPtr); @@ -1909,20 +1913,22 @@ DictValuesCmd( { Tcl_Obj *valuePtr, *listPtr; Tcl_DictSearch search; - int result, done; - char *pattern = NULL; + int done; + char *pattern; - if (objc!=3 && objc!=4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary ?pattern?"); + if (objc!=2 && objc!=3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?"); return TCL_ERROR; } - result= Tcl_DictObjFirst(interp, objv[2], &search, NULL, &valuePtr, &done); - if (result != TCL_OK) { + if (Tcl_DictObjFirst(interp, objv[1], &search, NULL, &valuePtr, + &done) != TCL_OK) { return TCL_ERROR; } - if (objc == 4) { - pattern = TclGetString(objv[3]); + if (objc == 3) { + pattern = TclGetString(objv[2]); + } else { + pattern = NULL; } listPtr = Tcl_NewListObj(0, NULL); for (; !done ; Tcl_DictObjNext(&search, NULL, &valuePtr, &done)) { @@ -1934,6 +1940,7 @@ DictValuesCmd( Tcl_ListObjAppendElement(interp, listPtr, valuePtr); } } + Tcl_DictObjDone(&search); Tcl_SetObjResult(interp, listPtr); return TCL_OK; @@ -1966,11 +1973,11 @@ DictSizeCmd( { int result, size; - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary"); + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); return TCL_ERROR; } - result = Tcl_DictObjSize(interp, objv[2], &size); + result = Tcl_DictObjSize(interp, objv[1], &size); if (result == TCL_OK) { Tcl_SetObjResult(interp, Tcl_NewIntObj(size)); } @@ -2005,12 +2012,12 @@ DictExistsCmd( Tcl_Obj *dictPtr, *valuePtr; int result; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary key ?key ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary key ?key ...?"); return TCL_ERROR; } - dictPtr = TclTraceDictPath(interp, objv[2], objc-4, objv+3, + dictPtr = TclTraceDictPath(interp, objv[1], objc-3, objv+2, DICT_PATH_EXISTS); if (dictPtr == NULL) { return TCL_ERROR; @@ -2055,12 +2062,12 @@ DictInfoCmd( Tcl_Obj *dictPtr; Dict *dict; - if (objc != 3) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary"); + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary"); return TCL_ERROR; } - dictPtr = objv[2]; + dictPtr = objv[1]; if (dictPtr->typePtr != &tclDictType) { int result = SetDictFromAny(interp, dictPtr); if (result != TCL_OK) { @@ -2105,19 +2112,19 @@ DictIncrCmd( int code = TCL_OK; Tcl_Obj *dictPtr, *valuePtr = NULL; - if (objc < 4 || objc > 5) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?increment?"); + if (objc < 3 || objc > 4) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?increment?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { /* * Variable didn't yet exist. Create new dictionary value. */ dictPtr = Tcl_NewDictObj(); - } else if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { + } else if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) { /* * Variable contents are not a dict, report error. */ @@ -2141,21 +2148,21 @@ DictIncrCmd( * Key not in dictionary. Create new key with increment as value. */ - if (objc == 5) { + if (objc == 4) { /* * Verify increment is an integer. */ mp_int increment; - code = Tcl_GetBignumFromObj(interp, objv[4], &increment); + code = Tcl_GetBignumFromObj(interp, objv[3], &increment); if (code != TCL_OK) { Tcl_AddErrorInfo(interp, "\n (reading increment)"); } else { - Tcl_DictObjPut(interp, dictPtr, objv[3], objv[4]); + Tcl_DictObjPut(interp, dictPtr, objv[2], objv[3]); } } else { - Tcl_DictObjPut(interp, dictPtr, objv[3], Tcl_NewIntObj(1)); + Tcl_DictObjPut(interp, dictPtr, objv[2], Tcl_NewIntObj(1)); } } else { /* @@ -2164,12 +2171,13 @@ DictIncrCmd( if (Tcl_IsShared(valuePtr)) { valuePtr = Tcl_DuplicateObj(valuePtr); - Tcl_DictObjPut(interp, dictPtr, objv[3], valuePtr); + Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr); } - if (objc == 5) { - code = TclIncrObj(interp, valuePtr, objv[4]); + if (objc == 4) { + code = TclIncrObj(interp, valuePtr, objv[3]); } else { Tcl_Obj *incrPtr = Tcl_NewIntObj(1); + Tcl_IncrRefCount(incrPtr); code = TclIncrObj(interp, valuePtr, incrPtr); Tcl_DecrRefCount(incrPtr); @@ -2177,7 +2185,7 @@ DictIncrCmd( } if (code == TCL_OK) { Tcl_InvalidateStringRep(dictPtr); - valuePtr = Tcl_ObjSetVar2(interp, objv[2], NULL, + valuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); if (valuePtr == NULL) { code = TCL_ERROR; @@ -2218,12 +2226,12 @@ DictLappendCmd( Tcl_Obj *dictPtr, *valuePtr, *resultPtr; int i, allocatedDict = 0, allocatedValue = 0; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?value ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?value ...?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2232,7 +2240,7 @@ DictLappendCmd( dictPtr = Tcl_DuplicateObj(dictPtr); } - if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { + if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) { if (allocatedDict) { TclDecrRefCount(dictPtr); } @@ -2240,7 +2248,7 @@ DictLappendCmd( } if (valuePtr == NULL) { - valuePtr = Tcl_NewListObj(objc-4, objv+4); + valuePtr = Tcl_NewListObj(objc-3, objv+3); allocatedValue = 1; } else { if (Tcl_IsShared(valuePtr)) { @@ -2248,7 +2256,7 @@ DictLappendCmd( valuePtr = Tcl_DuplicateObj(valuePtr); } - for (i=4 ; ibytes != NULL) { Tcl_InvalidateStringRep(dictPtr); } - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); if (resultPtr == NULL) { return TCL_ERROR; @@ -2305,12 +2313,12 @@ DictAppendCmd( Tcl_Obj *dictPtr, *valuePtr, *resultPtr; int i, allocatedDict = 0; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?value ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?value ...?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2319,7 +2327,7 @@ DictAppendCmd( dictPtr = Tcl_DuplicateObj(dictPtr); } - if (Tcl_DictObjGet(interp, dictPtr, objv[3], &valuePtr) != TCL_OK) { + if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) { if (allocatedDict) { TclDecrRefCount(dictPtr); } @@ -2334,13 +2342,13 @@ DictAppendCmd( } } - for (i=4 ; icmdFramePtr, 4); + result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3); if (result == TCL_CONTINUE) { result = TCL_OK; } else if (result != TCL_OK) { @@ -2502,12 +2510,12 @@ DictSetCmd( Tcl_Obj *dictPtr, *resultPtr; int result, allocatedDict = 0; - if (objc < 5) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?key ...? value"); + if (objc < 4) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?key ...? value"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2516,7 +2524,7 @@ DictSetCmd( dictPtr = Tcl_DuplicateObj(dictPtr); } - result = Tcl_DictObjPutKeyList(interp, dictPtr, objc-4, objv+3, + result = Tcl_DictObjPutKeyList(interp, dictPtr, objc-3, objv+2, objv[objc-1]); if (result != TCL_OK) { if (allocatedDict) { @@ -2525,7 +2533,7 @@ DictSetCmd( return TCL_ERROR; } - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); if (resultPtr == NULL) { return TCL_ERROR; @@ -2562,12 +2570,12 @@ DictUnsetCmd( Tcl_Obj *dictPtr, *resultPtr; int result, allocatedDict = 0; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "varName key ?key ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key ?key ...?"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { allocatedDict = 1; dictPtr = Tcl_NewDictObj(); @@ -2576,7 +2584,7 @@ DictUnsetCmd( dictPtr = Tcl_DuplicateObj(dictPtr); } - result = Tcl_DictObjRemoveKeyList(interp, dictPtr, objc-3, objv+3); + result = Tcl_DictObjRemoveKeyList(interp, dictPtr, objc-2, objv+2); if (result != TCL_OK) { if (allocatedDict) { TclDecrRefCount(dictPtr); @@ -2584,7 +2592,7 @@ DictUnsetCmd( return TCL_ERROR; } - resultPtr = Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG); if (resultPtr == NULL) { return TCL_ERROR; @@ -2618,7 +2626,7 @@ DictFilterCmd( int objc, Tcl_Obj *const *objv) { - Interp* iPtr = (Interp*) interp; + Interp *iPtr = (Interp *) interp; static const char *filters[] = { "key", "script", "value", NULL }; @@ -2631,19 +2639,19 @@ DictFilterCmd( int index, varc, done, result, satisfied; char *pattern; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary filterType ..."); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary filterType ..."); return TCL_ERROR; } - if (Tcl_GetIndexFromObj(interp, objv[3], filters, "filterType", + if (Tcl_GetIndexFromObj(interp, objv[2], filters, "filterType", 0, &index) != TCL_OK) { return TCL_ERROR; } switch ((enum FilterTypes) index) { case FILTER_KEYS: - if (objc != 5) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary key globPattern"); + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary key globPattern"); return TCL_ERROR; } @@ -2651,11 +2659,11 @@ DictFilterCmd( * Create a dictionary whose keys all match a certain pattern. */ - if (Tcl_DictObjFirst(interp, objv[2], &search, + if (Tcl_DictObjFirst(interp, objv[1], &search, &keyObj, &valueObj, &done) != TCL_OK) { return TCL_ERROR; } - pattern = TclGetString(objv[4]); + pattern = TclGetString(objv[3]); resultObj = Tcl_NewDictObj(); if (TclMatchIsTrivial(pattern)) { /* @@ -2664,9 +2672,9 @@ DictFilterCmd( */ Tcl_DictObjDone(&search); - Tcl_DictObjGet(interp, objv[2], objv[4], &valueObj); + Tcl_DictObjGet(interp, objv[1], objv[3], &valueObj); if (valueObj != NULL) { - Tcl_DictObjPut(interp, resultObj, objv[4], valueObj); + Tcl_DictObjPut(interp, resultObj, objv[3], valueObj); } } else { while (!done) { @@ -2680,8 +2688,8 @@ DictFilterCmd( return TCL_OK; case FILTER_VALUES: - if (objc != 5) { - Tcl_WrongNumArgs(interp, 2, objv, "dictionary value globPattern"); + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary value globPattern"); return TCL_ERROR; } @@ -2689,11 +2697,11 @@ DictFilterCmd( * Create a dictionary whose values all match a certain pattern. */ - if (Tcl_DictObjFirst(interp, objv[2], &search, + if (Tcl_DictObjFirst(interp, objv[1], &search, &keyObj, &valueObj, &done) != TCL_OK) { return TCL_ERROR; } - pattern = TclGetString(objv[4]); + pattern = TclGetString(objv[3]); resultObj = Tcl_NewDictObj(); while (!done) { if (Tcl_StringMatch(TclGetString(valueObj), pattern)) { @@ -2705,8 +2713,8 @@ DictFilterCmd( return TCL_OK; case FILTER_SCRIPT: - if (objc != 6) { - Tcl_WrongNumArgs(interp, 2, objv, + if (objc != 5) { + Tcl_WrongNumArgs(interp, 1, objv, "dictionary script {keyVar valueVar} filterScript"); return TCL_ERROR; } @@ -2717,7 +2725,7 @@ DictFilterCmd( * copying from the "dict for" implementation has occurred! */ - if (TclListObjGetElements(interp, objv[4], &varc, &varv) != TCL_OK) { + if (TclListObjGetElements(interp, objv[3], &varc, &varv) != TCL_OK) { return TCL_ERROR; } if (varc != 2) { @@ -2727,7 +2735,7 @@ DictFilterCmd( } keyVarObj = varv[0]; valueVarObj = varv[1]; - scriptObj = objv[5]; + scriptObj = objv[4]; /* * Make sure that these objects (which we need throughout the body of @@ -2740,7 +2748,7 @@ DictFilterCmd( Tcl_IncrRefCount(valueVarObj); Tcl_IncrRefCount(scriptObj); - result = Tcl_DictObjFirst(interp, objv[2], + result = Tcl_DictObjFirst(interp, objv[1], &search, &keyObj, &valueObj, &done); if (result != TCL_OK) { TclDecrRefCount(keyVarObj); @@ -2779,7 +2787,7 @@ DictFilterCmd( * TIP #280. Make invoking context available to loop body. */ - result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 5); + result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 4); switch (result) { case TCL_OK: boolObj = Tcl_GetObjResult(interp); @@ -2878,17 +2886,18 @@ DictUpdateCmd( int objc, Tcl_Obj *const *objv) { + Interp *iPtr = (Interp *) interp; Tcl_Obj *dictPtr, *objPtr; int i, result, dummy; Tcl_InterpState state; - if (objc < 6 || objc & 1) { - Tcl_WrongNumArgs(interp, 2, objv, + if (objc < 5 || !(objc & 1)) { + Tcl_WrongNumArgs(interp, 1, objv, "varName key varName ?key varName ...? script"); return TCL_ERROR; } - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG); if (dictPtr == NULL) { return TCL_ERROR; } @@ -2896,7 +2905,7 @@ DictUpdateCmd( return TCL_ERROR; } Tcl_IncrRefCount(dictPtr); - for (i=3 ; i+2cmdFramePtr, objc-1); if (result == TCL_ERROR) { Tcl_AddErrorInfo(interp, "\n (body of \"dict update\")"); } @@ -2925,7 +2934,7 @@ DictUpdateCmd( * If the dictionary variable doesn't exist, drop everything silently. */ - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { return result; } @@ -2949,7 +2958,7 @@ DictUpdateCmd( * an instruction to remove the key. */ - for (i=3 ; i+2 4) { - dictPtr = TclTraceDictPath(interp, dictPtr, objc-4, objv+3, + if (objc > 3) { + dictPtr = TclTraceDictPath(interp, dictPtr, objc-3, objv+2, DICT_PATH_READ); if (dictPtr == NULL) { return TCL_ERROR; @@ -3071,7 +3080,7 @@ DictWithCmd( * If the dictionary variable doesn't exist, drop everything silently. */ - dictPtr = Tcl_ObjGetVar2(interp, objv[2], NULL, 0); + dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0); if (dictPtr == NULL) { TclDecrRefCount(keysPtr); return result; @@ -3093,7 +3102,7 @@ DictWithCmd( allocdict = 1; } - if (objc > 4) { + if (objc > 3) { /* * Want to get to the dictionary which we will update; need to do * prepare-for-update de-sharing along the path *but* avoid generating @@ -3103,7 +3112,7 @@ DictWithCmd( * perfectly efficient (but no memory should be leaked). */ - leafPtr = TclTraceDictPath(interp, dictPtr, objc-4, objv+3, + leafPtr = TclTraceDictPath(interp, dictPtr, objc-3, objv+2, DICT_PATH_EXISTS | DICT_PATH_UPDATE); if (leafPtr == NULL) { TclDecrRefCount(keysPtr); @@ -3151,7 +3160,7 @@ DictWithCmd( * rep. */ - if (objc > 4) { + if (objc > 3) { InvalidateDictChain(leafPtr); } @@ -3159,7 +3168,7 @@ DictWithCmd( * Write back the outermost dictionary to the variable. */ - if (Tcl_ObjSetVar2(interp, objv[2], NULL, dictPtr, + if (Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr, TCL_LEAVE_ERR_MSG) == NULL) { Tcl_DiscardInterpState(state); return TCL_ERROR; @@ -3170,39 +3179,26 @@ DictWithCmd( /* *---------------------------------------------------------------------- * - * Tcl_DictObjCmd -- + * TclInitDictCmd -- * - * This function is invoked to process the "dict" Tcl command. See the - * user documentation for details on what it does, and TIP#111 for the - * formal specification. + * This function is create the "dict" Tcl command. See the user + * documentation for details on what it does, and TIP#111 for the formal + * specification. * * Results: - * A standard Tcl result. + * A Tcl command handle. * * Side effects: - * See the user documentation. + * May advance compilation epoch. * *---------------------------------------------------------------------- */ -int -Tcl_DictObjCmd( - ClientData clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const *objv) +Tcl_Command +TclInitDictCmd( + Tcl_Interp *interp) { - int index; - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); - return TCL_ERROR; - } - if (Tcl_GetIndexFromObjStruct(interp, objv[1], &implementationMap[0].name, - sizeof(EnsembleImplMap), "subcommand", 0, &index) != TCL_OK) { - return TCL_ERROR; - } - return implementationMap[index].proc(clientData, interp, objc, objv); + return TclMakeEnsemble(interp, "dict", implementationMap); } /* diff --git a/generic/tclInt.h b/generic/tclInt.h index f726571..66197fb 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.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: tclInt.h,v 1.350 2007/11/21 14:30:34 dkf Exp $ + * RCS: @(#) $Id: tclInt.h,v 1.351 2007/11/22 22:16:08 dkf Exp $ */ #ifndef _TCLINT @@ -2704,7 +2704,7 @@ MODULE_SCOPE int TclChanPendingObjCmd( MODULE_SCOPE int TclChanTruncateObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -MODULE_SCOPE void TclClockInit(Tcl_Interp*); +MODULE_SCOPE void TclClockInit(Tcl_Interp *interp); MODULE_SCOPE int TclClockOldscanObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -2723,9 +2723,7 @@ MODULE_SCOPE Tcl_TimerToken TclCreateAbsoluteTimerHandler( MODULE_SCOPE int TclDefaultBgErrorHandlerObjCmd( ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -MODULE_SCOPE int Tcl_DictObjCmd(ClientData clientData, - Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[]); +MODULE_SCOPE Tcl_Command TclInitDictCmd(Tcl_Interp *interp); MODULE_SCOPE int Tcl_DisassembleObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -2953,7 +2951,25 @@ MODULE_SCOPE int TclCompileCatchCmd(Tcl_Interp *interp, MODULE_SCOPE int TclCompileContinueCmd(Tcl_Interp *interp, Tcl_Parse *parsePtr, Command *cmdPtr, struct CompileEnv *envPtr); -MODULE_SCOPE int TclCompileDictCmd(Tcl_Interp *interp, +MODULE_SCOPE int TclCompileDictAppendCmd(Tcl_Interp *interp, + Tcl_Parse *parsePtr, Command *cmdPtr, + struct CompileEnv *envPtr); +MODULE_SCOPE int TclCompileDictForCmd(Tcl_Interp *interp, + Tcl_Parse *parsePtr, Command *cmdPtr, + struct CompileEnv *envPtr); +MODULE_SCOPE int TclCompileDictGetCmd(Tcl_Interp *interp, + Tcl_Parse *parsePtr, Command *cmdPtr, + struct CompileEnv *envPtr); +MODULE_SCOPE int TclCompileDictIncrCmd(Tcl_Interp *interp, + Tcl_Parse *parsePtr, Command *cmdPtr, + struct CompileEnv *envPtr); +MODULE_SCOPE int TclCompileDictLappendCmd(Tcl_Interp *interp, + Tcl_Parse *parsePtr, Command *cmdPtr, + struct CompileEnv *envPtr); +MODULE_SCOPE int TclCompileDictSetCmd(Tcl_Interp *interp, + Tcl_Parse *parsePtr, Command *cmdPtr, + struct CompileEnv *envPtr); +MODULE_SCOPE int TclCompileDictUpdateCmd(Tcl_Interp *interp, Tcl_Parse *parsePtr, Command *cmdPtr, struct CompileEnv *envPtr); MODULE_SCOPE int TclCompileEnsemble(Tcl_Interp *interp, diff --git a/tests/dict.test b/tests/dict.test index 4d3485b..a296fce 100644 --- a/tests/dict.test +++ b/tests/dict.test @@ -9,7 +9,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: dict.test,v 1.21 2007/09/08 22:36:59 dkf Exp $ +# RCS: @(#) $Id: dict.test,v 1.22 2007/11/22 22:16:08 dkf Exp $ if {[lsearch [namespace children] ::tcltest] == -1} { package require tcltest 2 @@ -35,10 +35,10 @@ proc getOrder {dictVal args} { test dict-1.1 {dict command basic syntax} { list [catch {dict} msg] $msg -} {1 {wrong # args: should be "dict subcommand ?arg ...?"}} +} {1 {wrong # args: should be "dict subcommand ?argument ...?"}} test dict-1.2 {dict command basic syntax} { list [catch {dict ?} msg] $msg -} {1 {bad subcommand "?": must be append, create, exists, filter, for, get, incr, info, keys, lappend, merge, remove, replace, set, size, unset, update, values, or with}} +} {1 {unknown or ambiguous subcommand "?": must be append, create, exists, filter, for, get, incr, info, keys, lappend, merge, remove, replace, set, size, unset, update, values, or with}} test dict-2.1 {dict create command} { dict create -- cgit v0.12