summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2010-04-05 11:02:09 -0400
committerRich Hickey <richhickey@gmail.com>2010-04-05 11:02:09 -0400
commite6e39d5931fbdf3dfa68cd2d059b8e26ce45c965 (patch)
tree9dadb9dd9c327fe52ad8d9a33d4d5f5c58cf0007
parentba6cc3bde1a1ea9801b2133748a45f1277166368 (diff)
catch duplicate map keys for literals and hash- and array-map calls. Fixes #87
-rw-r--r--src/clj/clojure/core.clj12
-rw-r--r--src/jvm/clojure/lang/PersistentArrayMap.java11
-rw-r--r--src/jvm/clojure/lang/PersistentHashMap.java25
-rw-r--r--src/jvm/clojure/lang/RT.java4
4 files changed, 37 insertions, 15 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index b4d4a31e..b5909407 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -283,7 +283,7 @@
Returns a new hash map with supplied mappings."
([] {})
([& keyvals]
- (. clojure.lang.PersistentHashMap (create keyvals))))
+ (. clojure.lang.PersistentHashMap (createWithCheck keyvals))))
(defn hash-set
"Returns a new hash set with supplied keys."
@@ -2971,7 +2971,7 @@
(defn array-map
"Constructs an array-map."
([] (. clojure.lang.PersistentArrayMap EMPTY))
- ([& keyvals] (new clojure.lang.PersistentArrayMap (to-array keyvals))))
+ ([& keyvals] (clojure.lang.PersistentArrayMap/createWithCheck (to-array keyvals))))
(defn nthnext
"Returns the nth next of coll, (seq coll) when n is 0."
@@ -2984,7 +2984,7 @@
;redefine let and loop with destructuring
(defn destructure [bindings]
- (let [bmap (apply array-map bindings)
+ (let [bents (partition 2 bindings)
pb (fn pb [bvec b v]
(let [pvec
(fn [bvec b val]
@@ -3035,10 +3035,10 @@
(vector? b) (pvec bvec b v)
(map? b) (pmap bvec b v)
:else (throw (new Exception (str "Unsupported binding form: " b))))))
- process-entry (fn [bvec b] (pb bvec (key b) (val b)))]
- (if (every? symbol? (keys bmap))
+ process-entry (fn [bvec b] (pb bvec (first b) (second b)))]
+ (if (every? symbol? (map first bents))
bindings
- (reduce process-entry [] bmap))))
+ (reduce process-entry [] bents))))
(defmacro let
"Evaluates the exprs in a lexical context in which the symbols in
diff --git a/src/jvm/clojure/lang/PersistentArrayMap.java b/src/jvm/clojure/lang/PersistentArrayMap.java
index 8f63e5f8..7965c92e 100644
--- a/src/jvm/clojure/lang/PersistentArrayMap.java
+++ b/src/jvm/clojure/lang/PersistentArrayMap.java
@@ -60,6 +60,17 @@ IPersistentMap createHT(Object[] init){
return PersistentHashMap.create(meta(), init);
}
+static public PersistentArrayMap createWithCheck(Object[] init){
+ for(int i=0;i< init.length;i += 2)
+ {
+ for(int j=i+2;j<init.length;j += 2)
+ {
+ if(equalKey(init[i],init[j]))
+ throw new IllegalArgumentException("Duplicate key: " + init[i]);
+ }
+ }
+ return new PersistentArrayMap(init);
+}
/**
* This ctor captures/aliases the passed array, so do not modify later
*
diff --git a/src/jvm/clojure/lang/PersistentHashMap.java b/src/jvm/clojure/lang/PersistentHashMap.java
index 4fae3f30..6f956a7f 100644
--- a/src/jvm/clojure/lang/PersistentHashMap.java
+++ b/src/jvm/clojure/lang/PersistentHashMap.java
@@ -58,15 +58,13 @@ public static PersistentHashMap create(Object... init){
return (PersistentHashMap) ret.persistent();
}
-public static PersistentHashMap create(List init){
+public static PersistentHashMap createWithCheck(Object... init){
ITransientMap ret = EMPTY.asTransient();
- for(Iterator i = init.iterator(); i.hasNext();)
+ for(int i = 0; i < init.length; i += 2)
{
- Object key = i.next();
- if(!i.hasNext())
- throw new IllegalArgumentException(String.format("No value supplied for key: %s", key));
- Object val = i.next();
- ret = ret.assoc(key, val);
+ ret = ret.assoc(init[i], init[i + 1]);
+ if(ret.count() != i/2 + 1)
+ throw new IllegalArgumentException("Duplicate key: " + init[i]);
}
return (PersistentHashMap) ret.persistent();
}
@@ -82,6 +80,19 @@ static public PersistentHashMap create(ISeq items){
return (PersistentHashMap) ret.persistent();
}
+static public PersistentHashMap createWithCheck(ISeq items){
+ ITransientMap ret = EMPTY.asTransient();
+ for(int i=0; items != null; items = items.next().next(), ++i)
+ {
+ if(items.next() == null)
+ throw new IllegalArgumentException(String.format("No value supplied for key: %s", items.first()));
+ ret = ret.assoc(items.first(), RT.second(items));
+ if(ret.count() != i + 1)
+ throw new IllegalArgumentException("Duplicate key: " + items.first());
+ }
+ return (PersistentHashMap) ret.persistent();
+}
+
/*
* @param init {key1,val1,key2,val2,...}
*/
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 22b57cf7..13afd8fd 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -1018,8 +1018,8 @@ static public IPersistentMap map(Object... init){
if(init == null)
return PersistentArrayMap.EMPTY;
else if(init.length <= PersistentArrayMap.HASHTABLE_THRESHOLD)
- return new PersistentArrayMap(init);
- return PersistentHashMap.create(init);
+ return PersistentArrayMap.createWithCheck(init);
+ return PersistentHashMap.createWithCheck(init);
}
static public IPersistentSet set(Object... init){