aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jim-array.c5
-rw-r--r--jim.c77
-rw-r--r--jim.h3
-rw-r--r--stdlib.tcl35
-rw-r--r--tests/dict.test158
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);
diff --git a/jim.c b/jim.c
index dbe6d89..b431e42 100644
--- a/jim.c
+++ b/jim.c
@@ -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 ...?");
diff --git a/jim.h b/jim.h
index 6fb1a3e..320a200 100644
--- a/jim.h
+++ b/jim.h
@@ -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,
diff --git a/stdlib.tcl b/stdlib.tcl
index fe406ed..84e9120 100644
--- a/stdlib.tcl
+++ b/stdlib.tcl
@@ -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