diff --git a/compiler/kernel-primitives.rkt b/compiler/kernel-primitives.rkt index 472cd09..d207226 100644 --- a/compiler/kernel-primitives.rkt +++ b/compiler/kernel-primitives.rkt @@ -93,7 +93,8 @@ 'make-hash 'make-hasheqv 'make-hasheq - + 'hash-ref + 'hash-set! )) (define-predicate KernelPrimitiveName? KernelPrimitiveName) diff --git a/js-assembler/runtime-src/baselib-boxes.js b/js-assembler/runtime-src/baselib-boxes.js index 023ea0b..7f495ab 100644 --- a/js-assembler/runtime-src/baselib-boxes.js +++ b/js-assembler/runtime-src/baselib-boxes.js @@ -59,6 +59,15 @@ return ((other instanceof Box) && baselib.equality.equals(this.val, other.val, aUnionFind)); }; + + Box.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("Box"); + k = baselib.hashes.hashMix(k); + k += baselib.hashes.getEqualHashCode(this.val, depth); + k = baselib.hashes.hashMix(k); + return k; + }; + var makeBox = function(x) { return new Box(x, true); diff --git a/js-assembler/runtime-src/baselib-bytes.js b/js-assembler/runtime-src/baselib-bytes.js index 507e0dd..a8500c2 100644 --- a/js-assembler/runtime-src/baselib-bytes.js +++ b/js-assembler/runtime-src/baselib-bytes.js @@ -60,6 +60,16 @@ return true; }; + Bytes.prototype.hashCode = function(depth) { + var i; + var k = baselib.hashes.getEqualHashCode('Bytes'); + for (i = 0; i < n; i++) { + k += this.bytes[i]; + k = baselib.hashes.hashMix(k); + } + return k; + }; + Bytes.prototype.toString = function(cache) { var ret = [], i; diff --git a/js-assembler/runtime-src/baselib-chars.js b/js-assembler/runtime-src/baselib-chars.js index de6bf90..d3a999d 100644 --- a/js-assembler/runtime-src/baselib-chars.js +++ b/js-assembler/runtime-src/baselib-chars.js @@ -67,7 +67,12 @@ return other instanceof Char && this.val == other.val; }; - + Char.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode('Char'); + k += this.val.charCodeAt(0); + k = baselib.hashes.hashMix(k); + return k; + }; exports.Char = Char; diff --git a/js-assembler/runtime-src/baselib-hashes.js b/js-assembler/runtime-src/baselib-hashes.js index 9f5a354..aa59f33 100644 --- a/js-assembler/runtime-src/baselib-hashes.js +++ b/js-assembler/runtime-src/baselib-hashes.js @@ -61,7 +61,7 @@ return new WhalesongHashtable( "hash", function (x) { - return baselib.format.toWrittenString(x); + return getEqualHashCode(x); }, function (x, y) { return baselib.equality.equals(x, y, new baselib.UnionFind()); @@ -72,7 +72,7 @@ return new WhalesongHashtable( "hasheqv", function (x) { - return baselib.format.toWrittenString(x); + return getEqHashCode(x); }, baselib.equality.eqv); }; @@ -134,7 +134,14 @@ return true; }; - + WhalesongHashtable.prototype.hashCode = function(depth) { + var k = getEqualHashCode(this.type); + var keys = this.hash.keys(), i; + for (i = 0; i < keys.length; i++) { + k += hashMix(getEqualHashCode(this.hash.get(keys[i]), depth)); + } + return hashMix(k); + }; @@ -159,9 +166,63 @@ }; + + + + + + + // Arbitrary magic number. We have to cut off the hashing at some point. + var MAX_HASH_DEPTH = 128; + + // Returns a JavaScript number. + var getEqualHashCode = function (x, depth) { + var i, t, k = 0; + if (depth === undefined) { depth = 0; } + + if (depth > MAX_HASH_DEPTH) { return 0; } + + if (baselib.numbers.isNumber(x)) { + return hashMix(baselib.numbers.toFixnum(x)); + } + + if (baselib.strings.isString(x)) { + t = x.toString(); + for (i = 0; i < t.length; i++) { + k += t.charCodeAt(i); + k = hashMix(k); + } + return k; + } + + if (x === undefined || x === null) { + return 1; + } + + if (typeof (x) === 'object' && typeof (y) === 'object' && + typeof(x.hashCode) === 'function') { + return x.hashCode(depth + 1); + } + return 0; + }; + + + // Does some weird math on k. Grabbed from Racket's implementation of hashes. + // References to: http://www.burtleburtle.net/bob/hash/doobs.html + var hashMix = function(k) { + k += (k << 10); + k ^= (k >> 6); + return k; + }; + + + ////////////////////////////////////////////////////////////////////// exports.getEqHashCode = getEqHashCode; + exports.getEqualHashCode = getEqualHashCode; + exports.hashMix = hashMix; + exports.makeEqHashCode = makeEqHashCode; exports.makeLowLevelEqHash = makeLowLevelEqHash; diff --git a/js-assembler/runtime-src/baselib-keywords.js b/js-assembler/runtime-src/baselib-keywords.js index 36068b3..0e70644 100644 --- a/js-assembler/runtime-src/baselib-keywords.js +++ b/js-assembler/runtime-src/baselib-keywords.js @@ -27,6 +27,13 @@ return other instanceof Keyword && this.val === other.val; }; + + Keyword.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("Keyword"); + k += baselib.hashes.getEqualHashCode(this.val, depth); + k = baselib.hashes.hashMix(k); + return k; + }; Keyword.prototype.toString = function (cache) { diff --git a/js-assembler/runtime-src/baselib-lists.js b/js-assembler/runtime-src/baselib-lists.js index c18e571..b6ae8fd 100644 --- a/js-assembler/runtime-src/baselib-lists.js +++ b/js-assembler/runtime-src/baselib-lists.js @@ -20,6 +20,11 @@ return other instanceof Empty; }; + Empty.prototype.hashCode = function(depth) { + return baselib.hashes.getEqualHashCode("empty"); + }; + + Empty.prototype.reverse = function () { return this; }; @@ -85,6 +90,16 @@ baselib.equality.equals(this.rest, other.rest, aUnionFind)); }; + Cons.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("Cons"); + k += baselib.hashes.getEqualHashCode(this.first); + k = baselib.hashes.hashMix(k); + k += baselib.hashes.getEqualHashCode(this.rest); + k = baselib.hashes.hashMix(k); + return k; + }; + + // Cons.append: (listof X) -> (listof X) Cons.prototype.append = function (b) { diff --git a/js-assembler/runtime-src/baselib-paths.js b/js-assembler/runtime-src/baselib-paths.js index 02ebfd8..231b6f0 100644 --- a/js-assembler/runtime-src/baselib-paths.js +++ b/js-assembler/runtime-src/baselib-paths.js @@ -16,6 +16,20 @@ return "#"; }; + + Path.prototype.equals = function(other, aUnionFind) { + return (other instanceof Path && + this.path === other.path); + }; + + Path.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("path"); + k += baselib.hashes.getEqualHashCode(this.path, depth); + k = baselib.hashes.hashMix(k); + return k; + }; + + ////////////////////////////////////////////////////////////////////// var makePath = function (p) { diff --git a/js-assembler/runtime-src/baselib-placeholders.js b/js-assembler/runtime-src/baselib-placeholders.js index 1ea581d..4c9fd52 100644 --- a/js-assembler/runtime-src/baselib-placeholders.js +++ b/js-assembler/runtime-src/baselib-placeholders.js @@ -43,6 +43,12 @@ baselib.equality.equals(this.val, other.val, aUnionFind)); }; + Placeholder.prototype.hashCode = function(depth) { + var k = baselib.hashes.hashCode("Placeholder"); + k += baselib.hashes.hashCode(this.val, depth); + return baselib.hashes.hashMix(k); + }; + var makePlaceholder = function(v) { return new Placeholder(v); diff --git a/js-assembler/runtime-src/baselib-primitives.js b/js-assembler/runtime-src/baselib-primitives.js index f24d611..e9ce002 100644 --- a/js-assembler/runtime-src/baselib-primitives.js +++ b/js-assembler/runtime-src/baselib-primitives.js @@ -61,6 +61,7 @@ // Exceptions and error handling. var raise = baselib.exceptions.raise; + var raiseContractError = baselib.exceptions.raiseContractError; var raiseArgumentTypeError = baselib.exceptions.raiseArgumentTypeError; var raiseArityMismatchError = baselib.exceptions.raiseArityMismatchError; @@ -2558,13 +2559,14 @@ } else { if (M.a === 2) { raiseContractError( - plt.baselib.format("hash-ref: no value found for key: ~e", + M, + baselib.format.format("hash-ref: no value found for key: ~e", [key])); } else { M.p = thunk; M.e.length -= M.a; M.a = 0; - baselib.functions.rawApply(); + baselib.functions.rawApply(M); } } }); diff --git a/js-assembler/runtime-src/baselib-strings.js b/js-assembler/runtime-src/baselib-strings.js index 23c3fbe..a22dbc5 100644 --- a/js-assembler/runtime-src/baselib-strings.js +++ b/js-assembler/runtime-src/baselib-strings.js @@ -107,6 +107,10 @@ return this.toString() === other.toString(); }; + Str.prototype.hashCode = function(depth) { + return baselib.hashes.getEqualHashCode(this.toString()); + }; + Str.prototype.set = function (i, c) { this.chars[i] = c; diff --git a/js-assembler/runtime-src/baselib-structs.js b/js-assembler/runtime-src/baselib-structs.js index cfa0455..29c89c8 100644 --- a/js-assembler/runtime-src/baselib-structs.js +++ b/js-assembler/runtime-src/baselib-structs.js @@ -60,6 +60,18 @@ return true; }; + Struct.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode(this.name); + var i; + k = baselib.hashes.hashMix(k); + for (i = 0; i < this._fields.length; i++) { + k += baselib.hashes.getEqualHashCode(this._fields[i]); + k = baselib.hashes.hashMix(k); + } + return k; + }; + + Struct.prototype.type = Struct; @@ -101,6 +113,13 @@ return this === other; }; + StructType.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("StructType"); + k = baselib.hashes.hashMix(k); + k += baselib.hashes.getEqualHashCode(this.name); + k = baselib.hashes.hashMix(k); + return k; + }; diff --git a/js-assembler/runtime-src/baselib-symbols.js b/js-assembler/runtime-src/baselib-symbols.js index 1c20530..41d6841 100644 --- a/js-assembler/runtime-src/baselib-symbols.js +++ b/js-assembler/runtime-src/baselib-symbols.js @@ -30,6 +30,14 @@ return other instanceof Symbol && this.val === other.val; }; + + Symbol.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("Symbol"); + k = baselib.hashes.hashMix(k); + k += baselib.hashes.getEqualHashCode(this.val); + k = baselib.hashes.hashMix(k); + return k; + }; Symbol.prototype.toString = function (cache) { diff --git a/js-assembler/runtime-src/baselib-vectors.js b/js-assembler/runtime-src/baselib-vectors.js index 11030f8..c9d600d 100644 --- a/js-assembler/runtime-src/baselib-vectors.js +++ b/js-assembler/runtime-src/baselib-vectors.js @@ -56,6 +56,17 @@ } }; + Vector.prototype.hashCode = function(depth) { + var k = baselib.hashes.getEqualHashCode("Vector"); + var i; + k = baselib.hashes.hashMix(k); + for (i = 0; i < this.elts.length; i++) { + k += baselib.hashes.getEqualHashCode(this.elts[i], depth); + k = baselib.hashes.hashMix(k); + } + return k; + }; + Vector.prototype.toList = function () { var ret = baselib.lists.EMPTY, i; for (i = this.length() - 1; i >= 0; i--) { diff --git a/lang/kernel.rkt b/lang/kernel.rkt index a79c723..2e751cd 100644 --- a/lang/kernel.rkt +++ b/lang/kernel.rkt @@ -153,7 +153,8 @@ make-hash make-hasheqv make-hasheq - + hash-ref + hash-set! ;; Kernel inlinable diff --git a/tests/more-tests/hash-code.rkt b/tests/more-tests/hash-code.rkt new file mode 100644 index 0000000..e5b4739 --- /dev/null +++ b/tests/more-tests/hash-code.rkt @@ -0,0 +1,14 @@ +#lang planet dyoo/whalesong/base + +;; boxes +;; bytes +;; chars +;; hashes +;; keywords +;; lists +;; paths +;; placeholders +;; strings +;; structs +;; symbols +;; vectors \ No newline at end of file diff --git a/tests/more-tests/hashes.rkt b/tests/more-tests/hashes.rkt index d2e4089..5a815ff 100644 --- a/tests/more-tests/hashes.rkt +++ b/tests/more-tests/hashes.rkt @@ -33,4 +33,32 @@ (make-hasheq '((1 . one) (2 . two) (3 . three) - (4 . four))) \ No newline at end of file + (4 . four))) + +(hash-ref (make-hash '((1 . one) + (2 . two) + (3 . three))) + 1) + +(hash-ref (make-hash '((1 . one) + (2 . two) + (3 . three))) + 4 + (lambda () 'not-found)) + + + +(define words '("this" "is" "a" "test" "that" "is" "only" "a" "test!")) +(define ht (make-hash)) +(for-each (lambda (w) + (hash-set! ht + w + (add1 (hash-ref ht w (lambda () 0))))) + words) +(hash-ref ht "this") +(hash-ref ht "is") +(hash-ref ht "a") +(hash-ref ht "test") +(hash-ref ht "that") +(hash-ref ht "only") +(hash-ref ht "test!") \ No newline at end of file