From bcc791aa8efb4d2042e92e7280bcdae74f64582c Mon Sep 17 00:00:00 2001 From: mohayonao Date: Sat, 17 May 2014 05:38:20 +0900 Subject: [PATCH] implements SCDictionary - implements SCAssociation - build v0.0.32 --- build/scscript-classlib.js | 677 ++++++++++++++++-- build/scscript.js | 11 +- package.json | 2 +- .../classlib/Collections/ArrayedCollection.js | 10 +- src/sc/classlib/Collections/Association.js | 60 ++ .../classlib/Collections/Association_test.js | 95 +++ src/sc/classlib/Collections/Dictionary.js | 551 ++++++++++++-- .../classlib/Collections/Dictionary_test.js | 605 ++++++++++++++-- .../Collections/SequenceableCollection.js | 30 +- .../SequenceableCollection_test.js | 39 +- src/sc/classlib/Collections/Set.js | 28 +- src/sc/lang/klass/constructors.js | 4 +- src/sc/lang/klass/klass.js | 5 + src/sc/test/utils.js | 21 +- 14 files changed, 1910 insertions(+), 228 deletions(-) create mode 100644 src/sc/classlib/Collections/Association.js create mode 100644 src/sc/classlib/Collections/Association_test.js diff --git a/build/scscript-classlib.js b/build/scscript-classlib.js index 2a51fae..ce22e18 100644 --- a/build/scscript-classlib.js +++ b/build/scscript-classlib.js @@ -6585,9 +6585,33 @@ SCScript.install(function(sc) { }; // TODO: implements minNyquist - // TODO: implements sort - // TODO: implements sortBy - // TODO: implements sortMap + + spec.sort = fn(function($function) { + if ($function === $nil) { + $function = $SC.Function(function($a, $b) { + return $a ["<="] ($b); + }); + } + this._sort($function); + return this; + }, "function"); + + spec.sortBy = fn(function($key) { + return this.sort($SC.Function(function($a, $b) { + return $a.at($key) ["<="] ($b.at($key)); + })); + }, "key"); + + spec.sortMap = fn(function($function) { + return this.sort($SC.Function(function($a, $b) { + return $function.value($a) ["<="] ($function.value($b)); + })); + }, "function"); + + // spec._sort = function($function) { + // this.mergeSort($function); + // }; + // TODO: implements sortedMedian // TODO: implements median // TODO: implements quickSort @@ -7066,7 +7090,7 @@ SCScript.install(function(sc) { this._ThrowIfImmutable(); - if ($aCollection.isSequenceableCollection().valueOf()) { + if (BOOL($aCollection.isCollection())) { $aCollection.do($SC.Function(function($item) { $this._.push($this.__elem__($item)); })); @@ -7394,6 +7418,14 @@ SCScript.install(function(sc) { return $elem.asString().__str__(); }).join(", ") + " ]"); }; + + // istanbul ignore next + spec._sort = function($function) { + this._ThrowIfImmutable(); + this._.sort(function($a, $b) { + return BOOL($function.value($a, $b)) ? -1 : 1; + }); + }; }); sc.lang.klass.refine("RawArray", function(spec, utils) { @@ -7706,11 +7738,11 @@ SCScript.install(function(sc) { var iterator = sc.lang.iterator; function SCSet(args) { - this.__initializeWith__("Collection"); var n = 2; if (args && args[0]) { n = args[0].__int__(); } + this.__initializeWith__("Collection"); this.initSet($SC.Integer(Math.max(n, 2) * 2)); } @@ -7733,7 +7765,7 @@ SCScript.install(function(sc) { }; spec.array_ = function($value) { - this._array = $value; + this._array = $value || /* istanbul ignore next */ $nil; return this; }; @@ -7946,25 +7978,11 @@ SCScript.install(function(sc) { }; spec.grow = function() { - var $this = this; - var oldElements; - - oldElements = this._array._; - this._array = SCArray.newClear( - $SC.Integer(this._array.size().__int__() * 2) - ); - this._size = $int_0; - - oldElements.forEach(function($item) { - if ($item !== $nil) { - $this.noCheckAdd($item); - } - }); - }; - - spec.noCheckAdd = function($item) { - this._array.put(this.scanFor($item), $item); - this._size = this._size.__inc__(); + var array, i, imax; + array = this._array._; + for (i = array.length, imax = i * 2; i < imax; ++i) { + array[i] = $nil; + } }; // istanbul ignore next @@ -7993,16 +8011,90 @@ SCScript.install(function(sc) { }); +// src/sc/classlib/Collections/Association.js +SCScript.install(function(sc) { + + var $SC = sc.lang.$SC; + + function SCAssociation(args) { + this.__initializeWith__("Magnitude"); + this._key = args.shift() || $SC.Nil(); + this._value = args.shift() || $SC.Nil(); + } + + sc.lang.klass.define(SCAssociation, "Association : Magnitude", function(spec, utils) { + var $nil = utils.$nil; + var $false = utils.$false; + + spec.valueOf = function() { + return this._key.valueOf(); + }; + + spec.key = function() { + return this._key; + }; + + spec.key_ = function($value) { + this._key = $value || /* istanbul ignore next */ $nil; + return this; + }; + + spec.value = function() { + return this._value; + }; + + spec.value_ = function($value) { + this._value = $value || /* istanbul ignore next */ $nil; + return this; + }; + + spec["=="] = function($anAssociation) { + if ($anAssociation.key) { + return this._key ["=="] ($anAssociation.key()); + } + return $false; + }; + + // TODO: implements hash + + spec["<"] = function($anAssociation) { + return this._key ["<"] ($anAssociation.key()); + }; + + // TODO: implements printOn + // TODO: implements storeOn + // TODO: implements embedInStream + // TODO: implements transformEvent + + }); + +}); + // src/sc/classlib/Collections/Dictionary.js SCScript.install(function(sc) { - function SCDictionary() { - this.__initializeWith__("Set"); + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + + function SCDictionary(args) { + var n = 8; + if (args && args[0]) { + n = args[0].__int__(); + } + this.__initializeWith__("Set", [ $SC.Integer(n) ]); this._ = {}; } sc.lang.klass.define(SCDictionary, "Dictionary : Set", function(spec, utils) { - var $nil = utils.$nil; + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var $true = utils.$true; + var $false = utils.$false; + var $int_1 = utils.$int_1; + var SCSet = $SC("Set"); + var SCArray = $SC("Array"); + var SCAssociation = $SC("Association"); spec.valueOf = function() { var obj; @@ -8020,51 +8112,502 @@ SCScript.install(function(sc) { return obj; }; - // TODO: implements $newFrom - // TODO: implements at - // TODO: implements atFail - // TODO: implements matchAt - // TODO: implements trueAt - // TODO: implements add - // TODO: implements put - // TODO: implements putAll - // TODO: implements putPairs - // TODO: implements getPairs - // TODO: implements associationAt - // TODO: implements associationAtFail - // TODO: implements keys - // TODO: implements values - // TODO: implements includes - // TODO: implements includesKey - // TODO: implements removeAt - // TODO: implements removeAtFail - // TODO: implements remove - // TODO: implements removeFail - // TODO: implements keysValuesDo - // TODO: implements keysValuesChange - // TODO: implements do - // TODO: implements keysDo - // TODO: implements associationsDo - // TODO: implements pairsDo - // TODO: implements collect - // TODO: implements select - // TODO: implements reject - // TODO: implements invert - // TODO: implements merge + spec.$newFrom = fn(function($aCollection) { + var $newCollection; + + $newCollection = this.new($aCollection.size()); + $aCollection.keysValuesDo($SC.Function(function($k, $v) { + $newCollection.put($k, $v); + })); + + return $newCollection; + }, "aCollection"); + + spec.at = fn(function($key) { + return this._array.at(this.scanFor($key).__inc__()); + }, "key"); + + spec.atFail = fn(function($key, $function) { + var $val; + + $val = this.at($key); + if ($val === $nil) { + $val = $function.value(); + } + + return $val; + }, "key; function"); + + spec.matchAt = fn(function($key) { + var ret = null; + + this.keysValuesDo($SC.Function(function($k, $v) { + if (BOOL($k.matchItem($key))) { + ret = $v; + return 65535; + } + })); + + return ret || $nil; + }, "key"); + + spec.trueAt = fn(function($key) { + var $ret; + + $ret = this.at($key); + + return $ret !== $nil ? $ret : $false; + }, "key"); + + spec.add = fn(function($anAssociation) { + this.put($anAssociation.key(), $anAssociation.value()); + return this; + }, "anAssociation"); + + spec.put = fn(function($key, $value) { + var $array, $index; + + if ($value === $nil) { + this.removeAt($key); + } else { + $array = this._array; + $index = this.scanFor($key); + $array.put($index.__inc__(), $value); + if ($array.at($index) === $nil) { + $array.put($index, $key); + this._size = this._size.__inc__(); + if ($array.size().__inc__() < this._size.__inc__() * 4) { + this.grow(); + } + } + } + + return this; + }, "key; value"); + + spec.putAll = function() { + var $this = this; + var func; + + func = $SC.Function(function($key, $value) { + $this.put($key, $value); + }); + + slice.call(arguments).forEach(function($dict) { + $dict.keysValuesDo(func); + }, this); + + return this; + }; + + spec.putPairs = fn(function($args) { + var $this = this; + + $args.pairsDo($SC.Function(function($key, $val) { + $this.put($key, $val); + })); + + return this; + }, "args"); + + spec.getPairs = fn(function($args) { + var $this = this; + var $result; + + if ($args === $nil) { + $args = this.keys(); + } + + $result = $nil; + $args.do($SC.Function(function($key) { + var $val; + $val = $this.at($key); + if ($val !== $nil) { + $result = $result.add($key).add($val); + } + })); + + return $result; + }, "args"); + + spec.associationAt = fn(function($key) { + var $res; + var array, index; + + array = this._array._; + index = this.scanFor($key).__int__(); + + // istanbul ignore else + if (index >= 0) { + $res = SCAssociation.new(array[index], array[index + 1]); + } + + return $res || /* istanbul ignore next */ $nil; + }, "key"); + + spec.associationAtFail = fn(function($argkey, $function) { + var $index, $key; + + $index = this.scanFor($argkey); + $key = this._array.at($index); + + if ($key === $nil) { + return $function.value(); + } + + return SCAssociation.new($key, this._array.at($index.__inc__())); + }, "argKey; function"); + + spec.keys = fn(function($species) { + var $set; + + if ($species === $nil) { + $species = SCSet; + } + + $set = $species.new(this._size); + this.keysDo($SC.Function(function($key) { + $set.add($key); + })); + + return $set; + }, "species"); + + spec.values = function() { + var $list; + + $list = $SC("List").new(this._size); + this.do($SC.Function(function($value) { + $list.add($value); + })); + + return $list; + }; + + spec.includes = fn(function($item1) { + var $ret = null; + + this.do($SC.Function(function($item2) { + if (BOOL($item1 ["=="] ($item2))) { + $ret = $true; + return 65535; + } + })); + + return $ret || $false; + }, "item1"); + + spec.includesKey = fn(function($key) { + return this.at($key).notNil(); + }, "key"); + + spec.removeAt = fn(function($key) { + var $array; + var $val, $index, $atKeyIndex; + + $array = this._array; + $index = this.scanFor($key); + $atKeyIndex = $array.at($index); + if ($atKeyIndex === $nil) { + return $nil; + } + + $val = $array.at($index.__inc__()); + $array.put($index, $nil); + $array.put($index.__inc__(), $nil); + + this._size = this._size.__dec__(); + + // this.fixCollisionsFrom($index); + + return $val; + }, "key"); + + spec.removeAtFail = fn(function($key, $function) { + var $array; + var $val, $index, $atKeyIndex; + + $array = this._array; + $index = this.scanFor($key); + $atKeyIndex = $array.at($index); + + if ($atKeyIndex === $nil) { + return $function.value(); + } + + $val = $array.at($index.__inc__()); + $array.put($index, $nil); + $array.put($index.__inc__(), $nil); + + this._size = this._size.__dec__(); + + // this.fixCollisionsFrom($index); + + return $val; + }, "key; function"); + + spec.remove = function() { + throw new Error("shouldNotImplement"); + }; + + spec.removeFail = function() { + throw new Error("shouldNotImplement"); + }; + + spec.keysValuesDo = fn(function($function) { + this.keysValuesArrayDo(this._array, $function); + return this; + }, "function"); + + spec.keysValuesChange = fn(function($function) { + var $this = this; + + this.keysValuesDo($SC.Function(function($key, $value, $i) { + $this.put($key, $function.value($key, $value, $i)); + })); + + return this; + }, "function"); + + spec.do = fn(function($function) { + this.keysValuesDo($SC.Function(function($key, $value, $i) { + $function.value($value, $i); + })); + return this; + }, "function"); + + spec.keysDo = fn(function($function) { + this.keysValuesDo($SC.Function(function($key, $val, $i) { + $function.value($key, $i); + })); + return this; + }, "function"); + + spec.associationsDo = fn(function($function) { + this.keysValuesDo($SC.Function(function($key, $val, $i) { + var $assoc = SCAssociation.new($key, $val); + $function.value($assoc, $i); + })); + return this; + }, "function"); + + spec.pairsDo = fn(function($function) { + this.keysValuesArrayDo(this._array, $function); + return this; + }, "function"); + + spec.collect = fn(function($function) { + var $res; + + $res = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $elem) { + $res.put($key, $function.value($elem, $key)); + })); + + return $res; + }, "function"); + + spec.select = fn(function($function) { + var $res; + + $res = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $elem) { + if (BOOL($function.value($elem, $key))) { + $res.put($key, $elem); + } + })); + + return $res; + }, "function"); + + spec.reject = fn(function($function) { + var $res; + + $res = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $elem) { + if (!BOOL($function.value($elem, $key))) { + $res.put($key, $elem); + } + })); + + return $res; + }, "function"); + + spec.invert = function() { + var $dict; + + $dict = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $val) { + $dict.put($val, $key); + })); + + return $dict; + }; + + spec.merge = fn(function($that, $func, $fill) { + var $this = this; + var $commonKeys, $myKeys, $otherKeys; + var $res; + + $res = this.class().new(); + + $myKeys = this.keys(); + $otherKeys = $that.keys(); + + if (BOOL($myKeys ["=="] ($otherKeys))) { + $commonKeys = $myKeys; + } else { + $commonKeys = $myKeys.sect($otherKeys); + } + + $commonKeys.do($SC.Function(function($key) { + $res.put($key, $func.value($this.at($key), $that.at($key), $key)); + })); + + if (BOOL($fill)) { + $myKeys.difference($otherKeys).do($SC.Function(function($key) { + $res.put($key, $this.at($key)); + })); + $otherKeys.difference($myKeys).do($SC.Function(function($key) { + $res.put($key, $that.at($key)); + })); + } + + return $res; + }, "that; func; fill=true"); + // TODO: implements blend - // TODO: implements findKeyForValue - // TODO: implements sortedKeysValuesDo - // TODO: implements choose - // TODO: implements order - // TODO: implements powerset - // TODO: implements transformEvent + + spec.findKeyForValue = fn(function($argValue) { + var $ret = null; + + this.keysValuesArrayDo(this._array, $SC.Function(function($key, $val) { + if (BOOL($argValue ["=="] ($val))) { + $ret = $key; + return 65535; + } + })); + + return $ret || $nil; + }, "argValue"); + + spec.sortedKeysValuesDo = fn(function($function, $sortFunc) { + var $this = this; + var $keys; + + $keys = this.keys(SCArray); + $keys.sort($sortFunc); + + $keys.do($SC.Function(function($key, $i) { + $function.value($key, $this.at($key), $i); + })); + + return this; + }, "$function; $sortFunc"); + + spec.choose = function() { + var $array; + var $size, $index; + + if (BOOL(this.isEmpty())) { + return $nil; + } + + $array = this._array; + $size = $array.size() [">>"] ($int_1); + + do { + $index = $size.rand() ["<<"] ($int_1); + } while ($array.at($index) === $nil); + + return $array.at($index.__inc__()); + }; + + spec.order = fn(function($func) { + var $assoc; + + if (BOOL(this.isEmpty())) { + return $nil; + } + + $assoc = $nil; + this.keysValuesDo($SC.Function(function($key, $val) { + $assoc = $assoc.add($key ["->"] ($val)); + })); + + return $assoc.sort($func).collect($SC.Function(function($_) { + return $_.key(); + })); + }, "func"); + + spec.powerset = function() { + var $this = this; + var $keys, $class; + + $keys = this.keys().asArray().powerset(); + $class = this.class(); + + return $keys.collect($SC.Function(function($list) { + var $dict; + + $dict = $class.new(); + $list.do($SC.Function(function($key) { + $dict.put($key, $this.at($key)); + })); + + return $dict; + })); + }; + + spec.transformEvent = fn(function($event) { + return $event.putAll(this); + }, "event"); + // TODO: implements embedInStream // TODO: implements asSortedArray // TODO: implements asKeyValuePairs - // TODO: implements keysValuesArrayDo + + spec.keysValuesArrayDo = function($argArray, $function) { + var $key, $val; + var array, j, i, imax; + + array = this._array._; + for (i = j = 0, imax = array.length; i < imax; i += 2, ++j) { + $key = array[i]; + if ($key !== $nil) { + $val = $argArray.at($SC.Integer(i + 1)); + $function.value($key, $val, $SC.Integer(j)); + } + } + }; + // TODO: implements grow // TODO: implements fixCollisionsFrom - // TODO: implements scanFor + + // istanbul ignore next + spec.scanFor = function($argKey) { + var array, i, imax; + var $elem; + + array = this._array._; + imax = array.length; + + for (i = 0; i < imax; i += 2) { + $elem = array[i]; + if (BOOL($elem ["=="] ($argKey))) { + return $SC.Integer(i); + } + } + for (i = 0; i < imax; i += 2) { + $elem = array[i]; + if ($elem === $nil) { + return $SC.Integer(i); + } + } + + return $SC.Integer(-2); + }; + // TODO: implements storeItemsOn // TODO: implements printItemsOn }); diff --git a/build/scscript.js b/build/scscript.js index 378b6d2..6adfbbd 100644 --- a/build/scscript.js +++ b/build/scscript.js @@ -1,7 +1,7 @@ (function(global) { "use strict"; -var sc = { VERSION: "0.0.31" }; +var sc = { VERSION: "0.0.32" }; // src/sc/sc.js (function(sc) { @@ -822,6 +822,11 @@ var sc = { VERSION: "0.0.31" }; // basic classes function SCObject() { this._ = this; + Object.defineProperties(this, { + _immutable: { + value: false, writable: true + } + }); } function SCClass() { @@ -1112,10 +1117,10 @@ var sc = { VERSION: "0.0.31" }; return instance; }; - $SC.String = function(value, immutable) { + $SC.String = function(value) { var instance = new SCString(); instance._ = String(value).split("").map($SC.Char); - instance._immutable = !!immutable; + instance._immutable = true; return instance; }; diff --git a/package.json b/package.json index cae1bc1..fdc2424 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scscript", - "version": "0.0.31", + "version": "0.0.32", "author": "Nao Yonamine ", "homepage": "http://mohayonao.github.io/SCScript/", "bugs": "https://github.com/mohayonao/SCScript/issues", diff --git a/src/sc/classlib/Collections/ArrayedCollection.js b/src/sc/classlib/Collections/ArrayedCollection.js index 237e263..db20921 100644 --- a/src/sc/classlib/Collections/ArrayedCollection.js +++ b/src/sc/classlib/Collections/ArrayedCollection.js @@ -432,7 +432,7 @@ SCScript.install(function(sc) { this._ThrowIfImmutable(); - if ($aCollection.isSequenceableCollection().valueOf()) { + if (BOOL($aCollection.isCollection())) { $aCollection.do($SC.Function(function($item) { $this._.push($this.__elem__($item)); })); @@ -760,6 +760,14 @@ SCScript.install(function(sc) { return $elem.asString().__str__(); }).join(", ") + " ]"); }; + + // istanbul ignore next + spec._sort = function($function) { + this._ThrowIfImmutable(); + this._.sort(function($a, $b) { + return BOOL($function.value($a, $b)) ? -1 : 1; + }); + }; }); sc.lang.klass.refine("RawArray", function(spec, utils) { diff --git a/src/sc/classlib/Collections/Association.js b/src/sc/classlib/Collections/Association.js new file mode 100644 index 0000000..1e0feff --- /dev/null +++ b/src/sc/classlib/Collections/Association.js @@ -0,0 +1,60 @@ +SCScript.install(function(sc) { + "use strict"; + + require("../Math/Magnitude"); + + var $SC = sc.lang.$SC; + + function SCAssociation(args) { + this.__initializeWith__("Magnitude"); + this._key = args.shift() || $SC.Nil(); + this._value = args.shift() || $SC.Nil(); + } + + sc.lang.klass.define(SCAssociation, "Association : Magnitude", function(spec, utils) { + var $nil = utils.$nil; + var $false = utils.$false; + + spec.valueOf = function() { + return this._key.valueOf(); + }; + + spec.key = function() { + return this._key; + }; + + spec.key_ = function($value) { + this._key = $value || /* istanbul ignore next */ $nil; + return this; + }; + + spec.value = function() { + return this._value; + }; + + spec.value_ = function($value) { + this._value = $value || /* istanbul ignore next */ $nil; + return this; + }; + + spec["=="] = function($anAssociation) { + if ($anAssociation.key) { + return this._key ["=="] ($anAssociation.key()); + } + return $false; + }; + + // TODO: implements hash + + spec["<"] = function($anAssociation) { + return this._key ["<"] ($anAssociation.key()); + }; + + // TODO: implements printOn + // TODO: implements storeOn + // TODO: implements embedInStream + // TODO: implements transformEvent + + }); + +}); diff --git a/src/sc/classlib/Collections/Association_test.js b/src/sc/classlib/Collections/Association_test.js new file mode 100644 index 0000000..124a648 --- /dev/null +++ b/src/sc/classlib/Collections/Association_test.js @@ -0,0 +1,95 @@ +(function() { + "use strict"; + + require("./Association"); + + var $SC = sc.lang.$SC; + + describe("SCAssociation", function() { + var SCAssociation; + before(function() { + SCAssociation = $SC("Association"); + this.createInstance = function($key, $value) { + return SCAssociation.new($key, $value); + }; + }); + it("#valueOf", function() { + var instance, test; + + instance = this.createInstance($SC.Integer(1), $SC.Integer(2)); + + test = instance.valueOf(); + expect(test).to.be.a("JSNumber").that.equals(1); + }); + it("<>value", function() { + var instance, test; + var $value; + + $value = sc.test.object(); + + instance = this.createInstance(); + + test = instance.value_($value); + expect(test).to.equal(instance); + + test = instance.value(); + expect(test).to.equal($value); + }); + it("<>key", function() { + var instance, test; + var $key; + + $key = sc.test.object(); + + instance = this.createInstance(); + + test = instance.key_($key); + expect(test).to.equal(instance); + + test = instance.key(); + expect(test).to.equal($key); + }); + it("#==", function() { + var instance, test; + var $anAssociation; + + instance = this.createInstance($SC.Integer(1), $SC.Integer(2)); + + $anAssociation = $SC.Nil(); + test = instance ["=="] ($anAssociation); + expect(test).to.be.a("SCBoolean").that.is.false; + + $anAssociation = SCAssociation.new(); + test = instance ["=="] ($anAssociation); + expect(test).to.be.a("SCBoolean").that.is.false; + + $anAssociation = SCAssociation.new($SC.Integer(1), $SC.Integer(3)); + test = instance ["=="] ($anAssociation); + expect(test).to.be.a("SCBoolean").that.is.true; + }); + it.skip("#hash", function() { + }); + it("#<", function() { + var instance, test; + var $anAssociation; + + instance = this.createInstance($SC.Integer(1), $SC.Integer(2)); + + $anAssociation = SCAssociation.new($SC.Integer(2), $SC.Integer(3)); + test = instance ["<"] ($anAssociation); + expect(test).to.be.a("SCBoolean").that.is.true; + + $anAssociation = SCAssociation.new($SC.Integer(0), $SC.Integer(3)); + test = instance ["<"] ($anAssociation); + expect(test).to.be.a("SCBoolean").that.is.false; + }); + it.skip("#printOn", function() { + }); + it.skip("#storeOn", function() { + }); + it.skip("#embedInStream", function() { + }); + it.skip("#transformEvent", function() { + }); + }); +})(); diff --git a/src/sc/classlib/Collections/Dictionary.js b/src/sc/classlib/Collections/Dictionary.js index 784c065..6633282 100644 --- a/src/sc/classlib/Collections/Dictionary.js +++ b/src/sc/classlib/Collections/Dictionary.js @@ -1,15 +1,31 @@ SCScript.install(function(sc) { "use strict"; + require("./Association"); require("./Set"); - function SCDictionary() { - this.__initializeWith__("Set"); + var slice = [].slice; + var fn = sc.lang.fn; + var $SC = sc.lang.$SC; + + function SCDictionary(args) { + var n = 8; + if (args && args[0]) { + n = args[0].__int__(); + } + this.__initializeWith__("Set", [ $SC.Integer(n) ]); this._ = {}; } sc.lang.klass.define(SCDictionary, "Dictionary : Set", function(spec, utils) { - var $nil = utils.$nil; + var BOOL = utils.BOOL; + var $nil = utils.$nil; + var $true = utils.$true; + var $false = utils.$false; + var $int_1 = utils.$int_1; + var SCSet = $SC("Set"); + var SCArray = $SC("Array"); + var SCAssociation = $SC("Association"); spec.valueOf = function() { var obj; @@ -27,51 +43,502 @@ SCScript.install(function(sc) { return obj; }; - // TODO: implements $newFrom - // TODO: implements at - // TODO: implements atFail - // TODO: implements matchAt - // TODO: implements trueAt - // TODO: implements add - // TODO: implements put - // TODO: implements putAll - // TODO: implements putPairs - // TODO: implements getPairs - // TODO: implements associationAt - // TODO: implements associationAtFail - // TODO: implements keys - // TODO: implements values - // TODO: implements includes - // TODO: implements includesKey - // TODO: implements removeAt - // TODO: implements removeAtFail - // TODO: implements remove - // TODO: implements removeFail - // TODO: implements keysValuesDo - // TODO: implements keysValuesChange - // TODO: implements do - // TODO: implements keysDo - // TODO: implements associationsDo - // TODO: implements pairsDo - // TODO: implements collect - // TODO: implements select - // TODO: implements reject - // TODO: implements invert - // TODO: implements merge + spec.$newFrom = fn(function($aCollection) { + var $newCollection; + + $newCollection = this.new($aCollection.size()); + $aCollection.keysValuesDo($SC.Function(function($k, $v) { + $newCollection.put($k, $v); + })); + + return $newCollection; + }, "aCollection"); + + spec.at = fn(function($key) { + return this._array.at(this.scanFor($key).__inc__()); + }, "key"); + + spec.atFail = fn(function($key, $function) { + var $val; + + $val = this.at($key); + if ($val === $nil) { + $val = $function.value(); + } + + return $val; + }, "key; function"); + + spec.matchAt = fn(function($key) { + var ret = null; + + this.keysValuesDo($SC.Function(function($k, $v) { + if (BOOL($k.matchItem($key))) { + ret = $v; + return sc.C.LOOP_BREAK; + } + })); + + return ret || $nil; + }, "key"); + + spec.trueAt = fn(function($key) { + var $ret; + + $ret = this.at($key); + + return $ret !== $nil ? $ret : $false; + }, "key"); + + spec.add = fn(function($anAssociation) { + this.put($anAssociation.key(), $anAssociation.value()); + return this; + }, "anAssociation"); + + spec.put = fn(function($key, $value) { + var $array, $index; + + if ($value === $nil) { + this.removeAt($key); + } else { + $array = this._array; + $index = this.scanFor($key); + $array.put($index.__inc__(), $value); + if ($array.at($index) === $nil) { + $array.put($index, $key); + this._size = this._size.__inc__(); + if ($array.size().__inc__() < this._size.__inc__() * 4) { + this.grow(); + } + } + } + + return this; + }, "key; value"); + + spec.putAll = function() { + var $this = this; + var func; + + func = $SC.Function(function($key, $value) { + $this.put($key, $value); + }); + + slice.call(arguments).forEach(function($dict) { + $dict.keysValuesDo(func); + }, this); + + return this; + }; + + spec.putPairs = fn(function($args) { + var $this = this; + + $args.pairsDo($SC.Function(function($key, $val) { + $this.put($key, $val); + })); + + return this; + }, "args"); + + spec.getPairs = fn(function($args) { + var $this = this; + var $result; + + if ($args === $nil) { + $args = this.keys(); + } + + $result = $nil; + $args.do($SC.Function(function($key) { + var $val; + $val = $this.at($key); + if ($val !== $nil) { + $result = $result.add($key).add($val); + } + })); + + return $result; + }, "args"); + + spec.associationAt = fn(function($key) { + var $res; + var array, index; + + array = this._array._; + index = this.scanFor($key).__int__(); + + // istanbul ignore else + if (index >= 0) { + $res = SCAssociation.new(array[index], array[index + 1]); + } + + return $res || /* istanbul ignore next */ $nil; + }, "key"); + + spec.associationAtFail = fn(function($argkey, $function) { + var $index, $key; + + $index = this.scanFor($argkey); + $key = this._array.at($index); + + if ($key === $nil) { + return $function.value(); + } + + return SCAssociation.new($key, this._array.at($index.__inc__())); + }, "argKey; function"); + + spec.keys = fn(function($species) { + var $set; + + if ($species === $nil) { + $species = SCSet; + } + + $set = $species.new(this._size); + this.keysDo($SC.Function(function($key) { + $set.add($key); + })); + + return $set; + }, "species"); + + spec.values = function() { + var $list; + + $list = $SC("List").new(this._size); + this.do($SC.Function(function($value) { + $list.add($value); + })); + + return $list; + }; + + spec.includes = fn(function($item1) { + var $ret = null; + + this.do($SC.Function(function($item2) { + if (BOOL($item1 ["=="] ($item2))) { + $ret = $true; + return sc.C.LOOP_BREAK; + } + })); + + return $ret || $false; + }, "item1"); + + spec.includesKey = fn(function($key) { + return this.at($key).notNil(); + }, "key"); + + spec.removeAt = fn(function($key) { + var $array; + var $val, $index, $atKeyIndex; + + $array = this._array; + $index = this.scanFor($key); + $atKeyIndex = $array.at($index); + if ($atKeyIndex === $nil) { + return $nil; + } + + $val = $array.at($index.__inc__()); + $array.put($index, $nil); + $array.put($index.__inc__(), $nil); + + this._size = this._size.__dec__(); + + // this.fixCollisionsFrom($index); + + return $val; + }, "key"); + + spec.removeAtFail = fn(function($key, $function) { + var $array; + var $val, $index, $atKeyIndex; + + $array = this._array; + $index = this.scanFor($key); + $atKeyIndex = $array.at($index); + + if ($atKeyIndex === $nil) { + return $function.value(); + } + + $val = $array.at($index.__inc__()); + $array.put($index, $nil); + $array.put($index.__inc__(), $nil); + + this._size = this._size.__dec__(); + + // this.fixCollisionsFrom($index); + + return $val; + }, "key; function"); + + spec.remove = function() { + throw new Error("shouldNotImplement"); + }; + + spec.removeFail = function() { + throw new Error("shouldNotImplement"); + }; + + spec.keysValuesDo = fn(function($function) { + this.keysValuesArrayDo(this._array, $function); + return this; + }, "function"); + + spec.keysValuesChange = fn(function($function) { + var $this = this; + + this.keysValuesDo($SC.Function(function($key, $value, $i) { + $this.put($key, $function.value($key, $value, $i)); + })); + + return this; + }, "function"); + + spec.do = fn(function($function) { + this.keysValuesDo($SC.Function(function($key, $value, $i) { + $function.value($value, $i); + })); + return this; + }, "function"); + + spec.keysDo = fn(function($function) { + this.keysValuesDo($SC.Function(function($key, $val, $i) { + $function.value($key, $i); + })); + return this; + }, "function"); + + spec.associationsDo = fn(function($function) { + this.keysValuesDo($SC.Function(function($key, $val, $i) { + var $assoc = SCAssociation.new($key, $val); + $function.value($assoc, $i); + })); + return this; + }, "function"); + + spec.pairsDo = fn(function($function) { + this.keysValuesArrayDo(this._array, $function); + return this; + }, "function"); + + spec.collect = fn(function($function) { + var $res; + + $res = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $elem) { + $res.put($key, $function.value($elem, $key)); + })); + + return $res; + }, "function"); + + spec.select = fn(function($function) { + var $res; + + $res = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $elem) { + if (BOOL($function.value($elem, $key))) { + $res.put($key, $elem); + } + })); + + return $res; + }, "function"); + + spec.reject = fn(function($function) { + var $res; + + $res = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $elem) { + if (!BOOL($function.value($elem, $key))) { + $res.put($key, $elem); + } + })); + + return $res; + }, "function"); + + spec.invert = function() { + var $dict; + + $dict = this.class().new(this.size()); + this.keysValuesDo($SC.Function(function($key, $val) { + $dict.put($val, $key); + })); + + return $dict; + }; + + spec.merge = fn(function($that, $func, $fill) { + var $this = this; + var $commonKeys, $myKeys, $otherKeys; + var $res; + + $res = this.class().new(); + + $myKeys = this.keys(); + $otherKeys = $that.keys(); + + if (BOOL($myKeys ["=="] ($otherKeys))) { + $commonKeys = $myKeys; + } else { + $commonKeys = $myKeys.sect($otherKeys); + } + + $commonKeys.do($SC.Function(function($key) { + $res.put($key, $func.value($this.at($key), $that.at($key), $key)); + })); + + if (BOOL($fill)) { + $myKeys.difference($otherKeys).do($SC.Function(function($key) { + $res.put($key, $this.at($key)); + })); + $otherKeys.difference($myKeys).do($SC.Function(function($key) { + $res.put($key, $that.at($key)); + })); + } + + return $res; + }, "that; func; fill=true"); + // TODO: implements blend - // TODO: implements findKeyForValue - // TODO: implements sortedKeysValuesDo - // TODO: implements choose - // TODO: implements order - // TODO: implements powerset - // TODO: implements transformEvent + + spec.findKeyForValue = fn(function($argValue) { + var $ret = null; + + this.keysValuesArrayDo(this._array, $SC.Function(function($key, $val) { + if (BOOL($argValue ["=="] ($val))) { + $ret = $key; + return sc.C.LOOP_BREAK; + } + })); + + return $ret || $nil; + }, "argValue"); + + spec.sortedKeysValuesDo = fn(function($function, $sortFunc) { + var $this = this; + var $keys; + + $keys = this.keys(SCArray); + $keys.sort($sortFunc); + + $keys.do($SC.Function(function($key, $i) { + $function.value($key, $this.at($key), $i); + })); + + return this; + }, "$function; $sortFunc"); + + spec.choose = function() { + var $array; + var $size, $index; + + if (BOOL(this.isEmpty())) { + return $nil; + } + + $array = this._array; + $size = $array.size() [">>"] ($int_1); + + do { + $index = $size.rand() ["<<"] ($int_1); + } while ($array.at($index) === $nil); + + return $array.at($index.__inc__()); + }; + + spec.order = fn(function($func) { + var $assoc; + + if (BOOL(this.isEmpty())) { + return $nil; + } + + $assoc = $nil; + this.keysValuesDo($SC.Function(function($key, $val) { + $assoc = $assoc.add($key ["->"] ($val)); + })); + + return $assoc.sort($func).collect($SC.Function(function($_) { + return $_.key(); + })); + }, "func"); + + spec.powerset = function() { + var $this = this; + var $keys, $class; + + $keys = this.keys().asArray().powerset(); + $class = this.class(); + + return $keys.collect($SC.Function(function($list) { + var $dict; + + $dict = $class.new(); + $list.do($SC.Function(function($key) { + $dict.put($key, $this.at($key)); + })); + + return $dict; + })); + }; + + spec.transformEvent = fn(function($event) { + return $event.putAll(this); + }, "event"); + // TODO: implements embedInStream // TODO: implements asSortedArray // TODO: implements asKeyValuePairs - // TODO: implements keysValuesArrayDo + + spec.keysValuesArrayDo = function($argArray, $function) { + var $key, $val; + var array, j, i, imax; + + array = this._array._; + for (i = j = 0, imax = array.length; i < imax; i += 2, ++j) { + $key = array[i]; + if ($key !== $nil) { + $val = $argArray.at($SC.Integer(i + 1)); + $function.value($key, $val, $SC.Integer(j)); + } + } + }; + // TODO: implements grow // TODO: implements fixCollisionsFrom - // TODO: implements scanFor + + // istanbul ignore next + spec.scanFor = function($argKey) { + var array, i, imax; + var $elem; + + array = this._array._; + imax = array.length; + + for (i = 0; i < imax; i += 2) { + $elem = array[i]; + if (BOOL($elem ["=="] ($argKey))) { + return $SC.Integer(i); + } + } + for (i = 0; i < imax; i += 2) { + $elem = array[i]; + if ($elem === $nil) { + return $SC.Integer(i); + } + } + + return $SC.Integer(-2); + }; + // TODO: implements storeItemsOn // TODO: implements printItemsOn }); diff --git a/src/sc/classlib/Collections/Dictionary_test.js b/src/sc/classlib/Collections/Dictionary_test.js index c3ce046..c445a1c 100644 --- a/src/sc/classlib/Collections/Dictionary_test.js +++ b/src/sc/classlib/Collections/Dictionary_test.js @@ -3,14 +3,20 @@ require("./Dictionary"); + var $ = sc.test.$; + var testCase = sc.test.testCase; + var $SC = sc.lang.$SC; describe("SCDictionary", function() { - var SCDictionary; + var SCDictionary, SCAssociation, SCArray, SCSet; before(function() { - SCDictionary = $SC("Dictionary"); - this.createInstance = function() { - return SCDictionary.new(); + SCDictionary = $SC("Dictionary"); + SCAssociation = $SC("Association"); + SCArray = $SC("Array"); + SCSet = $SC("Set"); + this.createInstance = function(list) { + return SCDictionary.newFrom(list ? $(list) : $SC.Array()); }; }); it("#valueOf", function() { @@ -20,82 +26,531 @@ test = instance.valueOf(); expect(test).to.be.a("JSObject").that.eqls({}); }); - it.skip("#$newFrom", function() { - }); - it.skip("#at", function() { - }); - it.skip("#atFail", function() { - }); - it.skip("#matchAt", function() { - }); - it.skip("#trueAt", function() { - }); - it.skip("#add", function() { - }); - it.skip("#put", function() { - }); - it.skip("#putAll", function() { - }); - it.skip("#putPairs", function() { - }); - it.skip("#getPairs", function() { - }); - it.skip("#associationAt", function() { - }); - it.skip("#associationAtFail", function() { - }); - it.skip("#keys", function() { - }); - it.skip("#values", function() { - }); - it.skip("#includes", function() { - }); - it.skip("#includesKey", function() { - }); - it.skip("#removeAt", function() { - }); - it.skip("#removeAtFail", function() { - }); - it.skip("#remove", function() { - }); - it.skip("#removeFail", function() { - }); - it.skip("#keysValuesDo", function() { - }); - it.skip("#keysValuesChange", function() { - }); - it.skip("#do", function() { - }); - it.skip("#keysDo", function() { - }); - it.skip("#associationsDo", function() { - }); - it.skip("#pairsDo", function() { - }); - it.skip("#collect", function() { - }); - it.skip("#select", function() { - }); - it.skip("#reject", function() { + it("#$newFrom", function() { + var instance; + + instance = SCDictionary.newFrom($SC.Array([ + $SC.Integer(1), $SC.Integer(2), + $SC.Integer(3), $SC.Integer(4), + ])); + + expect(instance).to.be.a("SCDictionary").that.eqls({ 1:2, 3: 4 }); + }); + it("#at", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 3 ], + result: 4 + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 2 ], + result: null + } + ]); + }); + it("#atFail", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 3, 0 ], + result: 4 + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 2, 0 ], + result: 0 + } + ]); + }); + it("#matchAt", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 3 ], + result: 4 + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 2 ], + result: null + } + ]); + }); + it("#trueAt", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 3 ], + result: 4 + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 2 ], + result: false + } + ]); + }); + it("#add", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ SCAssociation.new($SC.Integer(5), $SC.Integer(6)) ], + result: this, + after : { 1: 2, 3: 4, 5: 6 } + }, + ]); + }); + it("#put", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ $SC.Integer(3), $SC.Nil() ], + result: this, + after : { 1: 2 } + }, + { + source: [ 1, 2, 3, 4 ], + args : [ $SC.Integer(3), $SC.Integer(5) ], + result: this, + after : { 1: 2, 3: 5 } + }, + { + source: [ 1, 2, 3, 4 ], + args : [ $SC.Integer(5), $SC.Integer(6) ], + result: this, + after : { 1: 2, 3: 4, 5: 6 } + }, + ]); + }); + it("#putAll", function() { + testCase(this, [ + { + source: [ 1, 2 ], + args : [ SCDictionary.newFrom($( [ 1, 2, 3, 4, 5, 6 ] )) ], + result: this, + after : { 1: 2, 3: 4, 5: 6 } + } + ]); + }); + it("#putPairs", function() { + testCase(this, [ + { + source: [ 1, 2 ], + args : [ $( [ 1, 2, 3, 4, 5, 6 ] ) ], + result: this, + after : { 1: 2, 3: 4, 5: 6 } + } + ]); + }); + it("#getPairs", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [], + result: [ 1, 2, 3, 4 ] + }, + { + source: [ 1, 2, 3, 4, 5, 6 ], + args : [ $([ 2, 3, 5 ]) ], + result: [ 3, 4, 5, 6 ] + }, + ]); + }); + it("#associationAt", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 3 ], + result: SCAssociation.new($SC.Integer(3), $SC.Integer(4)) + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 4 ], + result: SCAssociation.new($SC.Nil(), $SC.Nil()) + }, + ]); + }); + it("#associationAtFail", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 3, 0 ], + result: SCAssociation.new($SC.Integer(3), $SC.Integer(4)) + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 5, 0 ], + result: 0 + }, + ]); + }); + it("#keys", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + result: SCSet.newFrom($([ 1, 3 ])) + }, + { + source: [ 1, 2, 3, 4 ], + args : [ SCArray ], + result: [ 1, 3 ] + }, + ]); + }); + it("#values", sinon.test(function() { + this.stub(sc.lang.klass, "get").withArgs("List").returns(SCArray); + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + result: [ 2, 4 ] + }, + ]); + })); + it("#includes", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 1 ], + result: false + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 2 ], + result: true + }, + ]); + }); + it("#includesKey", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 1 ], + result: true + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 2 ], + result: false + }, + ]); + }); + it("#removeAt", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 1 ], + result: 2, + after : { 3: 4 } + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 5 ], + result: null, + after : { 1: 2, 3: 4 } + }, + ]); + }); + it("#removeAtFail", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 1, 0 ], + result: 2, + after : { 3: 4 } + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 5, 0 ], + result: 0, + after : { 1: 2, 3: 4 } + }, + ]); + }); + it("#remove", function() { + var instance; + + instance = this.createInstance(); + + expect(function() { + instance.remove(); + }).to.throw("shouldNotImplement"); }); - it.skip("#invert", function() { + it("#removeFail", function() { + var instance; + + instance = this.createInstance(); + + expect(function() { + instance.removeFail(); + }).to.throw("shouldNotImplement"); }); - it.skip("#merge", function() { + it("#keysValuesDo", sinon.test(function() { + var instance, test; + var spy, $function; + + spy = this.spy(); + $function = $SC.Function(spy); + + instance = this.createInstance([ 1, 2, 3, 4 ]); + + test = instance.keysValuesDo($function); + expect(test).to.equal(instance); + expect(spy).to.callCount(2); + expect(spy.args[0]).js.to.eql([ 1, 2, 0 ]); + expect(spy.args[1]).js.to.eql([ 3, 4, 1 ]); + })); + it("#keysValuesChange", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ function($key, $value, $i) { + return $SC.Array([ $key, $value, $i ]); + } ], + result: this, + after : { 1: [ 1, 2, 0 ], 3: [ 3, 4, 1 ] } + } + ]); + }); + it("#do", sinon.test(function() { + var instance, test; + var spy, $function; + + spy = this.spy(); + $function = $SC.Function(spy); + + instance = this.createInstance([ 1, 2, 3, 4 ]); + + test = instance.do($function); + expect(test).to.equal(instance); + + expect(spy).to.callCount(2); + expect(spy.args[0]).js.to.eql([ 2, 0 ]); + expect(spy.args[1]).js.to.eql([ 4, 1 ]); + })); + it("#keysDo", sinon.test(function() { + var instance, test; + var spy, $function; + + spy = this.spy(); + $function = $SC.Function(spy); + + instance = this.createInstance([ 1, 2, 3, 4 ]); + + test = instance.keysDo($function); + expect(test).to.equal(instance); + + expect(spy).to.callCount(2); + expect(spy.args[0]).js.to.eql([ 1, 0 ]); + expect(spy.args[1]).js.to.eql([ 3, 1 ]); + })); + it("#associationsDo", sinon.test(function() { + var instance, test; + var spy, $function; + + spy = this.spy(); + $function = $SC.Function(spy); + + instance = this.createInstance([ 1, 2, 3, 4 ]); + + test = instance.associationsDo($function); + expect(test).to.equal(instance); + + expect(spy).to.callCount(2); + expect(spy.args[0]).js.to.eql([ 1, 0 ]); + expect(spy.args[1]).js.to.eql([ 3, 1 ]); + })); + it("#pairsDo", sinon.test(function() { + var instance, test; + var spy, $function; + + spy = this.spy(); + $function = $SC.Function(spy); + + instance = this.createInstance([ 1, 2, 3, 4 ]); + + test = instance.pairsDo($function); + expect(test).to.equal(instance); + + expect(spy).to.callCount(2); + expect(spy.args[0]).js.to.eql([ 1, 2, 0 ]); + expect(spy.args[1]).js.to.eql([ 3, 4, 1 ]); + })); + it("#collect", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ function($elem, $key) { + return $SC.Array([ $elem, $key ]); + } ], + result: { 1: [ 2, 1 ], 3: [ 4, 3 ] } + }, + ]); + }); + it("#select", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ function($elem, $key) { + return $SC.Boolean($key.valueOf() === 1); + } ], + result: { 1: 2 } + }, + ]); + }); + it("#reject", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ function($elem, $key) { + return $SC.Boolean($key.valueOf() === 1); + } ], + result: { 3: 4 } + }, + ]); + }); + it("#invert", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + result: { 2: 1, 4: 3 } + }, + { + source: [ 1, 2, 3, 4, 5, 2 ], + result: { 4: 3, 2: 5 } + }, + ]); + }); + it("#merge", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args: [ + SCDictionary.newFrom($([ 5, 6, 7, 8 ])), + function($a, $b) { + return $a ["+"] ($b); + }, + ], + result: { 1: 2, 3: 4, 5: 6, 7: 8 } + }, + { + source: [ 1, 2, 3, 4 ], + args: [ + SCDictionary.newFrom($([ 3, 4, 5, 6 ])), + function($a, $b) { + return $a ["+"] ($b); + }, + false + ], + result: { 3: 8 } + }, + { + source: [ 1, 2, 3, 4 ], + args: [ + SCDictionary.newFrom($([ 1, 2, 3, 4 ])), + function($a, $b) { + return $a ["+"] ($b); + }, + false + ], + result: { 1: 4, 3: 8 } + }, + ]); }); it.skip("#blend", function() { }); - it.skip("#findKeyForValue", function() { - }); - it.skip("#sortedKeysValuesDo", function() { - }); - it.skip("#choose", function() { - }); - it.skip("#order", function() { - }); - it.skip("#powerset", function() { - }); - it.skip("#transformEvent", function() { - }); + it("#findKeyForValue", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + args : [ 2 ], + result: 1 + }, + { + source: [ 1, 2, 3, 4 ], + args : [ 1 ], + result: null + }, + ]); + }); + it("#sortedKeysValuesDo", sinon.test(function() { + var instance, test; + var spy, $function, $sortFunc; + + spy = this.spy(); + $function = $SC.Function(spy); + $sortFunc = $SC.Function(function($a, $b) { + return $b ["<="] ($a); + }); + + instance = this.createInstance([ 1, 2, 3, 4 ]); + + test = instance.sortedKeysValuesDo($function, $sortFunc); + expect(test).to.equal(instance); + + expect(spy).to.callCount(2); + expect(spy.args[0]).js.to.eql([ 3, 4, 0 ]); + expect(spy.args[1]).js.to.eql([ 1, 2, 1 ]); + })); + it("#choose", function() { + testCase(this, [ + { + source: [], + result: null + }, + { + source: [ 1, 2, 3, 4, 5, 6 ], + result: 2 + }, + ], { randSeed: 0 }); + }); + it("#order", function() { + testCase(this, [ + { + source: [], + result: null + }, + { + source: [ 1, 2, 3, 4, 5, 6 ], + result: [ 1, 3, 5 ] + }, + { + source: [ 1, 2, 3, 4, 5, 6 ], + args : [ function($a, $b) { + return $b ["<="] ($a); + } ], + result: [ 5, 3, 1 ] + } + ]); + }); + it("#powerset", function() { + testCase(this, [ + { + source: [ 1, 2, 3, 4 ], + result: [ + {}, + { 1: 2 }, + { 3: 4 }, + { 1: 2, 3: 4 } + ] + } + ]); + }); + it("#transformEvent", sinon.test(function() { + var instance, test; + var $event; + + $event = sc.test.object({ + putAll: this.spy(sc.test.func) + }); + + instance = this.createInstance(); + test = instance.transformEvent($event); + + expect($event.putAll).to.be.calledWith(instance); + expect($event.putAll).to.be.calledLastIn(test); + })); it.skip("#embedInStream", function() { }); it.skip("#asSortedArray", function() { diff --git a/src/sc/classlib/Collections/SequenceableCollection.js b/src/sc/classlib/Collections/SequenceableCollection.js index ffa2bf5..6adb725 100644 --- a/src/sc/classlib/Collections/SequenceableCollection.js +++ b/src/sc/classlib/Collections/SequenceableCollection.js @@ -1549,9 +1549,33 @@ SCScript.install(function(sc) { }; // TODO: implements minNyquist - // TODO: implements sort - // TODO: implements sortBy - // TODO: implements sortMap + + spec.sort = fn(function($function) { + if ($function === $nil) { + $function = $SC.Function(function($a, $b) { + return $a ["<="] ($b); + }); + } + this._sort($function); + return this; + }, "function"); + + spec.sortBy = fn(function($key) { + return this.sort($SC.Function(function($a, $b) { + return $a.at($key) ["<="] ($b.at($key)); + })); + }, "key"); + + spec.sortMap = fn(function($function) { + return this.sort($SC.Function(function($a, $b) { + return $function.value($a) ["<="] ($function.value($b)); + })); + }, "function"); + + // spec._sort = function($function) { + // this.mergeSort($function); + // }; + // TODO: implements sortedMedian // TODO: implements median // TODO: implements quickSort diff --git a/src/sc/classlib/Collections/SequenceableCollection_test.js b/src/sc/classlib/Collections/SequenceableCollection_test.js index f82d328..de637b2 100644 --- a/src/sc/classlib/Collections/SequenceableCollection_test.js +++ b/src/sc/classlib/Collections/SequenceableCollection_test.js @@ -1318,11 +1318,44 @@ it.skip("#minNyquist", function() { }); - it.skip("#sort", function() { + it("#sort", function() { + testCase(this, [ + { + source: [ 1, 5, 2, 4, 3 ], + result: this, + after : [ 1, 2, 3, 4, 5 ] + }, + { + source: [ 1, 5, 2, 4, 3 ], + args: [ function($a, $b) { + return $b ["<="] ($a); + } ], + result: this, + after : [ 5, 4, 3, 2, 1 ] + } + ]); }); - it.skip("#sortBy", function() { + it("#sortBy", function() { + testCase(this, [ + { + source: [ [ 9, 1 ], [ 2, 8 ], [ 5, 5 ], [ 7, 3 ], [ 4, 6 ] ], + args : [ 1 ], + result: this, + after : [ [ 9, 1 ], [ 7, 3 ], [ 5, 5 ], [ 4, 6 ], [ 2, 8 ] ] + } + ]); }); - it.skip("#sortMap", function() { + it("#sortMap", function() { + testCase(this, [ + { + source: [ -5, 3, -2, 0, 1, 6, 4 ], + args : [ function($a) { + return $a.abs(); + } ], + result: this, + after : [ 0, 1, -2, 3, 4, -5, 6 ] + } + ]); }); it.skip("#sortedMedian", function() { }); diff --git a/src/sc/classlib/Collections/Set.js b/src/sc/classlib/Collections/Set.js index fe9f31f..8d2fa9a 100644 --- a/src/sc/classlib/Collections/Set.js +++ b/src/sc/classlib/Collections/Set.js @@ -8,11 +8,11 @@ SCScript.install(function(sc) { var iterator = sc.lang.iterator; function SCSet(args) { - this.__initializeWith__("Collection"); var n = 2; if (args && args[0]) { n = args[0].__int__(); } + this.__initializeWith__("Collection"); this.initSet($SC.Integer(Math.max(n, 2) * 2)); } @@ -35,7 +35,7 @@ SCScript.install(function(sc) { }; spec.array_ = function($value) { - this._array = $value; + this._array = $value || /* istanbul ignore next */ $nil; return this; }; @@ -248,25 +248,11 @@ SCScript.install(function(sc) { }; spec.grow = function() { - var $this = this; - var oldElements; - - oldElements = this._array._; - this._array = SCArray.newClear( - $SC.Integer(this._array.size().__int__() * 2) - ); - this._size = $int_0; - - oldElements.forEach(function($item) { - if ($item !== $nil) { - $this.noCheckAdd($item); - } - }); - }; - - spec.noCheckAdd = function($item) { - this._array.put(this.scanFor($item), $item); - this._size = this._size.__inc__(); + var array, i, imax; + array = this._array._; + for (i = array.length, imax = i * 2; i < imax; ++i) { + array[i] = $nil; + } }; // istanbul ignore next diff --git a/src/sc/lang/klass/constructors.js b/src/sc/lang/klass/constructors.js index 1016972..e3896a1 100644 --- a/src/sc/lang/klass/constructors.js +++ b/src/sc/lang/klass/constructors.js @@ -235,10 +235,10 @@ return instance; }; - $SC.String = function(value, immutable) { + $SC.String = function(value) { var instance = new SCString(); instance._ = String(value).split("").map($SC.Char); - instance._immutable = !!immutable; + instance._immutable = true; return instance; }; diff --git a/src/sc/lang/klass/klass.js b/src/sc/lang/klass/klass.js index 7168d09..71ec008 100644 --- a/src/sc/lang/klass/klass.js +++ b/src/sc/lang/klass/klass.js @@ -193,6 +193,11 @@ // basic classes function SCObject() { this._ = this; + Object.defineProperties(this, { + _immutable: { + value: false, writable: true + } + }); } function SCClass() { diff --git a/src/sc/test/utils.js b/src/sc/test/utils.js index 153d8e4..6c67738 100644 --- a/src/sc/test/utils.js +++ b/src/sc/test/utils.js @@ -6,6 +6,7 @@ require("../lang/installer"); require("../classlib/Collections/Array"); + require("../classlib/Collections/Association"); require("../classlib/Collections/String"); require("../classlib/Core/Boolean"); require("../classlib/Core/Char"); @@ -71,7 +72,7 @@ return $SC.Function(a); } - throw new Error("should not reached"); + return a; }; sc.test.$ = encode; @@ -83,6 +84,10 @@ return str; }; + var isDictionary = function(obj) { + return obj && obj.constructor === Object; + }; + var isSCObject = function(obj) { return obj && typeof obj._ !== "undefined"; }; @@ -92,18 +97,10 @@ if (isSCObject(obj)) { switch (obj.__tag) { - case sc.C.TAG_INT: return "SCInteger"; - case sc.C.TAG_SYM: return "SCSymbol"; - case sc.C.TAG_CHAR: return "SCChar"; - case sc.C.TAG_NIL: return "SCNil"; case sc.C.TAG_FALSE: return "SCBoolean"; case sc.C.TAG_TRUE: return "SCBoolean"; - case sc.C.TAG_FLOAT: return "SCFloat"; - case sc.C.TAG_STR: return "SCString"; - case sc.C.TAG_ARRAY: return "SCArray"; - case sc.C.TAG_FUNCTION: return "SCFunction"; } - return "SCObject"; + return "SC" + obj.__className; } if (guess) { @@ -230,6 +227,10 @@ }) ); } + } else if (isDictionary(raw)) { + if (items.after) { + expect(raw).with_message(desc + ": after").to.eql(items.after); + } } }); };