diff options
-rw-r--r-- | jim-array.c | 5 | ||||
-rw-r--r-- | jim.c | 77 | ||||
-rw-r--r-- | jim.h | 3 | ||||
-rw-r--r-- | stdlib.tcl | 35 | ||||
-rw-r--r-- | tests/dict.test | 158 |
5 files changed, 275 insertions, 3 deletions
diff --git a/jim-array.c b/jim-array.c index ef459a0..5ba024e 100644 --- a/jim-array.c +++ b/jim-array.c @@ -175,7 +175,10 @@ static int array_cmd_size(Jim_Interp *interp, int argc, Jim_Obj *const *argv) /* Not found means zero length */ objPtr = Jim_GetVariable(interp, argv[0], JIM_NONE); if (objPtr) { - len = Jim_ListLength(interp, objPtr) / 2; + len = Jim_DictSize(interp, objPtr); + if (len < 0) { + return JIM_ERR; + } } Jim_SetResultInt(interp, len); @@ -9032,6 +9032,24 @@ int Jim_EvalObjVector(Jim_Interp *interp, int objc, Jim_Obj *const *objv) return JimEvalObjVector(interp, objc, objv, NULL, 0); } +/** + * Invokes 'prefix' as a command with the objv array as arguments. + */ +int Jim_EvalObjPrefix(Jim_Interp *interp, const char *prefix, int objc, Jim_Obj *const *objv) +{ + int i; + int ret; + Jim_Obj **nargv = Jim_Alloc((objc + 1) * sizeof(*nargv)); + + nargv[0] = Jim_NewStringObj(interp, prefix, -1); + for (i = 0; i < objc; i++) { + nargv[i + 1] = objv[i]; + } + ret = Jim_EvalObjVector(interp, objc + 1, nargv); + Jim_Free(nargv); + return ret; +} + /* Interpolate the given tokens into a unique Jim_Obj returned by reference * via *objPtrPtr. This function is only called by Jim_EvalObj(). * The returned object has refcount = 0. */ @@ -12577,17 +12595,25 @@ int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj) return JIM_OK; } +int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr) +{ + if (SetDictFromAny(interp, objPtr) != JIM_OK) { + return -1; + } + return ((Jim_HashTable *)objPtr->internalRep.ptr)->used; +} + /* [dict] */ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; int option; const char *options[] = { - "create", "get", "set", "unset", "exists", "keys", NULL + "create", "get", "set", "unset", "exists", "keys", "merge", "size", "with", NULL }; enum { - OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST, OPT_KEYS + OPT_CREATE, OPT_GET, OPT_SET, OPT_UNSET, OPT_EXIST, OPT_KEYS, OPT_MERGE, OPT_SIZE, OPT_WITH, }; if (argc < 2) { @@ -12601,6 +12627,10 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg switch (option) { case OPT_GET: + if (argc < 3) { + Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?"); + return JIM_ERR; + } if (Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG) != JIM_OK) { return JIM_ERR; @@ -12616,6 +12646,10 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1]); case OPT_EXIST: + if (argc < 3) { + Jim_WrongNumArgs(interp, 2, argv, "varName ?key ...?"); + return JIM_ERR; + } Jim_SetResultBool(interp, Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_ERRMSG) == JIM_OK); return JIM_OK; @@ -12634,6 +12668,45 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg } return Jim_DictKeys(interp, argv[2], argc == 4 ? argv[3] : NULL); + case OPT_SIZE: { + int size; + + if (argc != 3) { + Jim_WrongNumArgs(interp, 2, argv, "dictVar"); + return JIM_ERR; + } + + size = Jim_DictSize(interp, argv[2]); + if (size < 0) { + return JIM_ERR; + } + Jim_SetResultInt(interp, size); + return JIM_OK; + } + + case OPT_MERGE: + if (argc == 2) { + return JIM_OK; + } + else if (argv[2]->typePtr != &dictObjType && SetDictFromAny(interp, argv[2]) != JIM_OK) { + return JIM_ERR; + } + else { + return Jim_EvalObjPrefix(interp, "dict merge", argc - 2, argv + 2); + } + + case OPT_WITH: + if (argc < 4) { + Jim_WrongNumArgs(interp, 2, argv, "dictVar ?key ...? script"); + return JIM_ERR; + } + else if (Jim_GetVariable(interp, argv[2], JIM_ERRMSG) == NULL) { + return JIM_ERR; + } + else { + return Jim_EvalObjPrefix(interp, "dict with", argc - 2, argv + 2); + } + case OPT_CREATE: if (argc % 2) { Jim_WrongNumArgs(interp, 2, argv, "?key value ...?"); @@ -623,6 +623,8 @@ JIM_EXPORT int Jim_EvalFile(Jim_Interp *interp, const char *filename); JIM_EXPORT int Jim_EvalObj (Jim_Interp *interp, Jim_Obj *scriptObjPtr); JIM_EXPORT int Jim_EvalObjVector (Jim_Interp *interp, int objc, Jim_Obj *const *objv); +JIM_EXPORT int Jim_EvalObjPrefix(Jim_Interp *interp, const char *prefix, + int objc, Jim_Obj *const *objv); JIM_EXPORT int Jim_SubstObj (Jim_Interp *interp, Jim_Obj *substObjPtr, Jim_Obj **resObjPtrPtr, int flags); @@ -791,6 +793,7 @@ JIM_EXPORT int Jim_DictPairs(Jim_Interp *interp, JIM_EXPORT int Jim_DictAddElement(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *keyObjPtr, Jim_Obj *valueObjPtr); JIM_EXPORT int Jim_DictKeys(Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj *patternObj); +JIM_EXPORT int Jim_DictSize(Jim_Interp *interp, Jim_Obj *objPtr); /* return code object */ JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr, @@ -113,3 +113,38 @@ proc {info nameofexecutable} {} { } return "" } + +# Script-based implementation of 'dict with' +proc {dict with} {dictVar args script} { + upvar $dictVar dict + set keys {} + foreach {n v} [dict get $dict {*}$args] { + upvar $n var_$n + set var_$n $v + lappend keys $n + } + catch {uplevel 1 $script} msg opts + if {[info exists dict] && [dict exists $dict {*}$args]} { + foreach n $keys { + if {[info exists var_$n]} { + dict set dict {*}$args $n [set var_$n] + } else { + dict unset dict {*}$args $n + } + } + } + return {*}$opts $msg +} + +# Script-based implementation of 'dict merge' +# This won't get called in the trivial case of no args +proc {dict merge} {dict args} { + foreach d $args { + # Check for a valid dict + dict size $d + foreach {k v} $d { + dict set dict $k $v + } + } + return $dict +} diff --git a/tests/dict.test b/tests/dict.test index f68db1e..dc36b39 100644 --- a/tests/dict.test +++ b/tests/dict.test @@ -40,4 +40,162 @@ test dict-2.3 "Modify dict via reference - one line" references { dict get [getref $dref] car } {toyota} +# Sort a dictionary in key order - return a list +proc dictsort {dict} { + set result {} + foreach k [lsort [dict keys $dict]] { + lappend result $k [dict get $dict $k] + } + return $result +} + +set a [dict create a 1 b 2] +set b [dict create b 3 c 4] +test dict-3.1 {Merge} { + dict merge +} {} +test dict-3.2 {Merge} { + dictsort [dict merge $a] +} {a 1 b 2} +test dict-3.3 {Merge} { + dictsort [dict merge $b] +} {b 3 c 4} +test dict-3.4 {Merge} { + dictsort [dict merge $a $b] +} {a 1 b 3 c 4} +test dict-3.5 {Merge} { + dictsort [dict merge $b $a] +} {a 1 b 2 c 4} +test dict-3.6 {Merge} { + dictsort [dict merge $b $a {a 5}] +} {a 5 b 2 c 4} +test dict-3.7 {Merge} { + dictsort [dict merge {a 5} $b $a] +} {a 1 b 2 c 4} +test dict-3.8 {Merge} { + catch {dict merge 1 $b $a} +} 1 +test dict-3.9 {Merge} { + catch {dict merge $b 1 $a} +} 1 +test dict-3.10 {Merge} { + catch {dict merge $b $a 1} +} 1 +test dict-3.11 {Merge} { + catch {dict merge 1} +} 1 + +test dict-4.1 {Dict size} { + dict size {a b} +} 1 +test dict-4.2 {Dict size} { + dict size {a b c d} +} 2 + +test dict-5.1 {Dict with} { + proc a {} { + set x [dict create a b c d] + dict with x { + set a B + unset c + } + set x + } + dictsort [a] +} {a B} +test dict-5.2 {Dict with} { + proc a {} { + set x [dict create a b c d] + dict with x { + set a B + unset c + } + set x + } + dictsort [a] +} {a B} + +test dict-22.1 {dict with command} { + list [catch {dict with} msg] $msg +} {1 {wrong # args: should be "dict with dictVar ?key ...? script"}} +test dict-22.2 {dict with command} { + list [catch {dict with v} msg] $msg +} {1 {wrong # args: should be "dict with dictVar ?key ...? script"}} +test dict-22.3 {dict with command} { + unset -nocomplain v + list [catch {dict with v {error "in body"}} msg] $msg +} {1 {can't read "v": no such variable}} +test dict-22.4 {dict with command} { + set a {b c d e} + unset -nocomplain b d + set result [list [info exist b] [info exist d]] + dict with a { + lappend result [info exist b] [info exist d] $b $d + } + set result +} {0 0 1 1 c e} +test dict-22.5 {dict with command} { + set a {b c d e} + dict with a { + lassign "$b $d" d b + } + dictsort $a +} {b e d c} +test dict-22.6 {dict with command} { + set a {b c d e} + dict with a { + unset b + # This *won't* go into the dict... + set f g + } + set a +} {d e} +test dict-22.7 {dict with command} { + set a {b c d e} + dict with a { + dict unset a b + } + dictsort $a +} {b c d e} +test dict-22.8 {dict with command} { + set a [dict create b c] + dict with a { + set b $a + } + set a +} {b {b c}} +test dict-22.9 {dict with command} { + set a {b {c d}} + dict with a b { + set c $c$c + } + set a +} {b {c dd}} +test dict-22.10 {dict with command: result handling tricky case} { + set a {b {c d}} + foreach i {0 1} { + if {$i} break + dict with a b { + set a {} + # We're checking to see if we lose this break + break + } + } + list $i $a +} {0 {}} +test dict-22.11 {dict with command: no recursive structures [Bug 1786481]} { + set foo {t {t {t {inner 1}}}} + dict with foo { + dict with t { + dict with t { + dict with t { + incr inner + } + } + } + } + string range [append foo OK] end-1 end +} OK + + testreport |