├── .gitignore ├── LICENSE ├── README.rst ├── benchmarks ├── List │ ├── 2014-12-25 │ ├── 2014-12-26 │ ├── 2014-12-30 │ ├── 2015-01-01 │ ├── 2015-01-13 │ └── 2015-01-16 ├── Queue │ └── 2015-01-13 ├── Record │ ├── 2015-01-04 │ ├── 2015-01-07 │ ├── 2015-01-16 │ └── 2015-01-22 └── Tuple │ ├── 2015-01-14 │ └── 2015-01-17 ├── build.js ├── build ├── Benchmark.js ├── Immutable.js ├── Immutable.min.js └── Test.js ├── doc ├── Changelog.rst └── doc.rst ├── package.json └── src ├── Benchmark.js ├── Benchmark ├── Array.js ├── List.js ├── Queue.js ├── Record.js ├── Tuple.js └── util.js ├── Immutable.js ├── Immutable ├── AVL.js ├── Array.js ├── Base.js ├── Cons.js ├── ImmutableDict.js ├── ImmutableList.js ├── ImmutableQueue.js ├── ImmutableRecord.js ├── ImmutableSet.js ├── ImmutableStack.js ├── ImmutableTuple.js ├── ImmutableType.js ├── MutableRef.js ├── Ordered.js ├── Sorted.js ├── Tag.js ├── UMD.js ├── equal.js ├── hash.js ├── iter.js ├── static.js ├── toJS.js ├── toJSON.js └── util.js ├── Test.js └── Test ├── Dict.js ├── List.js ├── Queue.js ├── Record.js ├── Ref.js ├── Set.js ├── Stack.js ├── Tag.js ├── Tuple.js ├── Type.js ├── assert.js ├── iter.js ├── misc.js ├── shim.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (c) 2014, 2015 Oni Labs, http://onilabs.com 2 | 3 | This file is licensed under the terms of the MIT License: 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | How to use 2 | ========== 3 | 4 | Just load up ``build/Immutable.min.js``. It works with AMD / Require.js, CommonJS / Node.js, or the ``Immutable`` global variable. 5 | 6 | You can find documentation in the ``doc/doc.rst`` file. 7 | 8 | You can find benchmarks in the ``benchmarks`` folder. 9 | 10 | 11 | Quick overview 12 | ============== 13 | 14 | See the documentation for an alphabetical list of the types / functions provided by this library. 15 | 16 | ---- 17 | 18 | ``Dict`` and ``Set`` can have anything as keys, including mutable objects and immutable objects (``Dict``, ``Set``, ``List``, etc.) 19 | 20 | You can also use ``SortedDict`` and ``SortedSet`` to define your own custom sorting. 21 | 22 | ---- 23 | 24 | Equality is well defined, and is based on `egal `__. 25 | 26 | What that means is that mutable objects are only equal if they are exactly the same object, but immutable objects are equal if they have the same value. 27 | 28 | These two mutable objects are different, and so they are not equal: 29 | 30 | .. code:: javascript 31 | 32 | // false 33 | equal({ "foo": 1 }, 34 | { "foo": 1 }); 35 | 36 | These two immutable objects are different, but they have the same keys / values, and so they are equal: 37 | 38 | .. code:: javascript 39 | 40 | // true 41 | equal(Dict({ "foo": 1 }), 42 | Dict({ "foo": 1 })); 43 | 44 | This is the only sane default behavior for equality. 45 | 46 | ---- 47 | 48 | You can easily convert to / from JavaScript: 49 | 50 | .. code:: javascript 51 | 52 | var obj = { "foo": 1 }; 53 | var dict = fromJS(obj); 54 | var obj = toJS(dict); 55 | 56 | You can also easily convert from one data type to another: 57 | 58 | .. code:: javascript 59 | 60 | var dict = Dict({ "foo": 1 }); 61 | var list = List(dict); 62 | var stack = Stack(list); 63 | var record = Record(stack); 64 | 65 | You can also losslessly convert to / from JSON, allowing for sending immutable objects over the network: 66 | 67 | .. code:: javascript 68 | 69 | var record1 = Record({ "foo": 1 }); 70 | var json = toJSON(record1); 71 | var record2 = fromJSON(json); 72 | 73 | // true 74 | equal(record1, record2); 75 | 76 | ---- 77 | 78 | For most operations, if the new value is the same as the old value, then it just returns the old value: 79 | 80 | .. code:: javascript 81 | 82 | var dict1 = Dict({ 83 | "foo": 1, 84 | "bar": 2 85 | }); 86 | 87 | var dict2 = dict1.set("foo", 1); 88 | 89 | // true 90 | dict2 === dict1; 91 | 92 | This means that you can use this library with `React `__, `Mercury `__, etc. and it will be **very** fast. 93 | 94 | This is also useful anytime you want to efficiently check if something has changed or not: 95 | 96 | .. code:: javascript 97 | 98 | var old_value = null; 99 | 100 | // Saving to the database is expensive, so we want to avoid doing it 101 | function save_to_database(value) { 102 | // Do nothing, the data has not changed 103 | if (value === old_value) { 104 | return; 105 | } 106 | 107 | old_value = value; 108 | 109 | // Save to the database 110 | ... 111 | } 112 | 113 | In the above function, if the immutable data has not changed, then it will be ``===`` to the old data, so we can avoid doing the expensive save operation. 114 | 115 | This should **only** be used as an optimization to speed things up: you should use ``equal`` to test whether two immutable objects are equal or not. 116 | 117 | How do you determine whether to use ``===`` or ``equal``? If using ``===`` changes the behavior of the program, then you should use ``equal`` instead. 118 | 119 | In this case, if the data is ``===``, we can safely choose to not save to the database. And if it's **not** ``===``, that's fine too: it just means we have to do the expensive save operation. So either way, the behavior is identical. 120 | 121 | But in a different situation, using ``===`` would change the behavior of the program, and so in that case you should use ``equal`` instead. 122 | 123 | ---- 124 | 125 | All data types accept an `ECMAScript 6 Iterable `__ and can be used as an ECMAScript 6 Iterable: 126 | 127 | .. code:: javascript 128 | 129 | var tuple = Tuple([1, 2, 3]); 130 | 131 | // 1 132 | // 2 133 | // 3 134 | for (var x of tuple) { 135 | console.log(x); 136 | } 137 | 138 | In addition, the various iteration functions (``each``, ``map``, ``zip``, etc.) accept and return Iterables: 139 | 140 | .. code:: javascript 141 | 142 | var tuple2 = map(tuple, function (x) { 143 | return x + 20; 144 | }); 145 | 146 | // 21 147 | // 22 148 | // 23 149 | for (var x of tuple2) { 150 | console.log(x); 151 | } 152 | 153 | 154 | For developers 155 | ============== 156 | 157 | You'll probably need to use ``npm install`` to get the required dependencies. Every time you make a change to the ``src`` directory, you have to run ``npm install`` to rebuild. 158 | 159 | Run the benchmarks with ``node build/Benchmark.js``. This will take a long time (several minutes, possibly hours). 160 | 161 | The unit tests are automatically run when using ``npm install``, but you can also run them manually by using ``node build/Test.js``. 162 | -------------------------------------------------------------------------------- /benchmarks/Record/2015-01-22: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | Information: 3 | -------------------------------------------------------------------- 4 | Node.js: 5 | URL: http://nodejs.org/ 6 | Version: v0.10.22 7 | -------------------------------------------------------------------- 8 | Benchmark.js: 9 | URL: https://github.com/bestiejs/benchmark.js 10 | Version: 1.0.0 11 | -------------------------------------------------------------------- 12 | Immutable-js: 13 | URL: https://github.com/facebook/immutable-js 14 | Version: 3.6.2 15 | -------------------------------------------------------------------- 16 | Mori: 17 | URL: https://github.com/swannodette/mori 18 | Version: 0.3.2 19 | -------------------------------------------------------------------- 20 | Immutable: 21 | URL: https://github.com/Pauan/Immutable 22 | Version: 6.0.1 23 | ---------------------------------------------------------------------- 24 | Record with 5 keys: 25 | -------------------------------------------------------------------- 26 | Creating: 27 | JavaScript Object 1,204,058 ops/sec 28 | JavaScript Object Copying 1,095,305 ops/sec 29 | JavaScript Object Copying (eval) 42,873,533 ops/sec 30 | JavaScript Object Copying (constructor) 25,558,706 ops/sec 31 | Immutable-js Map 175,769 ops/sec 32 | Immutable-js Record 170,322 ops/sec 33 | Mori Hash Map 707,800 ops/sec 34 | Mori Sorted Map 215,548 ops/sec 35 | Immutable Dict 161,483 ops/sec 36 | Immutable SortedDict 285,859 ops/sec 37 | Immutable Record 178,127 ops/sec 38 | -------------------------------------------------------------------- 39 | Retrieving at random: 40 | JavaScript Object 41 | JavaScript Object Copying 16,847,533 ops/sec 42 | JavaScript Object Copying (eval) 19,331,389 ops/sec 43 | JavaScript Object Copying (constructor) 16,977,115 ops/sec 44 | Immutable-js Map 1,584,120 ops/sec 45 | Immutable-js Record 1,359,444 ops/sec 46 | Mori Hash Map 9,352,134 ops/sec 47 | Mori Sorted Map 1,728,396 ops/sec 48 | Immutable Dict 2,872,156 ops/sec 49 | Immutable SortedDict 12,290,824 ops/sec 50 | Immutable Record 14,584,597 ops/sec 51 | -------------------------------------------------------------------- 52 | Setting at random: 53 | JavaScript Object 8,798,009 ops/sec 54 | JavaScript Object Copying 1,002,012 ops/sec 55 | JavaScript Object Copying (eval) 8,121,041 ops/sec 56 | JavaScript Object Copying (constructor) 7,018,078 ops/sec 57 | Immutable-js Map 751,376 ops/sec 58 | Immutable-js Record 433,248 ops/sec 59 | Mori Hash Map 6,233,720 ops/sec 60 | Mori Sorted Map 638,348 ops/sec 61 | Immutable Dict 1,179,260 ops/sec 62 | Immutable SortedDict 2,335,362 ops/sec 63 | Immutable Record 7,515,592 ops/sec 64 | ---------------------------------------------------------------------- 65 | Record with 10 keys: 66 | -------------------------------------------------------------------- 67 | Creating: 68 | JavaScript Object 588,616 ops/sec 69 | JavaScript Object Copying 644,269 ops/sec 70 | JavaScript Object Copying (eval) 20,469,783 ops/sec 71 | JavaScript Object Copying (constructor) 17,326,960 ops/sec 72 | Immutable-js Map 50,140 ops/sec 73 | Immutable-js Record 55,793 ops/sec 74 | Mori Hash Map 97,036 ops/sec 75 | Mori Sorted Map 73,186 ops/sec 76 | Immutable Dict 84,908 ops/sec 77 | Immutable SortedDict 141,118 ops/sec 78 | Immutable Record 110,942 ops/sec 79 | -------------------------------------------------------------------- 80 | Retrieving at random: 81 | JavaScript Object 82 | JavaScript Object Copying 18,617,735 ops/sec 83 | JavaScript Object Copying (eval) 19,873,268 ops/sec 84 | JavaScript Object Copying (constructor) 19,571,290 ops/sec 85 | Immutable-js Map 4,580,902 ops/sec 86 | Immutable-js Record 3,198,854 ops/sec 87 | Mori Hash Map 1,756,475 ops/sec 88 | Mori Sorted Map 1,112,825 ops/sec 89 | Immutable Dict 2,766,685 ops/sec 90 | Immutable SortedDict 10,384,782 ops/sec 91 | Immutable Record 16,921,448 ops/sec 92 | -------------------------------------------------------------------- 93 | Setting at random: 94 | JavaScript Object 8,851,457 ops/sec 95 | JavaScript Object Copying 607,535 ops/sec 96 | JavaScript Object Copying (eval) 6,440,493 ops/sec 97 | JavaScript Object Copying (constructor) 5,807,646 ops/sec 98 | Immutable-js Map 909,167 ops/sec 99 | Immutable-js Record 472,073 ops/sec 100 | Mori Hash Map 972,947 ops/sec 101 | Mori Sorted Map 441,935 ops/sec 102 | Immutable Dict 1,087,155 ops/sec 103 | Immutable SortedDict 2,047,414 ops/sec 104 | Immutable Record 6,889,267 ops/sec 105 | ---------------------------------------------------------------------- 106 | Record with 100 keys: 107 | -------------------------------------------------------------------- 108 | Creating: 109 | JavaScript Object 55,663 ops/sec 110 | JavaScript Object Copying 57,121 ops/sec 111 | JavaScript Object Copying (eval) 1,799,717 ops/sec 112 | JavaScript Object Copying (constructor) 1,436,150 ops/sec 113 | Immutable-js Map 15,781 ops/sec 114 | Immutable-js Record 15,591 ops/sec 115 | Mori Hash Map 12,056 ops/sec 116 | Mori Sorted Map 3,203 ops/sec 117 | Immutable Dict 7,525 ops/sec 118 | Immutable SortedDict 11,420 ops/sec 119 | Immutable Record 13,268 ops/sec 120 | -------------------------------------------------------------------- 121 | Retrieving at random: 122 | JavaScript Object 123 | JavaScript Object Copying 17,286,191 ops/sec 124 | JavaScript Object Copying (eval) 14,332,457 ops/sec 125 | JavaScript Object Copying (constructor) 14,250,535 ops/sec 126 | Immutable-js Map 4,453,713 ops/sec 127 | Immutable-js Record 2,957,703 ops/sec 128 | Mori Hash Map 1,895,745 ops/sec 129 | Mori Sorted Map 448,359 ops/sec 130 | Immutable Dict 2,149,011 ops/sec 131 | Immutable SortedDict 6,301,231 ops/sec 132 | Immutable Record 15,883,163 ops/sec 133 | -------------------------------------------------------------------- 134 | Setting at random: 135 | JavaScript Object 9,764,501 ops/sec 136 | JavaScript Object Copying 56,339 ops/sec 137 | JavaScript Object Copying (eval) 817,309 ops/sec 138 | JavaScript Object Copying (constructor) 630,900 ops/sec 139 | Immutable-js Map 506,224 ops/sec 140 | Immutable-js Record 309,680 ops/sec 141 | Mori Hash Map 964,601 ops/sec 142 | Mori Sorted Map 205,058 ops/sec 143 | Immutable Dict 830,345 ops/sec 144 | Immutable SortedDict 1,387,901 ops/sec 145 | Immutable Record 2,586,777 ops/sec 146 | ---------------------------------------------------------------------- 147 | Record with 1000 keys: 148 | -------------------------------------------------------------------- 149 | Creating: 150 | JavaScript Object 5,257 ops/sec 151 | JavaScript Object Copying 5,014 ops/sec 152 | JavaScript Object Copying (eval) 113,176 ops/sec 153 | JavaScript Object Copying (constructor) 5,191 ops/sec 154 | Immutable-js Map 1,701 ops/sec 155 | Immutable-js Record 1,685 ops/sec 156 | Mori Hash Map 508 ops/sec 157 | Mori Sorted Map 190 ops/sec 158 | Immutable Dict 651 ops/sec 159 | Immutable SortedDict 924 ops/sec 160 | Immutable Record 1,174 ops/sec 161 | -------------------------------------------------------------------- 162 | Retrieving at random: 163 | JavaScript Object 164 | JavaScript Object Copying 16,917,309 ops/sec 165 | JavaScript Object Copying (eval) 4,711,675 ops/sec 166 | JavaScript Object Copying (constructor) 15,499,889 ops/sec 167 | Immutable-js Map 3,606,848 ops/sec 168 | Immutable-js Record 2,521,340 ops/sec 169 | Mori Hash Map 829,717 ops/sec 170 | Mori Sorted Map 278,405 ops/sec 171 | Immutable Dict 1,849,494 ops/sec 172 | Immutable SortedDict 3,889,216 ops/sec 173 | Immutable Record 14,399,170 ops/sec 174 | -------------------------------------------------------------------- 175 | Setting at random: 176 | JavaScript Object 9,438,404 ops/sec 177 | JavaScript Object Copying 5,053 ops/sec 178 | JavaScript Object Copying (eval) 41,099 ops/sec 179 | JavaScript Object Copying (constructor) 5,230 ops/sec 180 | Immutable-js Map 561,784 ops/sec 181 | Immutable-js Record 329,745 ops/sec 182 | Mori Hash Map 508,508 ops/sec 183 | Mori Sorted Map 130,730 ops/sec 184 | Immutable Dict 682,999 ops/sec 185 | Immutable SortedDict 1,075,632 ops/sec 186 | Immutable Record 364,533 ops/sec 187 | ---------------------------------------------------------------------- 188 | Record with 10000 keys: 189 | -------------------------------------------------------------------- 190 | Creating: 191 | JavaScript Object 515 ops/sec 192 | JavaScript Object Copying 515 ops/sec 193 | JavaScript Object Copying (eval) 2,220 ops/sec 194 | JavaScript Object Copying (constructor) 346 ops/sec 195 | Immutable-js Map 155 ops/sec 196 | Immutable-js Record 159 ops/sec 197 | Mori Hash Map 64 ops/sec 198 | Mori Sorted Map 14 ops/sec 199 | Immutable Dict 49 ops/sec 200 | Immutable SortedDict 71 ops/sec 201 | Immutable Record 96 ops/sec 202 | -------------------------------------------------------------------- 203 | Retrieving at random: 204 | JavaScript Object 205 | JavaScript Object Copying 10,954,466 ops/sec 206 | JavaScript Object Copying (eval) 12,109,336 ops/sec 207 | JavaScript Object Copying (constructor) 11,137,889 ops/sec 208 | Immutable-js Map 2,979,187 ops/sec 209 | Immutable-js Record 1,931,894 ops/sec 210 | Mori Hash Map 784,784 ops/sec 211 | Mori Sorted Map 200,774 ops/sec 212 | Immutable Dict 1,445,646 ops/sec 213 | Immutable SortedDict 2,499,242 ops/sec 214 | Immutable Record 10,318,904 ops/sec 215 | -------------------------------------------------------------------- 216 | Setting at random: 217 | JavaScript Object 7,489,325 ops/sec 218 | JavaScript Object Copying 526 ops/sec 219 | JavaScript Object Copying (eval) 2,291 ops/sec 220 | JavaScript Object Copying (constructor) 346 ops/sec 221 | Immutable-js Map 432,834 ops/sec 222 | Immutable-js Record 273,398 ops/sec 223 | Mori Hash Map 426,238 ops/sec 224 | Mori Sorted Map 93,252 ops/sec 225 | Immutable Dict 539,587 ops/sec 226 | Immutable SortedDict 707,016 ops/sec 227 | Immutable Record 36,596 ops/sec 228 | -------------------------------------------------------------------------------- /benchmarks/Tuple/2015-01-17: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------- 2 | Information: 3 | -------------------------------------------------------------------- 4 | Node.js: 5 | URL: http://nodejs.org/ 6 | Version: v0.10.22 7 | -------------------------------------------------------------------- 8 | Benchmark.js: 9 | URL: https://github.com/bestiejs/benchmark.js 10 | Version: 1.0.0 11 | -------------------------------------------------------------------- 12 | Immutable-js: 13 | URL: https://github.com/facebook/immutable-js 14 | Version: 3.6.2 15 | -------------------------------------------------------------------- 16 | Mori: 17 | URL: https://github.com/swannodette/mori 18 | Version: 0.3.2 19 | -------------------------------------------------------------------- 20 | Immutable: 21 | URL: https://github.com/Pauan/Immutable 22 | Version: 6.0.1 23 | ---------------------------------------------------------------------- 24 | Tuple with 5 values: 25 | -------------------------------------------------------------------- 26 | Creating: 27 | JavaScript Array 14,719,496 ops/sec 28 | JavaScript Array Copying 30,781,824 ops/sec 29 | Immutable-js List 614,481 ops/sec 30 | Mori Vector 8,471,050 ops/sec 31 | Mori Vector (into) 582,507 ops/sec 32 | Immutable List 1,267,774 ops/sec 33 | Immutable Tuple 1,897,825 ops/sec 34 | Immutable Stack 1,118,552 ops/sec 35 | Cons 21,117,265 ops/sec 36 | -------------------------------------------------------------------- 37 | Retrieving at random: 38 | JavaScript Array 44,961,520 ops/sec 39 | JavaScript Array (error checking) 30,438,424 ops/sec 40 | Immutable-js List 27,458,094 ops/sec 41 | Mori Vector 27,134,884 ops/sec 42 | Immutable List 22,819,787 ops/sec 43 | Immutable Tuple 34,145,687 ops/sec 44 | -------------------------------------------------------------------- 45 | Setting at random: 46 | JavaScript Array 47 | JavaScript Array Copying 15,339,224 ops/sec 48 | Immutable-js List 1,828,753 ops/sec 49 | Mori Vector 12,225,278 ops/sec 50 | Immutable List 5,768,977 ops/sec 51 | Immutable Tuple 11,110,627 ops/sec 52 | ---------------------------------------------------------------------- 53 | Tuple with 10 values: 54 | -------------------------------------------------------------------- 55 | Creating: 56 | JavaScript Array 11,411,600 ops/sec 57 | JavaScript Array Copying 22,139,652 ops/sec 58 | Immutable-js List 576,749 ops/sec 59 | Mori Vector 6,739,631 ops/sec 60 | Mori Vector (into) 555,311 ops/sec 61 | Immutable List 870,482 ops/sec 62 | Immutable Tuple 1,543,006 ops/sec 63 | Immutable Stack 789,855 ops/sec 64 | Cons 11,923,637 ops/sec 65 | -------------------------------------------------------------------- 66 | Retrieving at random: 67 | JavaScript Array 45,483,701 ops/sec 68 | JavaScript Array (error checking) 27,346,888 ops/sec 69 | Immutable-js List 26,651,231 ops/sec 70 | Mori Vector 25,465,143 ops/sec 71 | Immutable List 18,951,074 ops/sec 72 | Immutable Tuple 34,245,625 ops/sec 73 | -------------------------------------------------------------------- 74 | Setting at random: 75 | JavaScript Array 76 | JavaScript Array Copying 12,753,425 ops/sec 77 | Immutable-js List 1,906,318 ops/sec 78 | Mori Vector 10,192,209 ops/sec 79 | Immutable List 4,731,421 ops/sec 80 | Immutable Tuple 9,473,911 ops/sec 81 | ---------------------------------------------------------------------- 82 | Tuple with 100 values: 83 | -------------------------------------------------------------------- 84 | Creating: 85 | JavaScript Array 2,080,641 ops/sec 86 | JavaScript Array Copying 4,066,960 ops/sec 87 | Immutable-js List 75,805 ops/sec 88 | Mori Vector 284,503 ops/sec 89 | Mori Vector (into) 178,258 ops/sec 90 | Immutable List 137,119 ops/sec 91 | Immutable Tuple 373,408 ops/sec 92 | Immutable Stack 122,156 ops/sec 93 | Cons 1,334,688 ops/sec 94 | -------------------------------------------------------------------- 95 | Retrieving at random: 96 | JavaScript Array 44,120,290 ops/sec 97 | JavaScript Array (error checking) 29,626,075 ops/sec 98 | Immutable-js List 23,059,082 ops/sec 99 | Mori Vector 24,544,961 ops/sec 100 | Immutable List 8,110,075 ops/sec 101 | Immutable Tuple 35,289,644 ops/sec 102 | -------------------------------------------------------------------- 103 | Setting at random: 104 | JavaScript Array 105 | JavaScript Array Copying 3,550,241 ops/sec 106 | Immutable-js List 1,385,829 ops/sec 107 | Mori Vector 3,272,865 ops/sec 108 | Immutable List 1,445,989 ops/sec 109 | Immutable Tuple 3,183,229 ops/sec 110 | ---------------------------------------------------------------------- 111 | Tuple with 1000 values: 112 | -------------------------------------------------------------------- 113 | Creating: 114 | JavaScript Array 224,579 ops/sec 115 | JavaScript Array Copying 400,393 ops/sec 116 | Immutable-js List 8,932 ops/sec 117 | Mori Vector 28,304 ops/sec 118 | Mori Vector (into) 25,110 ops/sec 119 | Immutable List 12,594 ops/sec 120 | Immutable Tuple 30,966 ops/sec 121 | Immutable Stack 11,441 ops/sec 122 | Cons 134,111 ops/sec 123 | -------------------------------------------------------------------- 124 | Retrieving at random: 125 | JavaScript Array 41,855,033 ops/sec 126 | JavaScript Array (error checking) 28,693,816 ops/sec 127 | Immutable-js List 23,555,813 ops/sec 128 | Mori Vector 24,386,909 ops/sec 129 | Immutable List 15,004,506 ops/sec 130 | Immutable Tuple 34,189,841 ops/sec 131 | -------------------------------------------------------------------- 132 | Setting at random: 133 | JavaScript Array 134 | JavaScript Array Copying 393,578 ops/sec 135 | Immutable-js List 1,409,775 ops/sec 136 | Mori Vector 3,277,466 ops/sec 137 | Immutable List 1,768,493 ops/sec 138 | Immutable Tuple 392,448 ops/sec 139 | ---------------------------------------------------------------------- 140 | Tuple with 10000 values: 141 | -------------------------------------------------------------------- 142 | Creating: 143 | JavaScript Array 23,398 ops/sec 144 | JavaScript Array Copying 38,812 ops/sec 145 | Immutable-js List 813 ops/sec 146 | Mori Vector 2,723 ops/sec 147 | Mori Vector (into) 2,547 ops/sec 148 | Immutable List 1,391 ops/sec 149 | Immutable Tuple 4,606 ops/sec 150 | Immutable Stack 1,277 ops/sec 151 | Cons 13,338 ops/sec 152 | -------------------------------------------------------------------- 153 | Retrieving at random: 154 | JavaScript Array 36,295,244 ops/sec 155 | JavaScript Array (error checking) 29,494,640 ops/sec 156 | Immutable-js List 20,241,185 ops/sec 157 | Mori Vector 21,911,567 ops/sec 158 | Immutable List 12,817,960 ops/sec 159 | Immutable Tuple 33,015,107 ops/sec 160 | -------------------------------------------------------------------- 161 | Setting at random: 162 | JavaScript Array 163 | JavaScript Array Copying 38,808 ops/sec 164 | Immutable-js List 1,206,016 ops/sec 165 | Mori Vector 2,212,783 ops/sec 166 | Immutable List 1,558,214 ops/sec 167 | Immutable Tuple 38,514 ops/sec 168 | -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | var fs = require("fs"); 4 | var child_process = require("child_process"); 5 | 6 | var package = require("./package.json"); 7 | 8 | // TODO is this correct ? 9 | // TODO handle stderr differently ? 10 | function exec(s, args, cb) { 11 | var process = child_process.spawn(s, args, { stdio: "inherit" }); 12 | 13 | var called = false; 14 | 15 | process.on("error", function (err) { 16 | if (!called) { 17 | called = true; 18 | return cb(err); 19 | } 20 | }); 21 | 22 | process.on("exit", function (code) { 23 | if (!called) { 24 | called = true; 25 | 26 | if (code === 0) { 27 | return cb(null); 28 | } else { 29 | return cb(new Error(code)); 30 | } 31 | } 32 | }); 33 | } 34 | 35 | function rm(file, cb) { 36 | fs.unlink(file, function (err) { 37 | if (err) { 38 | if (err.code === "ENOENT") { 39 | return cb(null); 40 | } else { 41 | return cb(err); 42 | } 43 | } else { 44 | return cb(null); 45 | } 46 | }); 47 | } 48 | 49 | function read(file, cb) { 50 | fs.readFile(file, { encoding: "utf8" }, cb); 51 | } 52 | 53 | function write(file, value, cb) { 54 | fs.writeFile(file, value, { encoding: "utf8" }, cb); 55 | } 56 | 57 | function add_license(to, license, cb) { 58 | license = "@license\n\nVersion " + package.version + "\n\n" + license.trim(); 59 | 60 | license = "/**\n" + license.split(/\n/g).map(function (x) { 61 | if (x === "") { 62 | return " *"; 63 | } else { 64 | return " * " + x; 65 | } 66 | }).join("\n") + "\n */\n"; 67 | 68 | read(to + ".js", function (err, data) { 69 | if (err) return cb(err); 70 | 71 | write(to + ".js", license + data, cb); 72 | }); 73 | } 74 | 75 | function compile(license, from, to, cb) { 76 | exec("compile-modules", ["convert", "--output", to + ".js", "--format", 77 | "bundle", from], function (err) { 78 | if (err) return cb(err); 79 | 80 | rm(to + ".js.map", function (err) { 81 | if (err) return cb(err); 82 | 83 | add_license(to, license, cb); 84 | }); 85 | }); 86 | } 87 | 88 | function minify(file, cb) { 89 | // --in-source-map build/Immutable.js.map --source-map build/Immutable.js.map 90 | exec("uglifyjs", [file + ".js", "--comments", "--screw-ie8", "--mangle", 91 | "--compress", "unsafe,pure_getters", "--output", file + ".min.js"], cb); 92 | } 93 | 94 | function run(cb) { 95 | read("./LICENSE", function (err, license) { 96 | if (err) return cb(err); 97 | 98 | compile(license, "./src/Immutable/UMD.js", "./build/Immutable", function (err) { 99 | if (err) return cb(err); 100 | 101 | minify("./build/Immutable", function (err) { 102 | if (err) return cb(err); 103 | 104 | compile(license, "./src/Benchmark.js", "./build/Benchmark", function (err) { 105 | if (err) return cb(err); 106 | 107 | compile(license, "./src/Test.js", "./build/Test", function (err) { 108 | if (err) return cb(err); 109 | 110 | rm("./build/Benchmark.min.js", function (err) { 111 | if (err) return cb(err); 112 | 113 | rm("./build/Test.min.js", function (err) { 114 | if (err) return cb(err); 115 | 116 | exec("node", ["./build/Test.js"], cb); 117 | }); 118 | }); 119 | }); 120 | }); 121 | }); 122 | }); 123 | }); 124 | } 125 | 126 | run(function (err) { 127 | if (err) throw err; 128 | }); 129 | -------------------------------------------------------------------------------- /doc/Changelog.rst: -------------------------------------------------------------------------------- 1 | Version 6.5.0 2 | ============= 3 | 4 | * **New features** 5 | 6 | * Adding in ``Type`` type, which is a special ``Tuple`` that can be tagged with a particular type. 7 | 8 | 9 | Version 6.4.0 10 | ============= 11 | 12 | * **New features** 13 | 14 | * Adding in ``Record isEmpty`` method, which returns ``true`` if the Record has no keys / values. 15 | 16 | * Adding in ``Tuple isEmpty`` method, which returns ``true`` if the Tuple has no values. 17 | 18 | 19 | Version 6.3.0 20 | ============= 21 | 22 | * **New features** 23 | 24 | * Adding in ``skip`` function, which skips a certain number of values. 25 | 26 | 27 | Version 6.2.0 28 | ============= 29 | 30 | * **New features** 31 | 32 | * Adding in ``repeat`` function, which repeats a value a certain number of times. 33 | 34 | 35 | Version 6.1.1 36 | ============= 37 | 38 | * **Bug fixes** 39 | 40 | * Using ``List slice`` where the first index is at the end of the list was an error, now it returns an empty list. 41 | 42 | 43 | Version 6.1.0 44 | ============= 45 | 46 | * **New features** 47 | 48 | * Adding in ``Record has``. 49 | 50 | * ``Record get`` now has an optional second argument, with the same behavior as ``Dict get``. 51 | 52 | 53 | Version 6.0.1 54 | ============= 55 | 56 | * **Bug fixes** 57 | 58 | * Fixing the incorrect behavior of ``Queue peek``. 59 | 60 | 61 | Version 6.0.0 62 | ============= 63 | 64 | * **Breaking changes** 65 | 66 | * ``build/Immutable.js`` has been renamed to ``build/Immutable.min.js``. 67 | 68 | * Before, the ``onchange`` function of a ``Ref`` was not called if the 69 | old and new values were ``===``. Now the ``onchange`` function is 70 | always called. 71 | 72 | In addition, whatever the ``onchange`` function returns becomes the 73 | new value. This enables it to do validation, returning the old 74 | value, or modifying the value before returning it. 75 | 76 | The old system: 77 | 78 | .. code:: javascript 79 | 80 | var ref = Ref(5, function (before, after) { 81 | // We can't really do much inside of the `onchange` function 82 | console.log("change", before, after); 83 | }); 84 | 85 | // The `onchange` function is not called, because the old and new values are `===` 86 | ref.set(5); 87 | 88 | // The `onchange` function is called, but it can't really do much. 89 | ref.set(10); 90 | 91 | The new system: 92 | 93 | .. code:: javascript 94 | 95 | var ref = Ref(5, function (before, after) { 96 | // Whatever the `onchange` function returns becomes the new value 97 | return before + after + 50; 98 | }); 99 | 100 | // The `onchange` function is called 101 | ref.set(5); 102 | 103 | // returns 60 104 | ref.get(); 105 | 106 | // The `onchange` function is called 107 | ref.set(10); 108 | 109 | // returns 120 110 | ref.get(); 111 | 112 | * The various constructor functions now use ``arguments.length`` 113 | to check for missing arguments, rather than checking for ``null``. 114 | 115 | To migrate to the new version: 116 | 117 | * ``Dict(null) -> Dict()`` 118 | * ``List(null) -> List()`` 119 | * ``Queue(null) -> Queue()`` 120 | * ``Record(null) -> Record()`` 121 | * ``Set(null) -> Set()`` 122 | * ``SortedDict(null) -> SortedDict()`` 123 | * ``SortedSet(null) -> SortedSet()`` 124 | * ``Stack(null) -> Stack()`` 125 | * ``Tuple(null) -> Tuple()`` 126 | 127 | * ``List slice``: now uses ``arguments.length`` to check 128 | for missing arguments, rather than checking for ``null``. 129 | It also checks that its arguments are numbers. 130 | 131 | To migrate to the new version: 132 | 133 | * ``slice(null) -> slice()`` 134 | * ``slice(5, null) -> slice(5)`` 135 | * ``slice(null, 5) -> slice(0, 5)`` 136 | * ``slice(null, null) -> slice()`` 137 | 138 | * ``List remove``: the index argument is now mandatory. 139 | 140 | This was done for consistency with the other ``remove`` 141 | methods. 142 | 143 | To migrate to the new version: 144 | 145 | * ``remove() -> remove(-1)`` 146 | 147 | * ``List insert``: swapped the arguments so that the index 148 | is now the first argument, with the value as the second 149 | argument. The first argument (index) is now mandatory. 150 | 151 | This was done for consistency with the other methods. 152 | 153 | To migrate to the new version: 154 | 155 | * ``insert(value) -> push(value)`` 156 | * ``insert(value, index) -> insert(index, value)`` 157 | 158 | * **New features** 159 | 160 | * ``List push`` is a faster version of ``List insert`` 161 | that only inserts at the end of the list. 162 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "Immutable", 4 | "version": "6.5.0", 5 | "repository": "https://github.com/Pauan/Immutable.git", 6 | "main": "build/Immutable.min.js", 7 | "devDependencies": { 8 | "es6-module-transpiler": "^0.9.6", 9 | "uglify-js": "^2.4.16", 10 | 11 | "benchmark": "1.0.0", 12 | "immutable": "3.6.2", 13 | "mori": "0.3.2", 14 | "microtime": "^1.2.0" 15 | }, 16 | "scripts": { 17 | "prepublish": "node ./build.js" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Benchmark.js: -------------------------------------------------------------------------------- 1 | import { group, message, run } from "./Benchmark/util"; 2 | import { list } from "./Benchmark/List"; 3 | import { record } from "./Benchmark/Record"; 4 | import { queue } from "./Benchmark/Queue"; 5 | import { tuple } from "./Benchmark/Tuple"; 6 | 7 | var package = require("../package.json"); 8 | 9 | var dependencies = package.devDependencies; 10 | 11 | function header() { 12 | group("Information", function () { 13 | group("Node.js", function () { 14 | message("URL: http://nodejs.org/"); 15 | message("Version: " + process.version); 16 | }); 17 | group("Benchmark.js", function () { 18 | message("URL: https://github.com/bestiejs/benchmark.js"); 19 | message("Version: " + dependencies.benchmark); 20 | }); 21 | group("Immutable-js", function () { 22 | message("URL: https://github.com/facebook/immutable-js"); 23 | message("Version: " + dependencies.immutable); 24 | }); 25 | group("Mori", function () { 26 | message("URL: https://github.com/swannodette/mori"); 27 | message("Version: " + dependencies.mori); 28 | }); 29 | group("Immutable", function () { 30 | message("URL: https://github.com/Pauan/Immutable"); 31 | message("Version: " + package.version); 32 | }); 33 | /*group("Elm", function () { 34 | message("URL: http://elm-lang.org/"); 35 | message("Version: 0.13"); 36 | });*/ 37 | }); 38 | } 39 | 40 | 41 | /*header(); 42 | list(5); 43 | list(10); 44 | list(100); 45 | list(1000);*/ 46 | 47 | header(); 48 | record(5); 49 | record(10); 50 | record(100); 51 | record(1000); 52 | record(10000); 53 | 54 | /*header(); 55 | queue(1); 56 | queue(10); 57 | queue(100); 58 | queue(1000); 59 | queue(10000);*/ 60 | 61 | /*header(); 62 | tuple(5); 63 | tuple(10); 64 | tuple(100); 65 | tuple(1000); 66 | tuple(10000);*/ 67 | 68 | run(); 69 | -------------------------------------------------------------------------------- /src/Benchmark/Array.js: -------------------------------------------------------------------------------- 1 | import { insert as insert_at, modify as modify_at, remove as remove_at, copy as array_copy } from "../Immutable/Array"; 2 | import { nth_has, nth_has_end } from "../Immutable/Ordered"; 3 | 4 | export { nth_has as array_has, array_copy }; 5 | 6 | export function array_get(array, i, def) { 7 | var len = array.length; 8 | 9 | if (i < 0) { 10 | i += len; 11 | } 12 | 13 | if (nth_has(i, len)) { 14 | return array[i]; 15 | } else if (arguments.length === 3) { 16 | return def; 17 | } else { 18 | throw new Error("Invalid index: " + i); 19 | } 20 | } 21 | 22 | export function array_insert(array, i, value) { 23 | var len = array.length; 24 | 25 | if (i < 0) { 26 | i += (len + 1); 27 | } 28 | 29 | if (nth_has_end(i, len)) { 30 | return insert_at(array, i, value); 31 | } else { 32 | throw new Error("Invalid index: " + i); 33 | } 34 | } 35 | 36 | export function array_modify(array, i, f) { 37 | var len = array.length; 38 | 39 | if (i < 0) { 40 | i += len; 41 | } 42 | 43 | if (nth_has(i, len)) { 44 | return modify_at(array, i, f); 45 | } else { 46 | throw new Error("Invalid index: " + i); 47 | } 48 | } 49 | 50 | export function array_remove(array, i) { 51 | var len = array.length; 52 | 53 | if (i < 0) { 54 | i += len; 55 | } 56 | 57 | if (nth_has(i, len)) { 58 | return remove_at(array, i); 59 | } else { 60 | throw new Error("Invalid index: " + i); 61 | } 62 | } 63 | 64 | export function array_slice(array, from, to) { 65 | var len = array.length; 66 | 67 | if (arguments.length < 2) { 68 | from = 0; 69 | } 70 | if (arguments.length < 3) { 71 | to = len; 72 | } 73 | 74 | if (from < 0) { 75 | from += len; 76 | } 77 | if (to < 0) { 78 | to += len; 79 | } 80 | 81 | if (from === 0 && to === len) { 82 | return array; 83 | 84 | } else if (from > to) { 85 | throw new Error("Index " + from + " is greater than index " + to); 86 | 87 | } else if (nth_has_end(from, len)) { 88 | if (from === to) { 89 | return []; 90 | 91 | } else if (nth_has_end(to, len)) { 92 | return array.slice(from, to); 93 | 94 | } else { 95 | throw new Error("Index " + to + " is not valid"); 96 | } 97 | 98 | } else { 99 | throw new Error("Index " + from + " is not valid"); 100 | } 101 | } 102 | 103 | export function array_concat(array, other) { 104 | if (array.length === 0) { 105 | return other; 106 | } else if (other.length === 0) { 107 | return array; 108 | } else { 109 | return array.concat(other); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/Benchmark/Queue.js: -------------------------------------------------------------------------------- 1 | var immutablejs = require("immutable"); 2 | var mori = require("mori"); 3 | var immutable = require("./Immutable.min.js"); 4 | 5 | import { group, message, time } from "./util"; 6 | import { insert as array_insert, remove as array_remove, copy as array_copy } from "../Immutable/Array"; 7 | 8 | function array_get(array, i) { 9 | return array[i]; 10 | } 11 | 12 | export function queue(counter) { 13 | var values = []; 14 | 15 | for (var i = 0; i < counter; ++i) { 16 | values.push(i); 17 | } 18 | 19 | group("Queue with " + counter + " values", function () { 20 | group("Creating", function () { 21 | time("JavaScript Array", function () { 22 | var values = []; 23 | 24 | for (var i = 0; i < counter; ++i) { 25 | values.push(i); 26 | } 27 | }); 28 | 29 | time("JavaScript Array Copying", function () { 30 | array_copy(values); 31 | }); 32 | 33 | time("Immutable-js List", function () { 34 | immutablejs.List(values); 35 | }); 36 | 37 | time("Mori Queue", function () { 38 | mori.queue.apply(null, values); 39 | }); 40 | 41 | time("Immutable List", function () { 42 | immutable.List(values); 43 | }); 44 | 45 | time("Immutable Queue", function () { 46 | immutable.Queue(values); 47 | }); 48 | }); 49 | 50 | 51 | group("Drain", function () { 52 | message("JavaScript Array"); 53 | 54 | time("JavaScript Array Copying", function () { 55 | var b = values; 56 | while (b.length) { 57 | array_get(b, 0); 58 | b = array_remove(b, 0); 59 | } 60 | }); 61 | 62 | ;(function () { 63 | var a = immutablejs.List(values); 64 | 65 | time("Immutable-js List", function () { 66 | var b = a; 67 | while (b.size) { 68 | b.get(0); 69 | b = b.shift(); 70 | } 71 | }); 72 | })(); 73 | 74 | ;(function () { 75 | var a = mori.queue.apply(null, values); 76 | 77 | time("Mori Queue", function () { 78 | var b = a; 79 | while (!mori.isEmpty(b)) { 80 | mori.peek(b); 81 | b = mori.pop(b); 82 | } 83 | }); 84 | })(); 85 | 86 | ;(function () { 87 | var a = immutable.List(values); 88 | 89 | time("Immutable List", function () { 90 | var b = a; 91 | while (!b.isEmpty()) { 92 | b.get(0); 93 | b = b.remove(0); 94 | } 95 | }); 96 | })(); 97 | 98 | ;(function () { 99 | var a = immutable.Queue(values); 100 | 101 | time("Immutable Queue", function () { 102 | var b = a; 103 | while (!b.isEmpty()) { 104 | b.peek(); 105 | b = b.pop(); 106 | } 107 | }); 108 | })(); 109 | }); 110 | 111 | 112 | /*group("Peek left", function () { 113 | ;(function () { 114 | var a = values; 115 | 116 | time("JavaScript Array", function () { 117 | a[0]; 118 | }); 119 | 120 | time("JavaScript Array Copying", function () { 121 | array_get(a, 0); 122 | }); 123 | })(); 124 | 125 | ;(function () { 126 | var a = immutablejs.List(values); 127 | 128 | time("Immutable-js List (first)", function () { 129 | a.first(); 130 | }); 131 | 132 | time("Immutable-js List (get)", function () { 133 | a.get(0); 134 | }); 135 | })(); 136 | 137 | ;(function () { 138 | var a = mori.queue.apply(null, values); 139 | 140 | time("Mori Queue", function () { 141 | mori.peek(a); 142 | }); 143 | })(); 144 | 145 | ;(function () { 146 | var a = immutable.List(values); 147 | 148 | time("Immutable List", function () { 149 | a.get(0); 150 | }); 151 | })(); 152 | 153 | ;(function () { 154 | var a = immutable.Queue(values); 155 | 156 | time("Immutable Queue", function () { 157 | a.peek(); 158 | }); 159 | })(); 160 | }); 161 | 162 | 163 | group("Pop left", function () { 164 | ;(function () { 165 | var a = values; 166 | 167 | message("JavaScript Array"); 168 | 169 | time("JavaScript Array Copying", function () { 170 | array_remove(a, 0); 171 | }); 172 | })(); 173 | 174 | ;(function () { 175 | var a = immutablejs.List(values); 176 | 177 | time("Immutable-js List (shift)", function () { 178 | a.shift(); 179 | }); 180 | 181 | time("Immutable-js List (delete)", function () { 182 | a.delete(0); 183 | }); 184 | 185 | time("Immutable-js List (rest)", function () { 186 | a.rest(); 187 | }); 188 | })(); 189 | 190 | ;(function () { 191 | var a = mori.queue.apply(null, values); 192 | 193 | time("Mori Queue (pop)", function () { 194 | mori.pop(a); 195 | }); 196 | 197 | time("Mori Queue (rest)", function () { 198 | mori.rest(a); 199 | }); 200 | })(); 201 | 202 | ;(function () { 203 | var a = immutable.List(values); 204 | 205 | time("Immutable List", function () { 206 | a.remove(0); 207 | }); 208 | })(); 209 | 210 | ;(function () { 211 | var a = immutable.Queue(values); 212 | 213 | time("Immutable Queue", function () { 214 | a.pop(); 215 | }); 216 | })(); 217 | }); 218 | 219 | 220 | group("Pop left all", function () { 221 | ;(function () { 222 | var a = values; 223 | 224 | message("JavaScript Array"); 225 | 226 | time("JavaScript Array Copying", function () { 227 | var b = a; 228 | while (b.length) { 229 | b = array_remove(b, 0); 230 | } 231 | }); 232 | })(); 233 | 234 | ;(function () { 235 | var a = immutablejs.List(values); 236 | 237 | time("Immutable-js List (shift)", function () { 238 | var b = a; 239 | while (b.size) { 240 | b = b.shift(); 241 | } 242 | }); 243 | 244 | time("Immutable-js List (delete)", function () { 245 | var b = a; 246 | while (b.size) { 247 | b = b.delete(0); 248 | } 249 | }); 250 | 251 | time("Immutable-js List (rest)", function () { 252 | var b = a; 253 | while (b.size) { 254 | b = b.rest(); 255 | } 256 | }); 257 | })(); 258 | 259 | ;(function () { 260 | var a = mori.queue.apply(null, values); 261 | 262 | time("Mori Queue (pop)", function () { 263 | var b = a; 264 | while (!mori.isEmpty(b)) { 265 | b = mori.pop(b); 266 | } 267 | }); 268 | 269 | time("Mori Queue (rest)", function () { 270 | var b = a; 271 | while (!mori.isEmpty(b)) { 272 | b = mori.rest(b); 273 | } 274 | }); 275 | })(); 276 | 277 | ;(function () { 278 | var a = immutable.List(values); 279 | 280 | time("Immutable List", function () { 281 | var b = a; 282 | while (!b.isEmpty()) { 283 | b = b.remove(0); 284 | } 285 | }); 286 | })(); 287 | 288 | ;(function () { 289 | var a = immutable.Queue(values); 290 | 291 | time("Immutable Queue", function () { 292 | var b = a; 293 | while (!b.isEmpty()) { 294 | b = b.pop(); 295 | } 296 | }); 297 | })(); 298 | }); 299 | 300 | 301 | group("Push right", function () { 302 | ;(function () { 303 | var a = values; 304 | 305 | message("JavaScript Array"); 306 | 307 | time("JavaScript Array Copying", function () { 308 | array_insert(a, a.length, 50); 309 | }); 310 | })(); 311 | 312 | ;(function () { 313 | var a = immutablejs.List(values); 314 | 315 | time("Immutable-js List (push)", function () { 316 | a.push(50); 317 | }); 318 | 319 | time("Immutable-js List (set)", function () { 320 | a.set(a.size, 50); 321 | }); 322 | })(); 323 | 324 | ;(function () { 325 | var a = mori.queue.apply(null, values); 326 | 327 | time("Mori Queue", function () { 328 | mori.conj.f2(a, 50); 329 | }); 330 | })(); 331 | 332 | ;(function () { 333 | var a = immutable.List(values); 334 | 335 | time("Immutable List", function () { 336 | a.push(50); 337 | }); 338 | })(); 339 | 340 | ;(function () { 341 | var a = immutable.Queue(values); 342 | 343 | time("Immutable Queue", function () { 344 | a.push(50); 345 | }); 346 | })(); 347 | }); 348 | 349 | 350 | group("Push right all", function () { 351 | ;(function () { 352 | var a = []; 353 | 354 | message("JavaScript Array"); 355 | 356 | time("JavaScript Array Copying", function () { 357 | var b = a; 358 | for (var i = 0; i < counter; ++i) { 359 | b = array_insert(b, b.length, i); 360 | } 361 | }); 362 | })(); 363 | 364 | ;(function () { 365 | var a = immutablejs.List(); 366 | 367 | time("Immutable-js List (push)", function () { 368 | var b = a; 369 | for (var i = 0; i < counter; ++i) { 370 | b = b.push(i); 371 | } 372 | }); 373 | 374 | time("Immutable-js List (set)", function () { 375 | var b = a; 376 | for (var i = 0; i < counter; ++i) { 377 | b = b.set(b.size, i); 378 | } 379 | }); 380 | })(); 381 | 382 | ;(function () { 383 | var a = mori.queue(); 384 | 385 | time("Mori Queue", function () { 386 | var b = a; 387 | for (var i = 0; i < counter; ++i) { 388 | b = mori.conj.f2(b, i); 389 | } 390 | }); 391 | })(); 392 | 393 | ;(function () { 394 | var a = immutable.List(); 395 | 396 | time("Immutable List", function () { 397 | var b = a; 398 | for (var i = 0; i < counter; ++i) { 399 | b = b.push(i); 400 | } 401 | }); 402 | })(); 403 | 404 | ;(function () { 405 | var a = immutable.Queue(); 406 | 407 | time("Immutable Queue", function () { 408 | var b = a; 409 | for (var i = 0; i < counter; ++i) { 410 | b = b.push(i); 411 | } 412 | }); 413 | })(); 414 | });*/ 415 | }); 416 | } 417 | -------------------------------------------------------------------------------- /src/Benchmark/Record.js: -------------------------------------------------------------------------------- 1 | var immutablejs = require("immutable"); 2 | var mori = require("mori"); 3 | var immutable = require("./Immutable.min.js"); 4 | 5 | import { group, message, time } from "./util"; 6 | 7 | function copy(input) { 8 | var output = {}; 9 | 10 | var a = Object.keys(input); 11 | for (var i = 0, l = a.length; i < l; ++i) { 12 | var key = a[i]; 13 | output[key] = input[key]; 14 | } 15 | 16 | return output; 17 | } 18 | 19 | function random(input) { 20 | return input[Math.floor(Math.random() * input.length)]; 21 | } 22 | 23 | export function record(counter) { 24 | var only_keys = []; 25 | var object_keys = {}; 26 | var record_keys = []; 27 | var mori_keys = []; 28 | 29 | for (var i = 0; i < counter; ++i) { 30 | only_keys.push("foo" + i); 31 | object_keys["foo" + i] = i; 32 | record_keys.push(["foo" + i, i]); 33 | mori_keys.push("foo" + i, i); 34 | } 35 | 36 | var eval_copy = new Function("obj", "return{" + only_keys.map(function (key) { 37 | return key + ":obj." + key; 38 | }).join(",") + "};"); 39 | 40 | var constructor = new Function("obj", only_keys.map(function (key) { 41 | return "this." + key + "=obj." + key + ";"; 42 | }).join("")); 43 | 44 | var ImmutableJSRecord = immutablejs.Record(object_keys); 45 | 46 | group("Record with " + counter + " keys", function () { 47 | group("Creating", function () { 48 | time("JavaScript Object", function () { 49 | var o = {}; 50 | 51 | for (var i = 0; i < counter; ++i) { 52 | o["foo" + i] = i; 53 | } 54 | }); 55 | 56 | time("JavaScript Object Copying", function () { 57 | copy(object_keys); 58 | }); 59 | 60 | time("JavaScript Object Copying (eval)", function () { 61 | eval_copy(object_keys); 62 | }); 63 | 64 | time("JavaScript Object Copying (constructor)", function () { 65 | new constructor(object_keys); 66 | }); 67 | 68 | time("Immutable-js Map", function () { 69 | immutablejs.Map(record_keys); 70 | }); 71 | 72 | time("Immutable-js Record", function () { 73 | new ImmutableJSRecord(record_keys); 74 | }); 75 | 76 | time("Mori Hash Map", function () { 77 | mori.hashMap.apply(null, mori_keys); 78 | }); 79 | 80 | time("Mori Sorted Map", function () { 81 | mori.sortedMap.apply(null, mori_keys); 82 | }); 83 | 84 | time("Immutable Dict", function () { 85 | immutable.Dict(record_keys); 86 | }); 87 | 88 | time("Immutable SortedDict", function () { 89 | immutable.SortedDict(immutable.simpleSort, record_keys); 90 | }); 91 | 92 | time("Immutable Record", function () { 93 | immutable.Record(record_keys); 94 | }); 95 | }); 96 | 97 | 98 | group("Retrieving at random", function () { 99 | message("JavaScript Object"); 100 | 101 | ;(function () { 102 | var o = object_keys; 103 | 104 | var o_eval = eval_copy(o); 105 | 106 | var o_cons = new constructor(o); 107 | 108 | time("JavaScript Object Copying", function () { 109 | o[random(only_keys)]; 110 | }); 111 | 112 | time("JavaScript Object Copying (eval)", function () { 113 | o_eval[random(only_keys)]; 114 | }); 115 | 116 | time("JavaScript Object Copying (constructor)", function () { 117 | o_cons[random(only_keys)]; 118 | }); 119 | })(); 120 | 121 | ;(function () { 122 | var o = immutablejs.Map(record_keys); 123 | 124 | time("Immutable-js Map", function () { 125 | o.get(random(only_keys)); 126 | }); 127 | })(); 128 | 129 | ;(function () { 130 | var o = new ImmutableJSRecord(record_keys); 131 | 132 | time("Immutable-js Record", function () { 133 | o.get(random(only_keys)); 134 | }); 135 | })(); 136 | 137 | ;(function () { 138 | var o = mori.hashMap.apply(null, mori_keys); 139 | 140 | time("Mori Hash Map", function () { 141 | mori.get.f2(o, random(only_keys)); 142 | }); 143 | })(); 144 | 145 | ;(function () { 146 | var o = mori.sortedMap.apply(null, mori_keys); 147 | 148 | time("Mori Sorted Map", function () { 149 | mori.get.f2(o, random(only_keys)); 150 | }); 151 | })(); 152 | 153 | ;(function () { 154 | var o = immutable.Dict(record_keys); 155 | 156 | time("Immutable Dict", function () { 157 | o.get(random(only_keys)); 158 | }); 159 | })(); 160 | 161 | ;(function () { 162 | var o = immutable.SortedDict(immutable.simpleSort, record_keys); 163 | 164 | time("Immutable SortedDict", function () { 165 | o.get(random(only_keys)); 166 | }); 167 | })(); 168 | 169 | ;(function () { 170 | var o = immutable.Record(record_keys); 171 | 172 | time("Immutable Record", function () { 173 | o.get(random(only_keys)); 174 | }); 175 | })(); 176 | }); 177 | 178 | 179 | group("Setting at random", function () { 180 | ;(function () { 181 | var o = copy(object_keys); 182 | 183 | time("JavaScript Object", function () { 184 | o[random(only_keys)] = -1; 185 | }); 186 | })(); 187 | 188 | ;(function () { 189 | var o = object_keys; 190 | 191 | var o_eval = eval_copy(o); 192 | 193 | var o_cons = new constructor(o); 194 | 195 | time("JavaScript Object Copying", function () { 196 | var x = copy(o); 197 | x[random(only_keys)] = -1; 198 | }); 199 | 200 | time("JavaScript Object Copying (eval)", function () { 201 | var x = eval_copy(o_eval); 202 | x[random(only_keys)] = -1; 203 | }); 204 | 205 | time("JavaScript Object Copying (constructor)", function () { 206 | var x = new constructor(o_cons); 207 | x[random(only_keys)] = -1; 208 | }); 209 | })(); 210 | 211 | ;(function () { 212 | var o = immutablejs.Map(record_keys); 213 | 214 | time("Immutable-js Map", function () { 215 | o.set(random(only_keys), -1); 216 | }); 217 | })(); 218 | 219 | ;(function () { 220 | var o = new ImmutableJSRecord(record_keys); 221 | 222 | time("Immutable-js Record", function () { 223 | o.set(random(only_keys), -1); 224 | }); 225 | })(); 226 | 227 | ;(function () { 228 | var o = mori.hashMap.apply(null, mori_keys); 229 | 230 | time("Mori Hash Map", function () { 231 | mori.assoc.f3(o, random(only_keys), -1); 232 | }); 233 | })(); 234 | 235 | ;(function () { 236 | var o = mori.sortedMap.apply(null, mori_keys); 237 | 238 | time("Mori Sorted Map", function () { 239 | mori.assoc.f3(o, random(only_keys), -1); 240 | }); 241 | })(); 242 | 243 | ;(function () { 244 | var o = immutable.Dict(record_keys); 245 | 246 | time("Immutable Dict", function () { 247 | o.set(random(only_keys), -1); 248 | }); 249 | })(); 250 | 251 | ;(function () { 252 | var o = immutable.SortedDict(immutable.simpleSort, record_keys); 253 | 254 | time("Immutable SortedDict", function () { 255 | o.set(random(only_keys), -1); 256 | }); 257 | })(); 258 | 259 | ;(function () { 260 | var o = immutable.Record(record_keys); 261 | 262 | time("Immutable Record", function () { 263 | o.set(random(only_keys), -1); 264 | }); 265 | })(); 266 | }); 267 | }); 268 | } 269 | -------------------------------------------------------------------------------- /src/Benchmark/Tuple.js: -------------------------------------------------------------------------------- 1 | var immutablejs = require("immutable"); 2 | var mori = require("mori"); 3 | var immutable = require("./Immutable.min.js"); 4 | 5 | import { group, message, time } from "./util"; 6 | import { nil } from "../Immutable/static"; 7 | import { Cons } from "../Immutable/Cons"; 8 | import { array_has, array_get, array_insert, array_modify, array_remove, array_slice, array_concat, array_copy } from "./Array"; 9 | 10 | function cons_push(x, i) { 11 | return new Cons(i, x); 12 | } 13 | 14 | function random(max) { 15 | return Math.floor(Math.random() * max); 16 | } 17 | 18 | export function tuple(counter) { 19 | var values = []; 20 | 21 | for (var i = 0; i < counter; ++i) { 22 | values.push(i); 23 | } 24 | 25 | group("Tuple with " + counter + " values", function () { 26 | group("Creating", function () { 27 | time("JavaScript Array", function () { 28 | var a = []; 29 | 30 | for (var i = 0; i < counter; ++i) { 31 | a.push(i); 32 | } 33 | }); 34 | 35 | time("JavaScript Array Copying", function () { 36 | array_copy(values); 37 | }); 38 | 39 | time("Immutable-js List", function () { 40 | immutablejs.List(values); 41 | }); 42 | 43 | time("Mori Vector", function () { 44 | mori.vector.apply(null, values); 45 | }); 46 | 47 | time("Mori Vector (into)", function () { 48 | mori.into.f2(mori.vector(), values); 49 | }); 50 | 51 | /*time("Mori List", function () { 52 | mori.list.apply(null, values); 53 | }); 54 | 55 | time("Mori Queue", function () { 56 | mori.queue.apply(null, values); 57 | });*/ 58 | 59 | time("Immutable List", function () { 60 | immutable.List(values); 61 | }); 62 | 63 | time("Immutable Tuple", function () { 64 | immutable.Tuple(values); 65 | }); 66 | 67 | /*time("Immutable Queue", function () { 68 | immutable.Queue(values); 69 | });*/ 70 | 71 | time("Immutable Stack", function () { 72 | immutable.Stack(values); 73 | }); 74 | 75 | time("Cons", function () { 76 | var a = nil; 77 | 78 | for (var i = 0; i < counter; ++i) { 79 | a = cons_push(a, i); 80 | } 81 | }); 82 | }); 83 | 84 | 85 | group("Retrieving at random", function () { 86 | ;(function () { 87 | var size = values.length; 88 | 89 | time("JavaScript Array", function () { 90 | values[random(size)]; 91 | }); 92 | 93 | time("JavaScript Array (error checking)", function () { 94 | array_get(values, random(size)); 95 | }); 96 | })(); 97 | 98 | ;(function () { 99 | var a = immutablejs.List(values); 100 | 101 | var size = a.size; 102 | 103 | time("Immutable-js List", function () { 104 | a.get(random(size)); 105 | }); 106 | })(); 107 | 108 | ;(function () { 109 | var a = mori.vector.apply(null, values); 110 | 111 | var size = mori.count(a); 112 | 113 | time("Mori Vector", function () { 114 | mori.nth.f2(a, random(size)); 115 | }); 116 | })(); 117 | 118 | ;(function () { 119 | var a = immutable.List(values); 120 | 121 | var size = a.size(); 122 | 123 | time("Immutable List", function () { 124 | a.get(random(size)); 125 | }); 126 | })(); 127 | 128 | ;(function () { 129 | var a = immutable.Tuple(values); 130 | 131 | var size = a.size(); 132 | 133 | time("Immutable Tuple", function () { 134 | a.get(random(size)); 135 | }); 136 | })(); 137 | }); 138 | 139 | 140 | group("Setting at random", function () { 141 | message("JavaScript Array"); 142 | 143 | ;(function () { 144 | var size = values.length; 145 | 146 | time("JavaScript Array Copying", function () { 147 | array_modify(values, random(size), function () { 148 | return -50; 149 | }); 150 | }); 151 | })(); 152 | 153 | ;(function () { 154 | var a = immutablejs.List(values); 155 | 156 | var size = a.size; 157 | 158 | time("Immutable-js List", function () { 159 | a.set(random(size), -50); 160 | }); 161 | })(); 162 | 163 | ;(function () { 164 | var a = mori.vector.apply(null, values); 165 | 166 | var size = mori.count(a); 167 | 168 | time("Mori Vector", function () { 169 | mori.assoc.f3(a, random(size), -50); 170 | }); 171 | })(); 172 | 173 | ;(function () { 174 | var a = immutable.List(values); 175 | 176 | var size = a.size(); 177 | 178 | time("Immutable List", function () { 179 | a.set(random(size), -50); 180 | }); 181 | })(); 182 | 183 | ;(function () { 184 | var a = immutable.Tuple(values); 185 | 186 | var size = a.size(); 187 | 188 | time("Immutable Tuple", function () { 189 | a.set(random(size), -50); 190 | }); 191 | })(); 192 | }); 193 | }); 194 | } 195 | -------------------------------------------------------------------------------- /src/Benchmark/util.js: -------------------------------------------------------------------------------- 1 | var Benchmark = require("benchmark"); 2 | 3 | var suite = new Benchmark.Suite({ 4 | // TODO is this correct ? 5 | onComplete: function () { 6 | display_messages(messages); 7 | }, 8 | onCycle: function (event) { 9 | var s = "" + event.target; 10 | s = s.replace(/^(.+) x ([0-9,\.]+) ops\/sec ±\d+\.\d+% \(\d+ runs sampled\)$/g, function (_, text, num) { 11 | if (/\./.test(num)) { 12 | num = "" + Math.round(+num); 13 | } 14 | return rpad(text, 49) + lpad(" " + lpad(num, 13) + " ops/sec", 21); 15 | }); 16 | console.log(s); 17 | } 18 | }); 19 | 20 | function repeat(i, x) { 21 | return new Array(i + 1).join(x); 22 | } 23 | 24 | function rpad(x, i) { 25 | return (x + repeat(i, " ")).slice(0, i); 26 | } 27 | 28 | function lpad(x, i) { 29 | return (repeat(i, " ") + x).slice(-i); 30 | } 31 | 32 | 33 | var indent = 0; 34 | var timers = []; 35 | var messages = []; 36 | 37 | function display_messages(a) { 38 | a.forEach(function (x) { 39 | console.log(x); 40 | }); 41 | } 42 | 43 | function add_timers() { 44 | var max = 0; 45 | 46 | timers.forEach(function (x) { 47 | max = Math.max(max, x.name.length); 48 | }); 49 | 50 | timers.forEach(function (x) { 51 | suite.add(repeat(indent, " ") + rpad(x.name, max), x.fn, { 52 | onStart: function () { 53 | display_messages(x.messages); 54 | } 55 | }); 56 | }); 57 | } 58 | 59 | export function group(name, f) { 60 | message(repeat(70 - indent, "-")); 61 | message(name + ":"); 62 | 63 | var old_indent = indent; 64 | var old_timers = timers; 65 | 66 | indent += 2; 67 | timers = []; 68 | 69 | try { 70 | f(); 71 | } finally { 72 | add_timers(); 73 | indent = old_indent; 74 | timers = old_timers; 75 | } 76 | } 77 | 78 | export function message(x) { 79 | if (arguments.length !== 1) { 80 | throw new Error("Expected 1 argument but got " + arguments.length); 81 | } 82 | messages.push(repeat(indent, " ") + x); 83 | } 84 | 85 | /*suite.on("complete", function () { 86 | var re = /([^\n]+)( x )([\d,]+)( ops\/sec ±[\d.]+% \(\d+ runs sampled\))/g; 87 | var s = this.join("\n"); 88 | var a; 89 | var out = []; 90 | var lMax = 0; 91 | var rMax = 0; 92 | while ((a = re.exec(s)) !== null) { 93 | lMax = Math.max(lMax, a[1].length); 94 | rMax = Math.max(rMax, a[3].length); 95 | out.push([a[1], a[2], a[3], a[4]]); 96 | } 97 | console.log(out.map(function (a) { 98 | return rpad(a[0], lMax) + a[1] + lpad(a[2], rMax) + a[3] 99 | }).join("\n")); 100 | });*/ 101 | 102 | export function time(s, f) { 103 | // This is to make sure that the function doesn't 104 | // have any errors before benchmarking it 105 | f(); 106 | 107 | var a = messages; 108 | messages = []; 109 | 110 | timers.push({ 111 | name: s, 112 | fn: f, 113 | messages: a 114 | }); 115 | } 116 | 117 | export function run() { 118 | add_timers(); 119 | suite.run(); 120 | } 121 | -------------------------------------------------------------------------------- /src/Immutable.js: -------------------------------------------------------------------------------- 1 | import { isObject } from "./Immutable/util"; 2 | 3 | import { equal } from "./Immutable/equal"; 4 | import { toJS, fromJS } from "./Immutable/toJS"; 5 | import { toJSON, fromJSON } from "./Immutable/toJSON"; 6 | import { simpleSort } from "./Immutable/Sorted"; 7 | import { SortedDict, Dict, isDict, isSortedDict } from "./Immutable/ImmutableDict"; 8 | import { SortedSet, Set, isSet, isSortedSet } from "./Immutable/ImmutableSet"; 9 | import { isList, List } from "./Immutable/ImmutableList"; 10 | import { isTuple, Tuple } from "./Immutable/ImmutableTuple"; 11 | import { isType, Type } from "./Immutable/ImmutableType"; 12 | import { isQueue, Queue } from "./Immutable/ImmutableQueue"; 13 | import { isStack, Stack } from "./Immutable/ImmutableStack"; 14 | import { isRecord, Record } from "./Immutable/ImmutableRecord"; 15 | import { deref, Ref, isRef } from "./Immutable/MutableRef"; 16 | import { isTag, isUUIDTag, Tag, UUIDTag } from "./Immutable/Tag"; 17 | import { each, map, keep, findIndex, reverse, foldl, 18 | foldr, join, zip, toArray, isIterable, 19 | any, all, find, partition, range, take, indexOf, 20 | toIterator, Iterable, repeat, skip } from "./Immutable/iter"; 21 | 22 | export { equal, toJS, fromJS, simpleSort, toJSON, fromJSON, 23 | SortedDict, Dict, isDict, isSortedDict, 24 | SortedSet, Set, isSet, isSortedSet, 25 | isList, List, isQueue, Queue, 26 | isTuple, Tuple, 27 | isStack, Stack, isRecord, Record, 28 | deref, Ref, isRef, 29 | isTag, isUUIDTag, Tag, UUIDTag, 30 | each, map, keep, findIndex, reverse, foldl, 31 | foldr, join, zip, toArray, isIterable, any, all, 32 | find, partition, range, take, indexOf, 33 | toIterator, Iterable, repeat, skip, 34 | isType, Type }; 35 | 36 | // TODO use `x instanceof ImmutableBase` ? What about ImmutableRef ? 37 | export function isImmutable(x) { 38 | if (isObject(x)) { 39 | return Object.isFrozen(x) || 40 | isDict(x) || 41 | isSet(x) || 42 | isList(x) || 43 | isTuple(x) || 44 | isQueue(x) || 45 | isStack(x) || 46 | isRecord(x) || 47 | isType(x); 48 | // TODO just return true? are there any mutable value types? 49 | } else { 50 | var type = typeof x; 51 | // Tags are currently implemented with strings 52 | return type === "string" || 53 | type === "number" || 54 | type === "boolean" || 55 | type === "symbol" || 56 | x == null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Immutable/AVL.js: -------------------------------------------------------------------------------- 1 | // http://arclanguage.org/item?id=14181 2 | // http://arclanguage.org/item?id=18936 3 | 4 | import { nil } from "./static"; 5 | 6 | // Faster than using Math.max 7 | export function max(x, y) { 8 | if (x > y) { 9 | return x; 10 | } else { 11 | return y; 12 | } 13 | } 14 | 15 | export function balanced_node(node, left, right) { 16 | var l_depth = left.depth; 17 | var r_depth = right.depth; 18 | 19 | // Left side is deeper 20 | if (l_depth > r_depth + 1) { 21 | var lleft = left.left; 22 | var lright = left.right; 23 | 24 | // Right side is deeper 25 | if (lright.depth > lleft.depth) { 26 | // Left rotate -> Right rotate 27 | return lright.copy(left.copy(lleft, lright.left), 28 | node.copy(lright.right, right)); 29 | 30 | // Left side is deeper 31 | } else { 32 | // Right rotate 33 | return left.copy(lleft, node.copy(lright, right)); 34 | } 35 | 36 | // Right side is deeper 37 | } else if (r_depth > l_depth + 1) { 38 | var rright = right.right; 39 | var rleft = right.left; 40 | 41 | // Left side is deeper 42 | if (rleft.depth > rright.depth) { 43 | // Right rotate -> Left rotate 44 | return rleft.copy(node.copy(left, rleft.left), 45 | right.copy(rleft.right, rright)); 46 | 47 | // Right side is deeper 48 | } else { 49 | // Left rotate 50 | return right.copy(node.copy(left, rleft), rright); 51 | } 52 | 53 | // No balancing needed 54 | } else { 55 | return node.copy(left, right); 56 | } 57 | } 58 | 59 | export function concat(x, y) { 60 | if (x === nil) { 61 | return y; 62 | 63 | } else if (y === nil) { 64 | return x; 65 | 66 | // TODO what if the depths are the same? 67 | } else if (x.depth < y.depth) { 68 | var left = concat(x, y.left); 69 | return balanced_node(y, left, y.right); 70 | 71 | } else { 72 | var right = concat(x.right, y); 73 | return balanced_node(x, x.left, right); 74 | } 75 | } 76 | 77 | export function insert_min(node, new_node) { 78 | if (node === nil) { 79 | return new_node; 80 | } else { 81 | // TODO do we need to use balanced_node ? 82 | return balanced_node(node, insert_min(node.left, new_node), node.right); 83 | } 84 | } 85 | 86 | export function insert_max(node, new_node) { 87 | if (node === nil) { 88 | return new_node; 89 | } else { 90 | // TODO do we need to use balanced_node ? 91 | return balanced_node(node, node.left, insert_max(node.right, new_node)); 92 | } 93 | } 94 | 95 | // http://n00tc0d3r.blogspot.com/2013/08/implement-iterator-for-binarytree-i-in.html 96 | export function iter_tree(node) { 97 | var parents = []; 98 | 99 | while (node !== nil) { 100 | parents.push(node); 101 | node = node.left; 102 | } 103 | 104 | return { 105 | next: function () { 106 | if (parents.length) { 107 | var parent = parents.pop(); 108 | 109 | node = parent.right; 110 | 111 | while (node !== nil) { 112 | parents.push(node); 113 | node = node.left; 114 | } 115 | 116 | return { value: parent }; 117 | } else { 118 | return { done: true }; 119 | } 120 | } 121 | }; 122 | } 123 | -------------------------------------------------------------------------------- /src/Immutable/Array.js: -------------------------------------------------------------------------------- 1 | export function copy(array) { 2 | var len = array.length; 3 | var out = new Array(len); 4 | 5 | for (var i = 0; i < len; ++i) { 6 | out[i] = array[i]; 7 | } 8 | 9 | return out; 10 | } 11 | 12 | export function insert(array, index, value) { 13 | var len = array.length + 1; 14 | 15 | var out = new Array(len); 16 | 17 | var i = 0; 18 | while (i < index) { 19 | out[i] = array[i]; 20 | ++i; 21 | } 22 | 23 | out[i] = value; 24 | ++i; 25 | 26 | while (i < len) { 27 | out[i] = array[i - 1]; 28 | ++i; 29 | } 30 | 31 | return out; 32 | } 33 | 34 | export function modify(array, index, f) { 35 | var old_value = array[index]; 36 | var new_value = f(old_value); 37 | 38 | if (old_value === new_value) { 39 | return array; 40 | 41 | } else { 42 | var new_array = copy(array); 43 | new_array[index] = new_value; 44 | return new_array; 45 | } 46 | } 47 | 48 | export function remove(array, index) { 49 | var len = array.length - 1; 50 | 51 | var out = new Array(len); 52 | 53 | var i = 0; 54 | while (i < index) { 55 | out[i] = array[i]; 56 | ++i; 57 | } 58 | 59 | while (i < len) { 60 | out[i] = array[i + 1]; 61 | ++i; 62 | } 63 | 64 | return out; 65 | } 66 | -------------------------------------------------------------------------------- /src/Immutable/Base.js: -------------------------------------------------------------------------------- 1 | import { hash } from "./hash"; 2 | import { toIterator } from "./iter"; 3 | import { Symbol_iterator } from "./Tag"; 4 | 5 | // TODO circular import ? 6 | import { toJSON } from "./toJSON"; 7 | 8 | export var MutableBase = {}; 9 | export var ImmutableBase = {}; 10 | 11 | function toString() { 12 | return hash(this); 13 | } 14 | 15 | // TODO Infinite cycle detection ? 16 | function _toJSON() { 17 | return toJSON(this); 18 | } 19 | 20 | MutableBase.toString = ImmutableBase.toString = toString; 21 | MutableBase.inspect = ImmutableBase.inspect = toString; 22 | 23 | // Mutable things cannot be converted to JSON 24 | ImmutableBase.toJSON = _toJSON; 25 | 26 | if (Symbol_iterator !== null) { 27 | MutableBase[Symbol_iterator] = ImmutableBase[Symbol_iterator] = function () { 28 | return toIterator(this); 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/Immutable/Cons.js: -------------------------------------------------------------------------------- 1 | import { nil } from "./static"; 2 | 3 | export function Cons(car, cdr) { 4 | this.car = car; 5 | this.cdr = cdr; 6 | } 7 | 8 | export function iter_cons(x) { 9 | return { 10 | next: function () { 11 | if (x === nil) { 12 | return { done: true }; 13 | } else { 14 | var value = x.car; 15 | x = x.cdr; 16 | return { value: value }; 17 | } 18 | } 19 | }; 20 | } 21 | 22 | export function each_cons(x, f) { 23 | while (x !== nil) { 24 | f(x.car); 25 | x = x.cdr; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableDict.js: -------------------------------------------------------------------------------- 1 | import { max, iter_tree } from "./AVL"; 2 | import { simpleSort, sorted_isEmpty, sorted_has, key_get, key_set, key_modify, 3 | sorted_remove, sorted_merge } from "./Sorted"; 4 | import { hash, hash_dict } from "./hash"; 5 | import { toJS_object } from "./toJS"; 6 | import { toJSON_object, fromJSON_object } from "./toJSON"; 7 | import { unsafe_Tuple } from "./ImmutableTuple"; 8 | import { ImmutableBase } from "./Base"; 9 | import { map_iter } from "./iter"; 10 | import { identity } from "./util"; 11 | import { nil, tag_hash, tag_toJS, tag_toJSON, fromJSON_registry, tag_iter } from "./static"; 12 | 13 | 14 | function KeyNode(left, right, hash, key, value) { 15 | this.left = left; 16 | this.right = right; 17 | this.hash = hash; 18 | this.key = key; 19 | this.value = value; 20 | this.depth = max(left.depth, right.depth) + 1; 21 | } 22 | 23 | KeyNode.prototype.copy = function (left, right) { 24 | return new KeyNode(left, right, this.hash, this.key, this.value); 25 | }; 26 | 27 | KeyNode.prototype.modify = function (info) { 28 | var hash = info.hash; 29 | var key = info.key; 30 | var value = info.value; 31 | // We don't use equal, for increased speed 32 | if (this.hash === hash && this.key === key && this.value === value) { 33 | return this; 34 | } else { 35 | return new KeyNode(this.left, this.right, hash, key, value); 36 | } 37 | }; 38 | 39 | 40 | export function ImmutableDict(root, sort, hash_fn) { 41 | this.root = root; 42 | this.sort = sort; 43 | this.hash_fn = hash_fn; 44 | this.hash = null; 45 | } 46 | 47 | ImmutableDict.prototype = Object.create(ImmutableBase); 48 | 49 | ImmutableDict.prototype[tag_toJS] = toJS_object; 50 | ImmutableDict.prototype.isEmpty = sorted_isEmpty; 51 | ImmutableDict.prototype.has = sorted_has; 52 | ImmutableDict.prototype.remove = sorted_remove(ImmutableDict); 53 | ImmutableDict.prototype.merge = sorted_merge; 54 | 55 | ImmutableDict.prototype[tag_iter] = function () { 56 | return map_iter(iter_tree(this.root), function (node) { 57 | return unsafe_Tuple([node.key, node.value]); 58 | }); 59 | }; 60 | 61 | ImmutableDict.prototype[tag_hash] = function (x) { 62 | if (x.hash === null) { 63 | // We don't use equal, for increased speed 64 | if (isDict(x) && !isSortedDict(x)) { 65 | x.hash = "(Dict" + hash_dict(x, " ") + ")"; 66 | } else { 67 | x.hash = "(SortedDict " + hash(x.sort) + hash_dict(x, " ") + ")"; 68 | } 69 | } 70 | 71 | return x.hash; 72 | }; 73 | 74 | fromJSON_registry["Dict"] = function (x) { 75 | return Dict(fromJSON_object(x)); 76 | }; 77 | 78 | ImmutableDict.prototype[tag_toJSON] = function (x) { 79 | if (isDict(x) && !isSortedDict(x)) { 80 | return toJSON_object("Dict", x); 81 | } else { 82 | throw new Error("Cannot convert SortedDict to JSON"); 83 | } 84 | }; 85 | 86 | ImmutableDict.prototype.removeAll = function () { 87 | return new ImmutableDict(nil, this.sort, this.hash_fn); 88 | }; 89 | 90 | ImmutableDict.prototype.get = function (key, def) { 91 | var node = key_get(this.root, this.sort, this.hash_fn(key)); 92 | if (node === nil) { 93 | if (arguments.length === 2) { 94 | return def; 95 | } else { 96 | throw new Error("Key " + key + " not found"); 97 | } 98 | } else { 99 | return node.value; 100 | } 101 | }; 102 | 103 | // TODO code duplication 104 | // TODO what if `sort` suspends ? 105 | ImmutableDict.prototype.set = function (key, value) { 106 | var root = this.root; 107 | var sort = this.sort; 108 | var hash_fn = this.hash_fn; 109 | var hash = hash_fn(key); 110 | var node = key_set(root, sort, hash, new KeyNode(nil, nil, hash, key, value)); 111 | if (node === root) { 112 | return this; 113 | } else { 114 | return new ImmutableDict(node, sort, hash_fn); 115 | } 116 | }; 117 | 118 | ImmutableDict.prototype.modify = function (key, f) { 119 | var root = this.root; 120 | var sort = this.sort; 121 | var hash_fn = this.hash_fn; 122 | var node = key_modify(root, sort, hash_fn(key), key, f); 123 | if (node === root) { 124 | return this; 125 | } else { 126 | return new ImmutableDict(node, sort, hash_fn); 127 | } 128 | }; 129 | 130 | 131 | export function isDict(x) { 132 | return x instanceof ImmutableDict; 133 | } 134 | 135 | export function isSortedDict(x) { 136 | return isDict(x) && x.hash_fn === identity; 137 | } 138 | 139 | export function SortedDict(sort, obj) { 140 | if (arguments.length === 1) { 141 | return new ImmutableDict(nil, sort, identity); 142 | 143 | } else if (arguments.length === 2) { 144 | // We don't use equal, for increased speed 145 | if (isSortedDict(obj) && obj.sort === sort) { 146 | return obj; 147 | } else { 148 | return new ImmutableDict(nil, sort, identity).merge(obj); 149 | } 150 | 151 | } else { 152 | throw new Error("Expected 1 to 2 arguments but got " + arguments.length); 153 | } 154 | } 155 | 156 | export function Dict(obj) { 157 | if (arguments.length === 0) { 158 | return new ImmutableDict(nil, simpleSort, hash); 159 | 160 | } else if (arguments.length === 1) { 161 | if (isDict(obj) && !isSortedDict(obj)) { 162 | return obj; 163 | } else { 164 | return new ImmutableDict(nil, simpleSort, hash).merge(obj); 165 | } 166 | 167 | } else { 168 | throw new Error("Expected 0 to 1 arguments but got " + arguments.length); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableQueue.js: -------------------------------------------------------------------------------- 1 | import { toJSON_array, fromJSON_array } from "./toJSON"; 2 | import { toJS_array } from "./toJS"; 3 | import { hash_array } from "./hash"; 4 | import { Cons, iter_cons, each_cons } from "./Cons"; 5 | import { concat_iter, reverse_iter } from "./iter"; 6 | import { ImmutableBase } from "./Base"; 7 | import { stack_size, stack_concat } from "./Sorted"; 8 | import { nil, tag_hash, tag_toJSON, fromJSON_registry, tag_toJS, tag_iter } from "./static"; 9 | 10 | export function ImmutableQueue(left, right, len) { 11 | this.left = left; 12 | this.right = right; 13 | this.len = len; 14 | this.hash = null; 15 | } 16 | 17 | ImmutableQueue.prototype = Object.create(ImmutableBase); 18 | 19 | ImmutableQueue.prototype[tag_toJS] = toJS_array; 20 | ImmutableQueue.prototype[tag_hash] = hash_array("Queue"); 21 | ImmutableQueue.prototype.size = stack_size; 22 | ImmutableQueue.prototype.concat = stack_concat; 23 | 24 | fromJSON_registry["Queue"] = function (x) { 25 | return Queue(fromJSON_array(x)); 26 | }; 27 | 28 | ImmutableQueue.prototype[tag_toJSON] = function (x) { 29 | return toJSON_array("Queue", x); 30 | }; 31 | 32 | ImmutableQueue.prototype.isEmpty = function () { 33 | return this.left === nil && this.right === nil; 34 | }; 35 | 36 | ImmutableQueue.prototype.removeAll = function () { 37 | return new ImmutableQueue(nil, nil, 0); 38 | }; 39 | 40 | ImmutableQueue.prototype[tag_iter] = function () { 41 | return concat_iter(iter_cons(this.left), reverse_iter(iter_cons(this.right))); 42 | }; 43 | 44 | ImmutableQueue.prototype.peek = function (def) { 45 | var left = this.left; 46 | if (left === nil) { 47 | if (arguments.length === 1) { 48 | return def; 49 | } else { 50 | throw new Error("Cannot peek from an empty queue"); 51 | } 52 | } else { 53 | return left.car; 54 | } 55 | }; 56 | 57 | ImmutableQueue.prototype.push = function (value) { 58 | var left = this.left; 59 | var right = this.right; 60 | 61 | // Pushing into a queue with 0 values in it 62 | if (left === nil && right === nil) { 63 | return new ImmutableQueue(new Cons(value, left), right, this.len + 1); 64 | 65 | // Pushing into a queue with 1+ values in it 66 | } else { 67 | return new ImmutableQueue(left, new Cons(value, right), this.len + 1); 68 | } 69 | }; 70 | 71 | ImmutableQueue.prototype.pop = function () { 72 | var left = this.left; 73 | var right = this.right; 74 | 75 | if (left === nil) { 76 | throw new Error("Cannot pop from an empty queue"); 77 | 78 | } else { 79 | left = left.cdr; 80 | 81 | if (left === nil && right !== nil) { 82 | // TODO a little gross 83 | // TODO replace with foldl ? 84 | each_cons(right, function (x) { 85 | left = new Cons(x, left); 86 | }); 87 | 88 | right = nil; 89 | } 90 | } 91 | 92 | return new ImmutableQueue(left, right, this.len - 1); 93 | }; 94 | 95 | 96 | export function isQueue(x) { 97 | return x instanceof ImmutableQueue; 98 | } 99 | 100 | export function Queue(x) { 101 | if (arguments.length === 0) { 102 | return new ImmutableQueue(nil, nil, 0); 103 | } else { 104 | if (x instanceof ImmutableQueue) { 105 | return x; 106 | } else { 107 | return new ImmutableQueue(nil, nil, 0).concat(x); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableRecord.js: -------------------------------------------------------------------------------- 1 | import { modify as array_modify } from "./Array"; 2 | import { hash, hash_dict } from "./hash"; 3 | import { toJSON_object, fromJSON_object } from "./toJSON"; 4 | import { toJS_object } from "./toJS"; 5 | import { ImmutableBase } from "./Base"; 6 | import { unsafe_Tuple } from "./ImmutableTuple"; 7 | import { iter_object, map, toIterator, each, toArray } from "./iter"; 8 | import { destructure_pair } from "./util"; 9 | import { simpleSort, sorted_merge } from "./Sorted"; 10 | import { tag_hash, tag_toJSON, fromJSON_registry, tag_toJS, tag_iter } from "./static"; 11 | 12 | function checkKey(key) { 13 | // Tags are currently implemented as strings 14 | if (typeof key !== "string") { 15 | throw new Error("Expected key to be a string or Tag but got " + key); 16 | } 17 | } 18 | 19 | export function ImmutableRecord(keys, values) { 20 | this.keys = keys; 21 | this.values = values; 22 | this.hash = null; 23 | } 24 | 25 | ImmutableRecord.prototype = Object.create(ImmutableBase); 26 | 27 | ImmutableRecord.prototype.update = sorted_merge; 28 | ImmutableRecord.prototype[tag_toJS] = toJS_object; 29 | 30 | fromJSON_registry["Record"] = function (x) { 31 | return Record(fromJSON_object(x)); 32 | }; 33 | 34 | ImmutableRecord.prototype[tag_toJSON] = function (x) { 35 | return toJSON_object("Record", x); 36 | }; 37 | 38 | ImmutableRecord.prototype[tag_hash] = function (x) { 39 | if (x.hash === null) { 40 | x.hash = "(Record" + hash_dict(x, " ") + ")"; 41 | } 42 | 43 | return x.hash; 44 | }; 45 | 46 | ImmutableRecord.prototype[tag_iter] = function () { 47 | var keys = this.keys; 48 | var values = this.values; 49 | 50 | // TODO a little gross 51 | return toIterator(map(iter_object(keys), function (_array) { 52 | // TODO should this use destructure_pair ? 53 | return destructure_pair(_array, function (s, index) { 54 | return unsafe_Tuple([s, values[index]]); 55 | }); 56 | })); 57 | }; 58 | 59 | ImmutableRecord.prototype.has = function (key) { 60 | checkKey(key); 61 | 62 | return this.keys[key] != null; 63 | }; 64 | 65 | ImmutableRecord.prototype.isEmpty = function () { 66 | return this.values.length === 0; 67 | }; 68 | 69 | ImmutableRecord.prototype.get = function (key, def) { 70 | checkKey(key); 71 | 72 | var index = this.keys[key]; 73 | if (index == null) { 74 | if (arguments.length === 2) { 75 | return def; 76 | } else { 77 | throw new Error("Key " + key + " not found"); 78 | } 79 | 80 | } else { 81 | return this.values[index]; 82 | } 83 | }; 84 | 85 | ImmutableRecord.prototype.set = function (key, value) { 86 | return this.modify(key, function () { 87 | return value; 88 | }); 89 | }; 90 | 91 | ImmutableRecord.prototype.modify = function (key, f) { 92 | checkKey(key); 93 | 94 | var keys = this.keys; 95 | var index = keys[key]; 96 | if (index == null) { 97 | throw new Error("Key " + key + " not found"); 98 | 99 | } else { 100 | var values = this.values; 101 | var array = array_modify(values, index, f); 102 | if (array === values) { 103 | return this; 104 | } else { 105 | return new ImmutableRecord(keys, array); 106 | } 107 | } 108 | }; 109 | 110 | 111 | export function isRecord(x) { 112 | return x instanceof ImmutableRecord; 113 | } 114 | 115 | export function Record(obj) { 116 | if (arguments.length === 0) { 117 | return new ImmutableRecord({}, []); 118 | 119 | } else { 120 | if (isRecord(obj)) { 121 | return obj; 122 | 123 | } else { 124 | var keys = {}; 125 | var values = []; 126 | 127 | var mapped = map(iter_object(obj), function (_array) { 128 | return destructure_pair(_array, function (key, value) { 129 | checkKey(key); 130 | return [key, value]; 131 | }); 132 | }); 133 | 134 | // TODO "sort" function in "iter.js" ? 135 | // TODO can this be made any faster/more efficient ? 136 | var sorted = toArray(mapped).sort(function (x, y) { 137 | return simpleSort(x[0], y[0]); 138 | }); 139 | 140 | each(sorted, function (_array) { 141 | var key = _array[0]; 142 | var value = _array[1]; 143 | 144 | var index = keys[key]; 145 | if (index == null) { 146 | keys[key] = values.push(value) - 1; 147 | } else { 148 | values[index] = value; 149 | } 150 | }); 151 | 152 | return new ImmutableRecord(keys, values); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableSet.js: -------------------------------------------------------------------------------- 1 | import { max, iter_tree } from "./AVL"; 2 | import { simpleSort, key_get, key_set, key_remove, sorted_isEmpty, 3 | sorted_has, sorted_remove } from "./Sorted"; 4 | import { hash, join_lines } from "./hash"; 5 | import { identity } from "./util"; 6 | import { toJSON_array, fromJSON_array } from "./toJSON"; 7 | import { toJS_array } from "./toJS"; 8 | import { ImmutableBase } from "./Base"; 9 | import { map_iter, map, foldl } from "./iter"; 10 | import { nil, tag_hash, tag_toJSON, fromJSON_registry, tag_toJS, tag_iter } from "./static"; 11 | 12 | 13 | function SetNode(left, right, hash, key) { 14 | this.left = left; 15 | this.right = right; 16 | this.hash = hash; 17 | this.key = key; 18 | this.depth = max(left.depth, right.depth) + 1; 19 | } 20 | 21 | SetNode.prototype.copy = function (left, right) { 22 | return new SetNode(left, right, this.hash, this.key); 23 | }; 24 | 25 | SetNode.prototype.modify = function (info) { 26 | var hash = info.hash; 27 | var key = info.key; 28 | // We don't use equal, for increased speed 29 | if (this.hash === hash && this.key === key) { 30 | return this; 31 | } else { 32 | return new SetNode(this.left, this.right, hash, key); 33 | } 34 | }; 35 | 36 | 37 | export function ImmutableSet(root, sort, hash_fn) { 38 | this.root = root; 39 | this.sort = sort; 40 | this.hash_fn = hash_fn; 41 | this.hash = null; 42 | } 43 | 44 | ImmutableSet.prototype = Object.create(ImmutableBase); 45 | 46 | ImmutableSet.prototype[tag_toJS] = toJS_array; 47 | ImmutableSet.prototype.isEmpty = sorted_isEmpty; 48 | ImmutableSet.prototype.has = sorted_has; 49 | ImmutableSet.prototype.remove = sorted_remove(ImmutableSet); 50 | 51 | fromJSON_registry["Set"] = function (x) { 52 | return Set(fromJSON_array(x)); 53 | }; 54 | 55 | ImmutableSet.prototype[tag_iter] = function () { 56 | return map_iter(iter_tree(this.root), function (node) { 57 | return node.key; 58 | }); 59 | }; 60 | 61 | ImmutableSet.prototype[tag_toJSON] = function (x) { 62 | if (isSet(x) && !isSortedSet(x)) { 63 | return toJSON_array("Set", x); 64 | } else { 65 | throw new Error("Cannot convert SortedSet to JSON"); 66 | } 67 | }; 68 | 69 | ImmutableSet.prototype[tag_hash] = function (x) { 70 | if (x.hash === null) { 71 | var a = map(x, function (value) { 72 | return hash(value); 73 | }); 74 | 75 | var spaces = " "; 76 | 77 | if (isSet(x) && !isSortedSet(x)) { 78 | x.hash = "(Set" + join_lines(a, spaces) + ")"; 79 | } else { 80 | x.hash = "(SortedSet " + hash(x.sort) + join_lines(a, spaces) + ")"; 81 | } 82 | } 83 | 84 | return x.hash; 85 | }; 86 | 87 | ImmutableSet.prototype.removeAll = function () { 88 | return new ImmutableSet(nil, this.sort, this.hash_fn); 89 | }; 90 | 91 | ImmutableSet.prototype.add = function (key) { 92 | var root = this.root; 93 | var sort = this.sort; 94 | var hash_fn = this.hash_fn; 95 | var hash = hash_fn(key); 96 | var node = key_set(root, sort, hash, new SetNode(nil, nil, hash, key)); 97 | if (node === root) { 98 | return this; 99 | } else { 100 | return new ImmutableSet(node, sort, hash_fn); 101 | } 102 | }; 103 | 104 | ImmutableSet.prototype.union = function (other) { 105 | return foldl(other, this, function (self, value) { 106 | return self.add(value); 107 | }); 108 | }; 109 | 110 | ImmutableSet.prototype.intersect = function (other) { 111 | var self = this; 112 | 113 | if (self.isEmpty()) { 114 | return self; 115 | 116 | } else { 117 | var out = self.removeAll(); 118 | 119 | return foldl(other, out, function (out, value) { 120 | if (self.has(value)) { 121 | return out.add(value); 122 | } else { 123 | return out; 124 | } 125 | }); 126 | } 127 | }; 128 | 129 | ImmutableSet.prototype.disjoint = function (other) { 130 | var self = this; 131 | 132 | return foldl(other, self, function (out, value) { 133 | if (self.has(value)) { 134 | return out.remove(value); 135 | } else { 136 | return out.add(value); 137 | } 138 | }); 139 | }; 140 | 141 | ImmutableSet.prototype.subtract = function (other) { 142 | if (this.isEmpty()) { 143 | return this; 144 | 145 | } else { 146 | return foldl(other, this, function (self, value) { 147 | return self.remove(value); 148 | }); 149 | } 150 | }; 151 | 152 | 153 | export function isSet(x) { 154 | return x instanceof ImmutableSet; 155 | } 156 | 157 | export function isSortedSet(x) { 158 | return isSet(x) && x.hash_fn === identity; 159 | } 160 | 161 | export function SortedSet(sort, array) { 162 | if (arguments.length === 1) { 163 | return new ImmutableSet(nil, sort, identity); 164 | 165 | } else if (arguments.length === 2) { 166 | // We don't use equal, for increased speed 167 | if (isSortedSet(array) && array.sort === sort) { 168 | return array; 169 | } else { 170 | return new ImmutableSet(nil, sort, identity).union(array); 171 | } 172 | 173 | } else { 174 | throw new Error("Expected 1 to 2 arguments but got " + arguments.length); 175 | } 176 | } 177 | 178 | export function Set(array) { 179 | if (arguments.length === 0) { 180 | return new ImmutableSet(nil, simpleSort, hash); 181 | 182 | } else if (arguments.length === 1) { 183 | if (isSet(array) && !isSortedSet(array)) { 184 | return array; 185 | } else { 186 | return new ImmutableSet(nil, simpleSort, hash).union(array); 187 | } 188 | 189 | } else { 190 | throw new Error("Expected 0 to 1 arguments but got " + arguments.length); 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableStack.js: -------------------------------------------------------------------------------- 1 | import { toJSON_array, fromJSON_array } from "./toJSON"; 2 | import { toJS_array } from "./toJS"; 3 | import { hash_array } from "./hash"; 4 | import { ImmutableBase } from "./Base"; 5 | import { Cons, iter_cons } from "./Cons"; 6 | import { reverse_iter } from "./iter"; 7 | import { sorted_isEmpty, stack_size, stack_concat } from "./Sorted"; 8 | import { nil, tag_toJSON, fromJSON_registry, tag_toJS, tag_hash, tag_iter } from "./static"; 9 | 10 | export function ImmutableStack(root, len) { 11 | this.root = root; 12 | this.len = len; 13 | this.hash = null; 14 | } 15 | 16 | ImmutableStack.prototype = Object.create(ImmutableBase); 17 | 18 | ImmutableStack.prototype[tag_toJS] = toJS_array; 19 | ImmutableStack.prototype[tag_hash] = hash_array("Stack"); 20 | ImmutableStack.prototype.isEmpty = sorted_isEmpty; 21 | ImmutableStack.prototype.size = stack_size; 22 | ImmutableStack.prototype.concat = stack_concat; 23 | 24 | fromJSON_registry["Stack"] = function (x) { 25 | return Stack(fromJSON_array(x)); 26 | }; 27 | 28 | ImmutableStack.prototype[tag_iter] = function () { 29 | return reverse_iter(iter_cons(this.root)); 30 | }; 31 | 32 | ImmutableStack.prototype[tag_toJSON] = function (x) { 33 | return toJSON_array("Stack", x); 34 | }; 35 | 36 | ImmutableStack.prototype.removeAll = function () { 37 | return new ImmutableStack(nil, 0); 38 | }; 39 | 40 | ImmutableStack.prototype.peek = function (def) { 41 | if (this.isEmpty()) { 42 | if (arguments.length === 1) { 43 | return def; 44 | } else { 45 | throw new Error("Cannot peek from an empty stack"); 46 | } 47 | } else { 48 | return this.root.car; 49 | } 50 | }; 51 | 52 | ImmutableStack.prototype.push = function (value) { 53 | return new ImmutableStack(new Cons(value, this.root), this.len + 1); 54 | }; 55 | 56 | ImmutableStack.prototype.pop = function () { 57 | if (this.isEmpty()) { 58 | throw new Error("Cannot pop from an empty stack"); 59 | } else { 60 | return new ImmutableStack(this.root.cdr, this.len - 1); 61 | } 62 | }; 63 | 64 | 65 | export function isStack(x) { 66 | return x instanceof ImmutableStack; 67 | } 68 | 69 | export function Stack(x) { 70 | if (arguments.length === 0) { 71 | return new ImmutableStack(nil, 0); 72 | } else { 73 | if (x instanceof ImmutableStack) { 74 | return x; 75 | } else { 76 | return new ImmutableStack(nil, 0).concat(x); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableTuple.js: -------------------------------------------------------------------------------- 1 | import { modify as array_modify } from "./Array"; 2 | import { hash_array } from "./hash"; 3 | import { toJSON_array, fromJSON_array } from "./toJSON"; 4 | import { toJS_array } from "./toJS"; 5 | import { ImmutableBase } from "./Base"; 6 | import { toIterator, each } from "./iter"; 7 | import { tag_hash, tag_toJSON, fromJSON_registry, tag_toJS, tag_iter } from "./static"; 8 | import { nth_has } from "./Ordered"; 9 | 10 | export function ImmutableTuple(values) { 11 | this._values = values; 12 | this.hash = null; 13 | } 14 | 15 | ImmutableTuple.prototype = Object.create(ImmutableBase); 16 | 17 | ImmutableTuple.prototype[tag_hash] = hash_array("Tuple"); 18 | ImmutableTuple.prototype[tag_toJS] = toJS_array; 19 | 20 | fromJSON_registry["Tuple"] = function (x) { 21 | return Tuple(fromJSON_array(x)); 22 | }; 23 | 24 | ImmutableTuple.prototype[tag_toJSON] = function (x) { 25 | return toJSON_array("Tuple", x); 26 | }; 27 | 28 | ImmutableTuple.prototype[tag_iter] = function () { 29 | return toIterator(this._values); 30 | }; 31 | 32 | ImmutableTuple.prototype.size = function () { 33 | return this._values.length; 34 | }; 35 | 36 | ImmutableTuple.prototype.isEmpty = function () { 37 | return this._values.length === 0; 38 | }; 39 | 40 | ImmutableTuple.prototype.get = function (index) { 41 | var len = this.size(); 42 | 43 | if (nth_has(index, len)) { 44 | return this._values[index]; 45 | } else { 46 | throw new Error("Index " + index + " is not valid"); 47 | } 48 | }; 49 | 50 | ImmutableTuple.prototype.modify = function (index, f) { 51 | var len = this.size(); 52 | 53 | if (nth_has(index, len)) { 54 | var values = this._values; 55 | var array = array_modify(values, index, f); 56 | if (array === values) { 57 | return this; 58 | } else { 59 | return new ImmutableTuple(array); 60 | } 61 | 62 | } else { 63 | throw new Error("Index " + index + " is not valid"); 64 | } 65 | }; 66 | 67 | // TODO a bit of code duplication 68 | ImmutableTuple.prototype.set = function (index, value) { 69 | return this.modify(index, function () { 70 | return value; 71 | }); 72 | }; 73 | 74 | export function isTuple(x) { 75 | return x instanceof ImmutableTuple; 76 | } 77 | 78 | export function unsafe_Tuple(array) { 79 | return new ImmutableTuple(array); 80 | } 81 | 82 | export function Tuple(array) { 83 | if (arguments.length === 0) { 84 | return new ImmutableTuple([]); 85 | 86 | } else { 87 | if (isTuple(array)) { 88 | return array; 89 | 90 | } else { 91 | var values = []; 92 | 93 | // We can't use toArray, because `array` might be mutated 94 | each(array, function (x) { 95 | values.push(x); 96 | }); 97 | 98 | return new ImmutableTuple(values); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Immutable/ImmutableType.js: -------------------------------------------------------------------------------- 1 | import { modify as array_modify } from "./Array"; 2 | import { hash, join_lines } from "./hash"; 3 | import { toJSON_array, fromJSON_array, toJSON, fromJSON } from "./toJSON"; 4 | import { toJS_array, toJS } from "./toJS"; 5 | import { each, map } from "./iter"; 6 | import { tag_hash, tag_toJSON, fromJSON_registry, tag_toJS } from "./static"; 7 | import { nth_has } from "./Ordered"; 8 | import { ImmutableTuple } from "./ImmutableTuple"; 9 | 10 | export function ImmutableType(type, values) { 11 | this._type = type; 12 | this._values = values; 13 | this.hash = null; 14 | } 15 | 16 | ImmutableType.prototype = Object.create(ImmutableTuple.prototype); 17 | 18 | // TODO hacky 19 | // TODO code duplication 20 | ImmutableType.prototype[tag_hash] = function (x) { 21 | if (x.hash === null) { 22 | var a = map(x, function (x) { 23 | return hash(x); 24 | }); 25 | 26 | x.hash = "(Type " + hash(x._type) + join_lines(a, " ") + ")"; 27 | } 28 | 29 | return x.hash; 30 | }; 31 | 32 | ImmutableType.prototype[tag_toJS] = function (x) { 33 | return { 34 | type: toJS(x._type), 35 | values: toJS_array(x) 36 | }; 37 | }; 38 | 39 | fromJSON_registry["Type"] = function (x) { 40 | // TODO hacky 41 | return Type(fromJSON(x.type), fromJSON_array(x)); 42 | }; 43 | 44 | ImmutableType.prototype[tag_toJSON] = function (x) { 45 | var x2 = toJSON_array("Type", x); 46 | // TODO hacky 47 | x2.type = toJSON(x._type); 48 | return x2; 49 | }; 50 | 51 | ImmutableType.prototype.type = function () { 52 | return this._type; 53 | }; 54 | 55 | // TODO code duplication 56 | ImmutableType.prototype.modify = function (index, f) { 57 | var len = this.size(); 58 | 59 | if (nth_has(index, len)) { 60 | var values = this._values; 61 | var array = array_modify(values, index, f); 62 | if (array === values) { 63 | return this; 64 | } else { 65 | return new ImmutableType(this._type, array); 66 | } 67 | 68 | } else { 69 | throw new Error("Index " + index + " is not valid"); 70 | } 71 | }; 72 | 73 | 74 | export function isType(x) { 75 | return x instanceof ImmutableType; 76 | } 77 | 78 | // TODO code duplication 79 | export function Type(type, array) { 80 | if (arguments.length === 1) { 81 | return new ImmutableType(type, []); 82 | 83 | } else if (arguments.length === 2) { 84 | // We don't use equal, for increased speed 85 | if (isType(array) && array._type === type) { 86 | return array; 87 | 88 | } else { 89 | var values = []; 90 | 91 | // We can't use toArray, because `array` might be mutated 92 | each(array, function (x) { 93 | values.push(x); 94 | }); 95 | 96 | return new ImmutableType(type, values); 97 | } 98 | 99 | } else { 100 | throw new Error("Expected 1 to 2 arguments but got " + arguments.length); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Immutable/MutableRef.js: -------------------------------------------------------------------------------- 1 | import { hash } from "./hash"; 2 | import { MutableBase } from "./Base"; 3 | import { tag_hash } from "./static"; 4 | 5 | var ref_id = 0; 6 | 7 | export function MutableRef(value, onchange) { 8 | this._id = ++ref_id; 9 | this._value = value; 10 | this._onchange = onchange; 11 | } 12 | 13 | MutableRef.prototype = Object.create(MutableBase); 14 | 15 | MutableRef.prototype[tag_hash] = function (x) { 16 | return "(Ref " + hash(x._id) + ")"; 17 | }; 18 | 19 | MutableRef.prototype.get = function () { 20 | return this._value; 21 | }; 22 | 23 | MutableRef.prototype.set = function (value) { 24 | var old = this._value; 25 | var onchange = this._onchange; 26 | if (onchange != null) { 27 | this._value = onchange(old, value); 28 | } else { 29 | this._value = value; 30 | } 31 | }; 32 | 33 | MutableRef.prototype.modify = function (f) { 34 | this.set(f(this.get())); 35 | }; 36 | 37 | 38 | export function deref(x) { 39 | if (isRef(x)) { 40 | return x.get(); 41 | } else { 42 | return x; 43 | } 44 | } 45 | 46 | export function isRef(x) { 47 | return x instanceof MutableRef; 48 | } 49 | 50 | export function Ref(value, onchange) { 51 | var arg_len = arguments.length; 52 | 53 | if (arg_len < 1 || arg_len > 2) { 54 | throw new Error("Expected 1 to 2 arguments but got " + arg_len); 55 | } 56 | 57 | return new MutableRef(value, onchange); 58 | } 59 | -------------------------------------------------------------------------------- /src/Immutable/Ordered.js: -------------------------------------------------------------------------------- 1 | export function nth_has(index, len) { 2 | return index >= 0 && index < len; 3 | } 4 | 5 | export function nth_has_end(index, len) { 6 | return index >= 0 && index <= len; 7 | } 8 | 9 | export function ordered_has(index) { 10 | var len = this.size(); 11 | 12 | if (index < 0) { 13 | index += len; 14 | } 15 | 16 | return nth_has(index, len); 17 | } 18 | -------------------------------------------------------------------------------- /src/Immutable/Sorted.js: -------------------------------------------------------------------------------- 1 | import { concat, balanced_node } from "./AVL"; 2 | import { foldl, iter_object } from "./iter"; 3 | import { destructure_pair } from "./util"; 4 | import { nil } from "./static"; 5 | 6 | export function simpleSort(x, y) { 7 | if (x === y) { 8 | return 0; 9 | } else if (x < y) { 10 | return -1; 11 | } else { 12 | return 1; 13 | } 14 | } 15 | 16 | // TODO what if `sort` suspends ? 17 | export function key_get(node, sort, hash) { 18 | while (node !== nil) { 19 | var order = sort(hash, node.hash); 20 | if (order === 0) { 21 | break; 22 | 23 | } else if (order < 0) { 24 | node = node.left; 25 | 26 | } else { 27 | node = node.right; 28 | } 29 | } 30 | 31 | return node; 32 | } 33 | 34 | // TODO what if `sort` suspends ? 35 | export function key_set(node, sort, hash, new_node) { 36 | if (node === nil) { 37 | return new_node; 38 | 39 | } else { 40 | var left = node.left; 41 | var right = node.right; 42 | 43 | var order = sort(hash, node.hash); 44 | if (order === 0) { 45 | return node.modify(new_node); 46 | 47 | } else if (order < 0) { 48 | var child = key_set(left, sort, hash, new_node); 49 | if (child === left) { 50 | return node; 51 | } else { 52 | return balanced_node(node, child, right); 53 | } 54 | 55 | } else { 56 | var child = key_set(right, sort, hash, new_node); 57 | if (child === right) { 58 | return node; 59 | } else { 60 | return balanced_node(node, left, child); 61 | } 62 | } 63 | } 64 | } 65 | 66 | // TODO code duplication with key_set 67 | // TODO what if `sort` suspends ? 68 | export function key_modify(node, sort, hash, key, f) { 69 | if (node === nil) { 70 | throw new Error("Key " + key + " not found"); 71 | 72 | } else { 73 | var left = node.left; 74 | var right = node.right; 75 | 76 | var order = sort(hash, node.hash); 77 | if (order === 0) { 78 | // TODO what if `f` suspends? 79 | return node.modify({ key: key, hash: hash, value: f(node.value) }); 80 | 81 | } else if (order < 0) { 82 | var child = key_modify(left, sort, hash, key, f); 83 | if (child === left) { 84 | return node; 85 | } else { 86 | return balanced_node(node, child, right); 87 | } 88 | 89 | } else { 90 | var child = key_modify(right, sort, hash, key, f); 91 | if (child === right) { 92 | return node; 93 | } else { 94 | return balanced_node(node, left, child); 95 | } 96 | } 97 | } 98 | } 99 | 100 | // TODO what if `sort` suspends ? 101 | export function key_remove(node, sort, hash) { 102 | if (node === nil) { 103 | return node; 104 | 105 | } else { 106 | var left = node.left; 107 | var right = node.right; 108 | 109 | var order = sort(hash, node.hash); 110 | if (order === 0) { 111 | return concat(left, right); 112 | 113 | } else if (order < 0) { 114 | var child = key_remove(left, sort, hash); 115 | if (child === left) { 116 | return node; 117 | } else { 118 | return balanced_node(node, child, right); 119 | } 120 | 121 | } else { 122 | var child = key_remove(right, sort, hash); 123 | if (child === right) { 124 | return node; 125 | } else { 126 | return balanced_node(node, left, child); 127 | } 128 | } 129 | } 130 | } 131 | 132 | // TODO this probably shouldn't be in here 133 | export function sorted_isEmpty() { 134 | return this.root === nil; 135 | } 136 | 137 | export function sorted_has(key) { 138 | return key_get(this.root, this.sort, this.hash_fn(key)) !== nil; 139 | } 140 | 141 | export function sorted_remove(f) { 142 | return function (key) { 143 | var root = this.root; 144 | var sort = this.sort; 145 | var hash_fn = this.hash_fn; 146 | var node = key_remove(root, sort, hash_fn(key)); 147 | if (node === root) { 148 | return this; 149 | } else { 150 | // TODO is this slower than using the constructor directly ? 151 | return new f(node, sort, hash_fn); 152 | } 153 | }; 154 | } 155 | 156 | // TODO this probably shouldn't be in here 157 | export function sorted_merge(other) { 158 | return foldl(iter_object(other), this, function (self, _array) { 159 | return destructure_pair(_array, function (key, value) { 160 | return self.set(key, value); 161 | }); 162 | }); 163 | } 164 | 165 | // TODO this probably shouldn't be in here 166 | export function stack_size() { 167 | return this.len; 168 | } 169 | 170 | // TODO this probably shouldn't be in here 171 | export function stack_concat(right) { 172 | return foldl(right, this, function (self, x) { 173 | return self.push(x); 174 | }); 175 | } 176 | -------------------------------------------------------------------------------- /src/Immutable/Tag.js: -------------------------------------------------------------------------------- 1 | var tag_uuid = "48de6fff-9d11-472d-a76f-ed77a59a5cbc"; 2 | var tag_id = 0; 3 | 4 | var uuid = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"; 5 | var uuid_regexp = new RegExp("^" + uuid + "$"); 6 | 7 | var is_tag_regexp = new RegExp("^\\(Tag " + tag_uuid + " [0-9]+\\)$"); 8 | 9 | var is_uuid_tag_regexp = new RegExp("^\\(UUIDTag " + uuid + "\\)$"); 10 | 11 | export var Symbol_iterator = (typeof Symbol !== "undefined" && typeof Symbol.iterator !== "undefined" 12 | ? Symbol.iterator 13 | : null); 14 | 15 | export var Symbol_keyFor = (typeof Symbol !== "undefined" && typeof Symbol.keyFor !== "undefined" 16 | ? Symbol.keyFor 17 | : null); 18 | 19 | // TODO this doesn't test variants 20 | export function isUUID(x) { 21 | return typeof x === "string" && uuid_regexp.test(x); 22 | } 23 | 24 | export function isTag(x) { 25 | return typeof x === "string" && 26 | (is_tag_regexp.test(x) || 27 | is_uuid_tag_regexp.test(x)); 28 | } 29 | 30 | // TODO Symbol support 31 | export function isUUIDTag(x) { 32 | return typeof x === "string" && is_uuid_tag_regexp.test(x); 33 | } 34 | 35 | // TODO Symbol support ? 36 | export function Tag() { 37 | var arg_len = arguments.length; 38 | if (arg_len === 0) { 39 | return "(Tag " + tag_uuid + " " + (++tag_id) + ")"; 40 | } else { 41 | throw new Error("Expected 0 arguments but got " + arg_len); 42 | } 43 | } 44 | 45 | // TODO Symbol support ? 46 | export function UUIDTag(id) { 47 | var arg_len = arguments.length; 48 | if (arg_len === 1) { 49 | if (isUUID(id)) { 50 | return "(UUIDTag " + id + ")"; 51 | } else { 52 | throw new Error("Expected a lower-case UUID, but got: " + id); 53 | } 54 | 55 | } else { 56 | throw new Error("Expected 1 argument but got " + arg_len); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Immutable/UMD.js: -------------------------------------------------------------------------------- 1 | import { equal, fromJS, toJS, isDict, isSet, isSortedDict, isSortedSet, 2 | isList, isQueue, isTuple, isStack, isImmutable, SortedDict, 3 | SortedSet, isIterable, Dict, Set, List, Tuple, Queue, Stack, 4 | simpleSort, isRecord, Record, toJSON, fromJSON, deref, Ref, 5 | isRef, isTag, isUUIDTag, Tag, UUIDTag, each, map, keep, 6 | findIndex, reverse, foldl, foldr, join, zip, toArray, any, 7 | all, find, partition, range, take, indexOf, toIterator, Iterable, 8 | repeat, skip, isType, Type } from "../Immutable"; 9 | 10 | // UMD https://github.com/umdjs/umd 11 | ;(function (root, fn) { 12 | if (typeof define === 'function' && define.amd) { 13 | define(["exports"], fn); 14 | } else if (typeof exports === 'object') { 15 | fn(exports); 16 | } else { 17 | root.Immutable = {}; 18 | fn(root.Immutable); 19 | } 20 | })(this, function (exports) { 21 | exports.equal = equal; 22 | exports.fromJS = fromJS; 23 | exports.toJS = toJS; 24 | exports.isDict = isDict; 25 | exports.isSet = isSet; 26 | exports.isSortedDict = isSortedDict; 27 | exports.isSortedSet = isSortedSet; 28 | exports.isList = isList; 29 | exports.isQueue = isQueue; 30 | exports.isTuple = isTuple; 31 | exports.isStack = isStack; 32 | exports.isImmutable = isImmutable; 33 | exports.SortedDict = SortedDict; 34 | exports.SortedSet = SortedSet; 35 | exports.isIterable = isIterable; 36 | exports.Dict = Dict; 37 | exports.Set = Set; 38 | exports.List = List; 39 | exports.Tuple = Tuple; 40 | exports.Queue = Queue; 41 | exports.Stack = Stack; 42 | exports.simpleSort = simpleSort; 43 | exports.isRecord = isRecord; 44 | exports.Record = Record; 45 | exports.toJSON = toJSON; 46 | exports.fromJSON = fromJSON; 47 | exports.deref = deref; 48 | exports.Ref = Ref; 49 | exports.isRef = isRef; 50 | exports.isTag = isTag; 51 | exports.isUUIDTag = isUUIDTag; 52 | exports.Tag = Tag; 53 | exports.UUIDTag = UUIDTag; 54 | exports.each = each; 55 | exports.map = map; 56 | exports.keep = keep; 57 | exports.findIndex = findIndex; 58 | exports.reverse = reverse; 59 | exports.foldl = foldl; 60 | exports.foldr = foldr; 61 | exports.join = join; 62 | exports.zip = zip; 63 | exports.toArray = toArray; 64 | exports.any = any; 65 | exports.all = all; 66 | exports.find = find; 67 | exports.partition = partition; 68 | exports.range = range; 69 | exports.take = take; 70 | exports.indexOf = indexOf; 71 | exports.toIterator = toIterator; 72 | exports.Iterable = Iterable; 73 | exports.repeat = repeat; 74 | exports.skip = skip; 75 | exports.isType = isType; 76 | exports.Type = Type; 77 | }); 78 | -------------------------------------------------------------------------------- /src/Immutable/equal.js: -------------------------------------------------------------------------------- 1 | import { hash } from "./hash"; 2 | 3 | export function equal(x, y) { 4 | return x === y || hash(x) === hash(y); 5 | } 6 | -------------------------------------------------------------------------------- /src/Immutable/hash.js: -------------------------------------------------------------------------------- 1 | import { isTag, Symbol_keyFor } from "./Tag"; 2 | import { pad_right, repeat, destructure_pair } from "./util"; 3 | import { map, each, join } from "./iter"; 4 | import { tag_hash } from "./static"; 5 | 6 | var mutable_hash_id = 0; 7 | 8 | var Symbol_id = 0; 9 | var Symbol_registry = {}; 10 | 11 | export function hash_string(x) { 12 | return "\"" + x.replace(/[\\\"\n]/g, function (s) { 13 | if (s === "\n") { 14 | return s + " "; 15 | } else { 16 | return "\\" + s; 17 | } 18 | }) + "\""; 19 | } 20 | 21 | export function hash_symbol(x) { 22 | var key; 23 | if (Symbol_keyFor !== null && (key = Symbol_keyFor(x)) != null) { 24 | return "(Symbol.for " + hash(key) + ")"; 25 | } else { 26 | key = Symbol_registry[x]; 27 | if (key == null) { 28 | key = Symbol_registry[x] = (++Symbol_id); 29 | } 30 | return "(Symbol " + key + ")"; 31 | } 32 | } 33 | 34 | export function hash(x) { 35 | var type = typeof x; 36 | // TODO this is probably pretty inefficient 37 | if (type === "string") { 38 | if (isTag(x)) { 39 | return x; 40 | } else { 41 | return hash_string(x); 42 | } 43 | 44 | } else if (type === "number" || 45 | type === "boolean" || 46 | type === "undefined" || 47 | x === null) { 48 | return "" + x; 49 | 50 | } else if (type === "symbol") { 51 | return hash_symbol(x); 52 | 53 | } else { 54 | var hasher = x[tag_hash]; 55 | if (hasher != null) { 56 | return hasher(x); 57 | 58 | } else { 59 | if (Object.isExtensible(x)) { 60 | var id = "(Mutable " + (++mutable_hash_id) + ")"; 61 | 62 | Object.defineProperty(x, tag_hash, { 63 | configurable: false, 64 | enumerable: false, 65 | writable: false, 66 | value: function () { 67 | return id; 68 | } 69 | }); 70 | 71 | return id; 72 | 73 | } else { 74 | throw new Error("Cannot use a non-extensible object as a key: " + x); 75 | } 76 | } 77 | } 78 | } 79 | 80 | export function hash_dict(x, spaces) { 81 | var max_key = 0; 82 | 83 | var a = []; 84 | 85 | each(x, function (_array) { 86 | // TODO is destructure_pair needed here ? 87 | destructure_pair(_array, function (key, value) { 88 | key = hash(key); 89 | value = hash(value); 90 | 91 | key = key.split(/\n/); 92 | 93 | each(key, function (key) { 94 | max_key = Math.max(max_key, key.length); 95 | }); 96 | 97 | a.push({ 98 | key: key, 99 | value: value 100 | }); 101 | }); 102 | }); 103 | 104 | var spaces = " "; 105 | 106 | a = map(a, function (x) { 107 | var last = x.key.length - 1; 108 | x.key[last] = pad_right(x.key[last], max_key, " "); 109 | 110 | var key = join(x.key, "\n"); 111 | 112 | var value = x.value.replace(/\n/g, "\n" + repeat(" ", max_key + 3)); 113 | 114 | return key + " = " + value; 115 | }); 116 | 117 | return join_lines(a, spaces); 118 | } 119 | 120 | export function hash_array(s) { 121 | return function (x) { 122 | if (x.hash === null) { 123 | var a = map(x, function (x) { 124 | return hash(x); 125 | }); 126 | 127 | x.hash = "(" + s + join_lines(a, " ") + ")"; 128 | } 129 | 130 | return x.hash; 131 | }; 132 | } 133 | 134 | export function join_lines(a, spaces) { 135 | var separator = "\n" + spaces; 136 | 137 | return join(map(a, function (x) { 138 | return separator + x.replace(/\n/g, separator); 139 | })); 140 | } 141 | -------------------------------------------------------------------------------- /src/Immutable/static.js: -------------------------------------------------------------------------------- 1 | import { UUIDTag } from "./Tag"; 2 | 3 | export var tag_hash = UUIDTag("e1c3818d-4c4f-4703-980a-00969e4ca900"); 4 | export var tag_iter = UUIDTag("6199065c-b518-4cb3-8b41-ab70a9769ec3"); 5 | export var tag_toJS = UUIDTag("1b75a273-16bd-4248-be8a-e4b5e8c4b523"); 6 | export var tag_toJSON_type = UUIDTag("89d8297c-d95e-4ce9-bc9b-6b6f73fa6a37"); 7 | export var tag_toJSON = UUIDTag("99e14916-bc99-4c48-81aa-299cf1ad6de3"); 8 | 9 | export var fromJSON_registry = {}; 10 | 11 | export var nil = {}; 12 | nil.depth = 0; 13 | nil.size = 0; 14 | -------------------------------------------------------------------------------- /src/Immutable/toJS.js: -------------------------------------------------------------------------------- 1 | import { isObject, destructure_pair, isJSLiteral } from "./util"; 2 | import { each } from "./iter"; 3 | import { tag_toJS } from "./static"; 4 | 5 | // TODO circular import ? 6 | import { List } from "./ImmutableList"; 7 | 8 | // TODO circular import ? 9 | import { Dict } from "./ImmutableDict"; 10 | 11 | export function fromJS(x) { 12 | if (Array.isArray(x)) { 13 | var out = List(); 14 | 15 | for (var i = 0, l = x.length; i < l; ++i) { 16 | out = out.push(fromJS(x[i])); 17 | } 18 | 19 | return out; 20 | 21 | } else if (isJSLiteral(x)) { 22 | var out = Dict(); 23 | 24 | // TODO is using Object.keys correct ? 25 | Object.keys(x).forEach(function (key) { 26 | // TODO unit test for this 27 | out = out.set(fromJS(key), fromJS(x[key])); 28 | }); 29 | 30 | return out; 31 | 32 | } else { 33 | return x; 34 | } 35 | } 36 | 37 | export function toJS(x) { 38 | if (isObject(x)) { 39 | var fn = x[tag_toJS]; 40 | if (fn != null) { 41 | return fn(x); 42 | 43 | } else if (Array.isArray(x)) { 44 | return x.map(toJS); 45 | 46 | } else if (isJSLiteral(x)) { 47 | var out = {}; 48 | 49 | // TODO is using Object.keys correct ? 50 | Object.keys(x).forEach(function (key) { 51 | // TODO unit test for this 52 | out[toJS(key)] = toJS(x[key]); 53 | }); 54 | 55 | return out; 56 | 57 | } else { 58 | return x; 59 | } 60 | } else { 61 | return x; 62 | } 63 | } 64 | 65 | export function toJS_object(x) { 66 | var o = {}; 67 | 68 | each(x, function (_array) { 69 | destructure_pair(_array, function (key, value) { 70 | // Tags are currently implemented as strings 71 | // TODO use isString test ? 72 | if (typeof key !== "string") { 73 | throw new Error("Cannot convert to JavaScript: expected key to be string or Tag but got " + key); 74 | } 75 | 76 | o[key] = toJS(value); 77 | }); 78 | }); 79 | 80 | return o; 81 | } 82 | 83 | export function toJS_array(x) { 84 | var a = []; 85 | 86 | each(x, function (value) { 87 | a.push(toJS(value)); 88 | }); 89 | 90 | return a; 91 | } 92 | -------------------------------------------------------------------------------- /src/Immutable/toJSON.js: -------------------------------------------------------------------------------- 1 | import { isTag, isUUIDTag } from "./Tag"; 2 | import { isObject, isJSLiteral, isFinite, destructure_pair } from "./util"; 3 | import { each } from "./iter"; 4 | import { tag_toJSON, tag_toJSON_type, fromJSON_registry } from "./static"; 5 | 6 | export function fromJSON(x) { 7 | var type = typeof x; 8 | 9 | if (isTag(x)) { 10 | if (isUUIDTag(x)) { 11 | return x; 12 | } else { 13 | throw new Error("Cannot convert Tag from JSON, use UUIDTag instead: " + x); 14 | } 15 | 16 | } else if (type === "string" || type === "boolean" || x === null || isFinite(x)) { 17 | return x; 18 | 19 | } else if (isObject(x)) { 20 | var type = x[tag_toJSON_type]; 21 | if (type != null) { 22 | var register = fromJSON_registry[type]; 23 | if (register != null) { 24 | return register(x); 25 | } else { 26 | throw new Error("Cannot handle type " + type); 27 | } 28 | 29 | } else if (Array.isArray(x)) { 30 | return x.map(fromJSON); 31 | 32 | } else if (isJSLiteral(x)) { 33 | var out = {}; 34 | // TODO is Object.keys correct here ? 35 | Object.keys(x).forEach(function (key) { 36 | // TODO unit tests for this 37 | out[fromJSON(key)] = fromJSON(x[key]); 38 | }); 39 | return out; 40 | 41 | } else { 42 | throw new Error("Cannot convert from JSON: " + x); 43 | } 44 | 45 | } else { 46 | throw new Error("Cannot convert from JSON: " + x); 47 | } 48 | } 49 | 50 | export function toJSON(x) { 51 | var type = typeof x; 52 | 53 | if (isTag(x)) { 54 | if (isUUIDTag(x)) { 55 | return x; 56 | } else { 57 | throw new Error("Cannot convert Tag to JSON, use UUIDTag instead: " + x); 58 | } 59 | 60 | } else if (type === "string" || type === "boolean" || x === null || isFinite(x)) { 61 | return x; 62 | 63 | } else if (isObject(x)) { 64 | var fn = x[tag_toJSON]; 65 | if (fn != null) { 66 | return fn(x); 67 | 68 | // TODO isFunction ? 69 | // TODO should this be before or after tag_toJSON ? 70 | } else if (typeof x.toJSON === "function") { 71 | return toJSON(x.toJSON()); 72 | 73 | } else if (Array.isArray(x)) { 74 | return x.map(toJSON); 75 | 76 | } else if (isJSLiteral(x)) { 77 | var out = {}; 78 | // TODO is Object.keys correct here ? 79 | Object.keys(x).forEach(function (key) { 80 | // TODO unit tests for this 81 | out[toJSON(key)] = toJSON(x[key]); 82 | }); 83 | return out; 84 | 85 | } else { 86 | throw new Error("Cannot convert to JSON: " + x); 87 | } 88 | 89 | } else { 90 | throw new Error("Cannot convert to JSON: " + x); 91 | } 92 | } 93 | 94 | export function toJSON_object(type, x) { 95 | var o = {}; 96 | 97 | o[tag_toJSON_type] = type; 98 | 99 | o.keys = []; 100 | o.values = []; 101 | 102 | each(x, function (_array) { 103 | destructure_pair(_array, function (key, value) { 104 | o.keys.push(toJSON(key)); 105 | o.values.push(toJSON(value)); 106 | }); 107 | }); 108 | 109 | return o; 110 | } 111 | 112 | export function toJSON_array(type, x) { 113 | var o = {}; 114 | 115 | o[tag_toJSON_type] = type; 116 | 117 | o.values = []; 118 | 119 | each(x, function (value) { 120 | o.values.push(toJSON(value)); 121 | }); 122 | 123 | return o; 124 | } 125 | 126 | export function fromJSON_object(x) { 127 | var keys = x.keys; 128 | var values = x.values; 129 | 130 | var l = keys.length; 131 | var out = new Array(l); 132 | 133 | for (var i = 0; i < l; ++i) { 134 | out[i] = [fromJSON(keys[i]), fromJSON(values[i])]; 135 | } 136 | 137 | return out; 138 | } 139 | 140 | export function fromJSON_array(x) { 141 | var values = x.values; 142 | 143 | var l = values.length; 144 | var out = new Array(l); 145 | 146 | for (var i = 0; i < l; ++i) { 147 | out[i] = fromJSON(values[i]); 148 | } 149 | 150 | return out; 151 | } 152 | -------------------------------------------------------------------------------- /src/Immutable/util.js: -------------------------------------------------------------------------------- 1 | // TODO circular import 2 | import { isTuple } from "./ImmutableTuple"; 3 | 4 | // The built-in NaN auto-coerces 5 | export function isNaN(x) { 6 | return x !== x; 7 | } 8 | 9 | // TODO is there a faster implementation of this? 10 | export function isInteger(x) { 11 | return Math.round(x) === x; 12 | } 13 | 14 | // The built-in isFinite auto-coerces 15 | export function isFinite(x) { 16 | return typeof x === "number" && 17 | x !== Infinity && 18 | x !== -Infinity && 19 | !isNaN(x); 20 | } 21 | 22 | export function isObject(x) { 23 | return Object(x) === x; 24 | } 25 | 26 | export function isJSLiteral(x) { 27 | // TODO this won't work cross-realm 28 | return isObject(x) && Object.getPrototypeOf(x) === Object.prototype; 29 | } 30 | 31 | export function repeat(s, i) { 32 | return new Array(i + 1).join(s); 33 | } 34 | 35 | export function pad_right(input, i, s) { 36 | var right = Math.max(0, i - input.length); 37 | return input + repeat(s, right); 38 | } 39 | 40 | export function identity(x) { 41 | return x; 42 | } 43 | 44 | export function plural(i, s) { 45 | if (i === 1) { 46 | return s; 47 | } else { 48 | return s + "s"; 49 | } 50 | } 51 | 52 | export function destructure_pair(x, f) { 53 | if (Array.isArray(x)) { 54 | if (x.length === 2) { 55 | return f(x[0], x[1]); 56 | } else { 57 | throw new Error("Expected array with 2 elements but got " + x.length + " " + plural(x.length, "element")); 58 | } 59 | 60 | } else if (isTuple(x)) { 61 | if (x.size() === 2) { 62 | return f(x.get(0), x.get(1)); 63 | } else { 64 | // TODO code duplication 65 | throw new Error("Expected Tuple with 2 elements but got " + x.size() + " " + plural(x.size(), "element")); 66 | } 67 | 68 | } else { 69 | throw new Error("Expected array or Tuple but got: " + x); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Test.js: -------------------------------------------------------------------------------- 1 | import "./Test/shim"; 2 | import { test_run } from "./Test/assert"; 3 | import { test_Dict } from "./Test/Dict"; 4 | import { test_Set } from "./Test/Set"; 5 | import { test_List } from "./Test/List"; 6 | import { test_Tuple } from "./Test/Tuple"; 7 | import { test_Queue } from "./Test/Queue"; 8 | import { test_Stack } from "./Test/Stack"; 9 | import { test_Record } from "./Test/Record"; 10 | import { test_Ref } from "./Test/Ref"; 11 | import { test_Tag } from "./Test/Tag"; 12 | import { test_misc } from "./Test/misc"; 13 | import { test_iter } from "./Test/iter"; 14 | import { test_Type } from "./Test/Type"; 15 | 16 | test_run(function () { 17 | test_Dict(); 18 | test_Set(); 19 | test_List(); 20 | test_Tuple(); 21 | test_Queue(); 22 | test_Stack(); 23 | test_Record(); 24 | test_Ref(); 25 | test_Tag(); 26 | test_misc(); 27 | test_iter(); 28 | test_Type(); 29 | }); 30 | -------------------------------------------------------------------------------- /src/Test/Queue.js: -------------------------------------------------------------------------------- 1 | import { isQueue, toJS, Queue, List, equal } from "../Immutable"; 2 | import { assert, context, test, assert_raises } from "./assert"; 3 | import { deepEqual, verify_json, test_each } from "./util"; 4 | import { nil } from "../Immutable/static"; 5 | 6 | function verify_queue(queue, array) { 7 | assert(isQueue(queue)); 8 | 9 | var size = queue.size(); 10 | if (size === 0) { 11 | assert(queue.left === nil); 12 | assert(queue.right === nil); 13 | } else if (size === 1) { 14 | assert(queue.left !== nil); 15 | assert(queue.right === nil); 16 | } else { 17 | assert(queue.left !== nil); 18 | //assert(queue.right !== nil); 19 | } 20 | 21 | assert(deepEqual(toJS(queue), array)); 22 | 23 | return queue; 24 | } 25 | 26 | export function test_Queue() { 27 | context("Queue", function () { 28 | var empty_queue = Queue(); 29 | var five_queue = Queue().push(1).push(2).push(3).push(4).push(5); 30 | 31 | test("isQueue", function () { 32 | assert(!isQueue(List())); 33 | assert(isQueue(Queue())); 34 | }); 35 | 36 | test("verify", function () { 37 | verify_queue(empty_queue, []); 38 | verify_queue(five_queue, [1, 2, 3, 4, 5]); 39 | }); 40 | 41 | test("init", function () { 42 | verify_queue(Queue([1, 2, 3]), [1, 2, 3]); 43 | 44 | assert_raises(function () { 45 | Queue(null); 46 | }, "Cannot read property '(UUIDTag 6199065c-b518-4cb3-8b41-ab70a9769ec3)' of null"); 47 | 48 | verify_queue(Queue(), []); 49 | }); 50 | 51 | test("isEmpty", function () { 52 | assert(empty_queue.isEmpty()); 53 | assert(!five_queue.isEmpty()); 54 | }); 55 | 56 | test("size", function () { 57 | assert(empty_queue.size() === 0); 58 | assert(five_queue.size() === 5); 59 | }); 60 | 61 | test("peek", function () { 62 | assert_raises(function () { 63 | empty_queue.peek(); 64 | }, "Cannot peek from an empty queue"); 65 | 66 | assert(empty_queue.peek(50) === 50); 67 | 68 | assert(five_queue.peek() === 1); 69 | assert(five_queue.peek(50) === 1); 70 | 71 | assert(Queue().push(5).push(10).pop().peek() === 10); 72 | assert(Queue().push(5).push(10).push(15).pop().peek() === 10); 73 | }); 74 | 75 | test("push", function () { 76 | var x = empty_queue.push(10); 77 | 78 | verify_queue(empty_queue, []); 79 | verify_queue(x, [10]); 80 | 81 | assert(empty_queue.size() === 0); 82 | assert(x.size() === 1); 83 | assert(x.peek() === 10); 84 | 85 | assert(Queue().push(1).peek() === 1); 86 | 87 | verify_queue(five_queue.push(10), [1, 2, 3, 4, 5, 10]); 88 | verify_queue(five_queue.push(10).push(20), [1, 2, 3, 4, 5, 10, 20]); 89 | verify_queue(five_queue, [1, 2, 3, 4, 5]); 90 | 91 | verify_queue(Queue().push(5).push(4).push(3).push(2).push(1), 92 | [5, 4, 3, 2, 1]); 93 | 94 | verify_queue(Queue().push(5).push(10).pop().push(15), [10, 15]); 95 | }); 96 | 97 | test("removeAll", function () { 98 | verify_queue(empty_queue.removeAll(), []); 99 | verify_queue(five_queue.removeAll(), []); 100 | }); 101 | 102 | test("pop", function () { 103 | assert_raises(function () { 104 | empty_queue.pop(); 105 | }, "Cannot pop from an empty queue"); 106 | 107 | verify_queue(Queue().push(5).pop(), []); 108 | verify_queue(Queue().push(5).push(10).pop(), [10]); 109 | verify_queue(Queue().push(5).push(10).push(15).pop(), [10, 15]); 110 | 111 | verify_queue(five_queue.pop(), [2, 3, 4, 5]); 112 | verify_queue(five_queue.pop().pop(), [3, 4, 5]); 113 | 114 | verify_queue(Queue(), []); 115 | verify_queue(Queue().push(5).push(10).push(20).push(30), [5, 10, 20, 30]); 116 | verify_queue(Queue().push(5).push(10).push(20).push(30).pop(), [10, 20, 30]); 117 | }); 118 | 119 | test("concat", function () { 120 | verify_queue(empty_queue.concat(empty_queue), []); 121 | verify_queue(five_queue.concat(five_queue), [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]); 122 | verify_queue(Queue([10, 20, 30]).concat(five_queue), [10, 20, 30, 1, 2, 3, 4, 5]); 123 | verify_queue(five_queue.concat(Queue([10, 20, 30])), [1, 2, 3, 4, 5, 10, 20, 30]); 124 | verify_queue(five_queue.concat([10, 20, 30]), [1, 2, 3, 4, 5, 10, 20, 30]); 125 | }); 126 | 127 | test("=== when not modified", function () { 128 | assert(Queue(five_queue) === five_queue); 129 | 130 | assert(empty_queue.concat(empty_queue) === empty_queue); 131 | assert(five_queue.concat(empty_queue) === five_queue); 132 | assert(empty_queue.concat(five_queue) !== five_queue); 133 | }); 134 | 135 | test("equal", function () { 136 | assert(equal(empty_queue, empty_queue)); 137 | assert(equal(five_queue, five_queue)); 138 | 139 | assert(equal(Queue([1, 2, 3]), Queue([1, 2, 3]))); 140 | assert(!equal(Queue([1, 2, 3]), Queue([1, 2, 4]))); 141 | assert(!equal(Queue([1, 2, 3]), Queue([1, 3, 2]))); 142 | 143 | assert(equal(Queue([1, 2, 3, 4, 5]), five_queue)); 144 | assert(equal(five_queue, Queue([1, 2, 3, 4, 5]))); 145 | 146 | assert(equal(Queue([Queue([1, 2, 3])]), Queue([Queue([1, 2, 3])]))); 147 | }); 148 | 149 | test("toJS", function () { 150 | assert(deepEqual(toJS(empty_queue), [])); 151 | assert(deepEqual(toJS(five_queue), [1, 2, 3, 4, 5])); 152 | assert(deepEqual(toJS(Queue([1, 2, Queue([3])])), [1, 2, [3]])); 153 | }); 154 | 155 | test("toJSON", function () { 156 | verify_json(empty_queue, []); 157 | verify_json(five_queue, [1, 2, 3, 4, 5]); 158 | verify_json(Queue([4, 5, Queue([1, 2, 3])]), [4, 5, [1, 2, 3]]); 159 | }); 160 | 161 | test("each", function () { 162 | test_each(Queue(), []); 163 | 164 | var x = Queue([3]); 165 | test_each(Queue([1, 2, x, 4]), [1, 2, x, 4]); 166 | }); 167 | 168 | test("toString", function () { 169 | assert("" + empty_queue === "(Queue)"); 170 | assert("" + Queue([1, 2, 3]) === "(Queue\n 1\n 2\n 3)"); 171 | assert("" + Queue([1, Queue([2]), 3]) === "(Queue\n 1\n (Queue\n 2)\n 3)"); 172 | }); 173 | 174 | /* 175 | // TODO code duplication 176 | test("zip", function () { 177 | var a = random_list(200); 178 | assert(deepEqual(toArray(zip(Queue(a))), toArray(zip(a)))); 179 | });*/ 180 | }); 181 | } 182 | -------------------------------------------------------------------------------- /src/Test/Record.js: -------------------------------------------------------------------------------- 1 | import { isRecord, toJS, Record, Dict, equal, isTuple, toArray, Tuple, List, zip, map } from "../Immutable"; 2 | import { assert, context, test, assert_raises } from "./assert"; 3 | import { deepEqual, verify_json, test_each_dict } from "./util"; 4 | 5 | function verify_record(record, obj) { 6 | assert(isRecord(record)); 7 | 8 | var count = 0; 9 | 10 | for (var _ in record.keys) { 11 | ++count; 12 | } 13 | 14 | assert(count === record.values.length); 15 | 16 | assert(deepEqual(toJS(record), obj)); 17 | 18 | return record; 19 | } 20 | 21 | export function test_Record() { 22 | context("Record", function () { 23 | var Empty = Record({}); 24 | var Foo = Record({ foo: 1 }); 25 | 26 | test("isRecord", function () { 27 | assert(!isRecord(Dict())); 28 | assert(isRecord(Empty)); 29 | assert(isRecord(Foo)); 30 | }); 31 | 32 | test("verify", function () { 33 | verify_record(Empty, {}); 34 | verify_record(Foo, { foo: 1 }); 35 | }); 36 | 37 | test("toString", function () { 38 | assert("" + Empty === "(Record)"); 39 | assert("" + Foo === "(Record\n \"foo\" = 1)"); 40 | assert("" + Record({ foo: 2 }) === "(Record\n \"foo\" = 2)"); 41 | assert("" + Record({ foo: 1 }) === "(Record\n \"foo\" = 1)"); 42 | assert("" + Record({ foo: 1, bar: 2 }) === "(Record\n \"bar\" = 2\n \"foo\" = 1)"); 43 | assert("" + Record({ "foo\nbar\nqux": 1, bar: 2 }) === "(Record\n \"bar\" = 2\n \"foo\n bar\n qux\" = 1)"); 44 | assert("" + Record({ foo: Record({ qux: 3 }), bar: 2 }) === "(Record\n \"bar\" = 2\n \"foo\" = (Record\n \"qux\" = 3))"); 45 | assert("" + Record({ "foo\nbar\nqux": Record({ qux: 3 }), bar: 2 }) === "(Record\n \"bar\" = 2\n \"foo\n bar\n qux\" = (Record\n \"qux\" = 3))"); 46 | 47 | assert("" + Record({ foobarquxcorgenou: 1, bar: 2 }) === "(Record\n \"bar\" = 2\n \"foobarquxcorgenou\" = 1)"); 48 | assert("" + Record({ "foobar\nquxcorgenou": 1, bar: 2 }) === "(Record\n \"bar\" = 2\n \"foobar\n quxcorgenou\" = 1)"); 49 | assert("" + Record({ "foo\nbar\nqux": 1, "barquxcorgenou": 2 }) === "(Record\n \"barquxcorgenou\" = 2\n \"foo\n bar\n qux\" = 1)"); 50 | }); 51 | 52 | test("init", function () { 53 | assert_raises(function () { 54 | Record(null); 55 | }, "Cannot read property '(UUIDTag 6199065c-b518-4cb3-8b41-ab70a9769ec3)' of null"); 56 | 57 | verify_record(Record(), {}); 58 | 59 | var x = Record({ foo: 1 }); 60 | verify_record(x, { foo: 1 }); 61 | assert(equal(x, Foo)); 62 | assert(equal(Foo, x)); 63 | 64 | assert_raises(function () { 65 | Record(Object.create(null)); 66 | }, "Cannot convert object to primitive value"); 67 | 68 | verify_record(Record({ foo: 2 }), { foo: 2 }); 69 | 70 | verify_record(Record(), {}); 71 | 72 | 73 | verify_record(Record([["foo", 2]]), { foo: 2 }); 74 | 75 | verify_record(Record([["foo", 2], ["foo", 3]]), { foo: 3 }); 76 | verify_record(Record([["bar", 1], ["foo", 2], ["qux", 4], ["foo", 3], ["corge", 5]]), { bar: 1, foo: 3, qux: 4, corge: 5 }); 77 | 78 | var x = map(Record([["bar", 1], ["foo", 2], ["qux", 4], ["foo", 3], ["corge", 5]]), function (x) { 79 | assert(isTuple(x)); 80 | return toArray(x); 81 | }); 82 | assert(deepEqual(toArray(x), [["bar", 1], ["corge", 5], ["foo", 3], ["qux", 4]])); 83 | 84 | verify_record(Record([Tuple(["foo", 2])]), { foo: 2 }); 85 | 86 | assert_raises(function () { 87 | Record([List(["foo", 2])]); 88 | }, "Expected array or Tuple but got: (List\n \"foo\"\n 2)"); 89 | 90 | assert_raises(function () { 91 | Record([{}]); 92 | }, "Expected array or Tuple but got: [object Object]"); 93 | 94 | assert_raises(function () { 95 | Record([[]]); 96 | }, "Expected array with 2 elements but got 0 elements"); 97 | 98 | assert_raises(function () { 99 | Record([["foo"]]); 100 | }, "Expected array with 2 elements but got 1 element"); 101 | 102 | assert_raises(function () { 103 | Record([["foo", 2, 3]]); 104 | }, "Expected array with 2 elements but got 3 elements"); 105 | 106 | assert_raises(function () { 107 | Record([Tuple([])]); 108 | }, "Expected Tuple with 2 elements but got 0 elements"); 109 | 110 | assert_raises(function () { 111 | Record([Tuple(["foo"])]); 112 | }, "Expected Tuple with 2 elements but got 1 element"); 113 | 114 | assert_raises(function () { 115 | Record([Tuple(["foo", 2, 3])]); 116 | }, "Expected Tuple with 2 elements but got 3 elements"); 117 | }); 118 | 119 | test("isEmpty", function () { 120 | assert(Empty.isEmpty()); 121 | assert(!Foo.isEmpty()); 122 | }); 123 | 124 | test("has", function () { 125 | assert(!Empty.has("foo")); 126 | assert(!Empty.has("bar")); 127 | 128 | assert(Foo.has("foo")); 129 | assert(!Foo.has("bar")); 130 | }); 131 | 132 | test("get", function () { 133 | assert_raises(function () { 134 | Empty.get("foo"); 135 | }, "Key foo not found"); 136 | 137 | assert(Empty.get("foo", 50) === 50); 138 | 139 | assert(Foo.get("foo") === 1); 140 | assert(Foo.get("foo", 50) === 1); 141 | }); 142 | 143 | test("set", function () { 144 | assert_raises(function () { 145 | Empty.set("bar", 2); 146 | }, "Key bar not found"); 147 | 148 | var x = Foo; 149 | var x2 = x.set("foo", 3); 150 | assert(x.get("foo") === 1); 151 | assert(x2.get("foo") === 3); 152 | }); 153 | 154 | test("modify", function () { 155 | var ran = false; 156 | 157 | assert_raises(function () { 158 | Empty.modify("foo", function (x) { 159 | ran = true; 160 | return x + 1; 161 | }); 162 | }, "Key foo not found"); 163 | 164 | assert(ran === false); 165 | 166 | 167 | var ran = false; 168 | 169 | var x = Foo; 170 | var x2 = x.modify("foo", function (x) { 171 | ran = true; 172 | assert(x === 1); 173 | return x + 5; 174 | }); 175 | 176 | assert(ran === true); 177 | 178 | assert(x.get("foo") === 1); 179 | assert(x2.get("foo") === 6); 180 | }); 181 | 182 | test("update", function () { 183 | verify_record(Record({ foo: 1 }), { foo: 1 }); 184 | verify_record(Record({ foo: 1 }).update(Record({ foo: 2 })), { foo: 2 }); 185 | verify_record(Record({ foo: 1 }).update([["foo", 3]]), { foo: 3 }); 186 | verify_record(Record({ foo: 1 }).update({ foo: 3 }), { foo: 3 }); 187 | 188 | assert_raises(function () { 189 | Record({ foo: 1 }).update(Record({ foo: 2, bar: 3 })); 190 | }, "Key bar not found"); 191 | 192 | 193 | verify_record(Record([["foo", 2]]).update([["foo", 3]]), { foo: 3 }); 194 | verify_record(Record([["foo", 2]]).update([Tuple(["foo", 3])]), { foo: 3 }); 195 | 196 | assert_raises(function () { 197 | Record().update(Object.create(null)); 198 | }, "Cannot convert object to primitive value"); 199 | 200 | assert_raises(function () { 201 | Record([["foo", 2]]).update([List(["foo", 3])]); 202 | }, "Expected array or Tuple but got: (List\n \"foo\"\n 3)"); 203 | 204 | assert_raises(function () { 205 | Record([["foo", 2]]).update([{}]); 206 | }, "Expected array or Tuple but got: [object Object]"); 207 | 208 | assert_raises(function () { 209 | Record([["foo", 2]]).update([[]]); 210 | }, "Expected array with 2 elements but got 0 elements"); 211 | 212 | assert_raises(function () { 213 | Record([["foo", 2]]).update([["foo"]]); 214 | }, "Expected array with 2 elements but got 1 element"); 215 | 216 | assert_raises(function () { 217 | Record([["foo", 2]]).update([["foo", 2, 3]]); 218 | }, "Expected array with 2 elements but got 3 elements"); 219 | 220 | assert_raises(function () { 221 | Record([["foo", 2]]).update([Tuple([])]); 222 | }, "Expected Tuple with 2 elements but got 0 elements"); 223 | 224 | assert_raises(function () { 225 | Record([["foo", 2]]).update([Tuple(["foo"])]); 226 | }, "Expected Tuple with 2 elements but got 1 element"); 227 | 228 | assert_raises(function () { 229 | Record([["foo", 2]]).update([Tuple(["foo", 2, 3])]); 230 | }, "Expected Tuple with 2 elements but got 3 elements"); 231 | }); 232 | 233 | test("complex keys", function () { 234 | var o = Dict().set({}, 1); 235 | 236 | assert_raises(function () { 237 | Record(o); 238 | }, "Expected key to be a string or Tag but got [object Object]"); 239 | 240 | assert_raises(function () { 241 | Record([[{}, 1]]); 242 | }, "Expected key to be a string or Tag but got [object Object]"); 243 | 244 | assert_raises(function () { 245 | Foo.get({}); 246 | }, "Expected key to be a string or Tag but got [object Object]"); 247 | 248 | assert_raises(function () { 249 | Foo.set({}, 5); 250 | }, "Expected key to be a string or Tag but got [object Object]"); 251 | 252 | assert_raises(function () { 253 | Foo.modify({}, function () { throw new Error("FAIL") }); 254 | }, "Expected key to be a string or Tag but got [object Object]"); 255 | }); 256 | 257 | test("=== when not modified", function () { 258 | var x = Foo; 259 | 260 | assert(x.set("foo", 1) === x); 261 | assert(x.set("foo", 2) !== x); 262 | 263 | assert(x.modify("foo", function () { 264 | return 1; 265 | }) === x); 266 | 267 | assert(x.modify("foo", function () { 268 | return 2; 269 | }) !== x); 270 | 271 | var x = Record({ foo: 1 }); 272 | assert(Record(x) === x); 273 | assert(Record({ foo: 1 }) !== x); 274 | 275 | assert(x.update([]) === x); 276 | assert(x.update([["foo", 1]]) === x); 277 | assert(x.update([["foo", 2]]) !== x); 278 | }); 279 | 280 | test("equal", function () { 281 | assert(!equal(Empty, Foo)); 282 | assert(equal(Empty, Empty)); 283 | assert(equal(Foo, Foo)); 284 | 285 | assert(equal(Record({}), Record({}))); 286 | assert(equal(Record({ foo: 1 }), Record({ foo: 1 }))); 287 | 288 | assert(!equal(Foo, Record({ foo: 2 }))); 289 | assert(equal(Foo, Record({ foo: 1 }))); 290 | assert(equal(Record({ foo: 2 }), Record({ foo: 2 }))); 291 | assert(!equal(Record({ foo: 2 }), Record({ foo: 3 }))); 292 | 293 | assert(equal(Record([["foo", 1], ["bar", 2]]), Record([["bar", 2], ["foo", 1]]))); 294 | }); 295 | 296 | test("toJS", function () { 297 | assert(deepEqual(toJS(Empty), {})); 298 | assert(deepEqual(toJS(Foo), { foo: 1 })); 299 | assert(deepEqual(toJS(Record({ foo: Record({ bar: 2 }) })), 300 | { foo: { bar: 2 } })); 301 | }); 302 | 303 | test("toJSON", function () { 304 | verify_json(Empty, {}); 305 | verify_json(Foo, { foo: 1 }); 306 | verify_json(Record({ foo: Record({ bar: 2 }) }), { foo: { bar: 2 } }); 307 | }); 308 | 309 | test("each", function () { 310 | test_each_dict(Record([]), []); 311 | test_each_dict(Record([["foo", 2]]), [["foo", 2]]); 312 | test_each_dict(Record([["foo", 2], ["bar", 3]]), [["bar", 3], ["foo", 2]]); 313 | test_each_dict(Record([["bar", 3], ["foo", 2]]), [["bar", 3], ["foo", 2]]); 314 | test_each_dict(Record([["2", 1], ["1", 2]]), [["1", 2], ["2", 1]]); 315 | 316 | var corge = Record({ corge: 3 }); 317 | test_each_dict(Record([["foo", 1], ["qux", corge], ["bar", 2]]), [["bar", 2], ["foo", 1], ["qux", corge]]); 318 | }); 319 | 320 | // TODO code duplication 321 | test("zip", function () { 322 | var a = [["a", 1], ["b", 2], ["c", 3], ["d", 4], 323 | ["e", 5], ["f", 6], ["g", 7], ["h", 8]]; 324 | assert(deepEqual(toArray(zip(Record(a))), toArray(zip(a)))); 325 | }); 326 | }); 327 | } 328 | -------------------------------------------------------------------------------- /src/Test/Ref.js: -------------------------------------------------------------------------------- 1 | import { Ref, isRef, Dict, equal } from "../Immutable"; 2 | import { assert, context, test, assert_raises } from "./assert"; 3 | 4 | export function test_Ref() { 5 | context("Ref", function () { 6 | var ref1 = Ref(1); 7 | var ref2 = Ref(2); 8 | 9 | test("isRef", function () { 10 | assert(!isRef(Dict())); 11 | assert(isRef(ref1)); 12 | assert(isRef(ref2)); 13 | }); 14 | 15 | test("toString", function () { 16 | assert("" + ref1 === "(Ref 1)"); 17 | assert("" + ref2 === "(Ref 2)"); 18 | assert("" + Ref(50) === "(Ref 3)"); 19 | assert("" + Ref([100]) === "(Ref 4)"); 20 | }); 21 | 22 | test("init", function () { 23 | assert_raises(function () { 24 | Ref(); 25 | }, "Expected 1 to 2 arguments but got 0"); 26 | 27 | assert_raises(function () { 28 | Ref(1, 2, 3); 29 | }, "Expected 1 to 2 arguments but got 3"); 30 | }); 31 | 32 | test("get", function () { 33 | assert(ref1.get() === 1); 34 | assert(ref2.get() === 2); 35 | }); 36 | 37 | test("set", function () { 38 | assert(ref1.get() === 1); 39 | ref1.set(50); 40 | assert(ref1.get() === 50); 41 | 42 | var ran1 = false; 43 | 44 | var x = Ref(5, function (before, after) { 45 | assert(ran1 === false); 46 | assert(before === 5); 47 | assert(after === 10); 48 | ran1 = true; 49 | return after + 1; 50 | }); 51 | 52 | assert(x.get() === 5); 53 | assert(ran1 === false); 54 | 55 | x.set(10); 56 | 57 | assert(x.get() === 11); 58 | assert(ran1 === true); 59 | 60 | 61 | var ran2 = false; 62 | 63 | var x = Ref(5, function (before, after) { 64 | assert(ran2 === false); 65 | ran2 = true; 66 | return before; 67 | }); 68 | 69 | assert(x.get() === 5); 70 | assert(ran2 === false); 71 | 72 | x.set(5); 73 | 74 | assert(x.get() === 5); 75 | assert(ran2 === true); 76 | 77 | 78 | var ran3 = false; 79 | var ran4 = false; 80 | 81 | var x = Ref(5, function (before, after) { 82 | assert(ran4 === false); 83 | 84 | if (ran3 === false) { 85 | assert(x.get() === 5); 86 | assert(before === 5); 87 | assert(after === 10); 88 | 89 | ran3 = true; 90 | 91 | x.set(20); 92 | 93 | assert(x.get() === 25); 94 | assert(ran4 === true); 95 | return x.get(); 96 | 97 | } else { 98 | assert(x.get() === 5); 99 | assert(before === 5); 100 | assert(after === 20); 101 | 102 | ran4 = true; 103 | return after + 5; 104 | } 105 | }); 106 | 107 | assert(x.get() === 5); 108 | assert(ran3 === false); 109 | assert(ran4 === false); 110 | 111 | x.set(10); 112 | 113 | assert(x.get() === 25); 114 | assert(ran3 === true); 115 | assert(ran4 === true); 116 | }); 117 | 118 | test("modify", function () { 119 | var ran1 = false; 120 | var ran2 = false; 121 | 122 | var x = Ref(5, function (before, after) { 123 | assert(ran1 === false); 124 | assert(before === 5); 125 | assert(after === 10); 126 | ran1 = true; 127 | return after + 1; 128 | }); 129 | 130 | assert(x.get() === 5); 131 | assert(ran1 === false); 132 | assert(ran2 === false); 133 | 134 | x.modify(function (x) { 135 | assert(x === 5); 136 | ran2 = true; 137 | return 10; 138 | }); 139 | 140 | assert(x.get() === 11); 141 | assert(ran1 === true); 142 | assert(ran2 === true); 143 | 144 | 145 | var ran3 = false; 146 | var ran4 = false; 147 | 148 | var x = Ref(5, function (before, after) { 149 | assert(ran3 === false); 150 | assert(before === 5); 151 | assert(after === 5); 152 | ran3 = true; 153 | return after; 154 | }); 155 | 156 | assert(x.get() === 5); 157 | assert(ran3 === false); 158 | assert(ran4 === false); 159 | 160 | x.modify(function (x) { 161 | assert(x === 5); 162 | ran4 = true; 163 | return 5; 164 | }); 165 | 166 | assert(x.get() === 5); 167 | assert(ran3 === true); 168 | assert(ran4 === true); 169 | }); 170 | 171 | test("equal", function () { 172 | assert(!equal(ref1, ref2)); 173 | assert(equal(ref1, ref1)); 174 | assert(equal(ref2, ref2)); 175 | 176 | assert(!equal(Ref(1), Ref(1))); 177 | assert(!equal(ref1, Ref(1))); 178 | }); 179 | }); 180 | } 181 | -------------------------------------------------------------------------------- /src/Test/Set.js: -------------------------------------------------------------------------------- 1 | import { isSet, toJS, Set, Dict, SortedSet, simpleSort, 2 | isSortedSet, equal, toJSON, toArray, zip } from "../Immutable"; 3 | import { assert, context, test, assert_raises } from "./assert"; 4 | import { verify_tree, deepEqual, otherSort, verify_json, random_list, 5 | test_each } from "./util"; 6 | 7 | function verify_set(tree, array) { 8 | assert(isSet(tree)); 9 | 10 | verify_tree(tree); 11 | 12 | assert(deepEqual(toJS(tree), array)); 13 | 14 | return tree; 15 | } 16 | 17 | export function test_Set() { 18 | context("Set", function () { 19 | var empty_set = Set(); 20 | var five_set = Set().add(1).add(2).add(3).add(4).add(5); 21 | 22 | test("isSet", function () { 23 | assert(!isSet(Dict())); 24 | 25 | assert(isSet(Set())); 26 | assert(isSet(SortedSet(simpleSort))); 27 | 28 | assert(isSortedSet(SortedSet(simpleSort))); 29 | assert(!isSortedSet(Set())); 30 | }); 31 | 32 | test("verify", function () { 33 | verify_set(empty_set, []); 34 | verify_set(five_set, [1, 2, 3, 4, 5]); 35 | }); 36 | 37 | test("init", function () { 38 | verify_set(Set([1, 2, 3]), [1, 2, 3]); 39 | 40 | assert_raises(function () { 41 | Set(null); 42 | }, "Cannot read property '(UUIDTag 6199065c-b518-4cb3-8b41-ab70a9769ec3)' of null"); 43 | 44 | assert_raises(function () { 45 | SortedSet(simpleSort, null); 46 | }, "Cannot read property '(UUIDTag 6199065c-b518-4cb3-8b41-ab70a9769ec3)' of null"); 47 | 48 | assert_raises(function () { 49 | SortedSet(); 50 | }, "Expected 1 to 2 arguments but got 0"); 51 | 52 | verify_set(Set(), []); 53 | verify_set(SortedSet(simpleSort), []); 54 | }); 55 | 56 | test("isEmpty", function () { 57 | assert(empty_set.isEmpty()); 58 | assert(!five_set.isEmpty()); 59 | }); 60 | 61 | test("has", function () { 62 | assert(!empty_set.has(1)); 63 | assert(!five_set.has(0)); 64 | assert(five_set.has(1)); 65 | assert(five_set.has(2)); 66 | assert(five_set.has(3)); 67 | assert(five_set.has(4)); 68 | assert(five_set.has(5)); 69 | assert(!five_set.has(6)); 70 | }); 71 | 72 | test("add", function () { 73 | verify_set(empty_set, []); 74 | verify_set(empty_set.add(5), [5]); 75 | verify_set(empty_set, []); 76 | 77 | verify_set(five_set, [1, 2, 3, 4, 5]); 78 | verify_set(five_set.add(5), [1, 2, 3, 4, 5]); 79 | verify_set(five_set, [1, 2, 3, 4, 5]); 80 | }); 81 | 82 | test("remove", function () { 83 | verify_set(empty_set.remove(1), []); 84 | 85 | verify_set(five_set.remove(1), [2, 3, 4, 5]); 86 | verify_set(five_set.remove(1).remove(4), [2, 3, 5]); 87 | }); 88 | 89 | test("removeAll", function () { 90 | verify_set(empty_set.removeAll(), []); 91 | verify_set(five_set.removeAll(), []); 92 | 93 | var empty_sorted_set = SortedSet(simpleSort, []); 94 | var five_sorted_set = SortedSet(simpleSort, [1, 2, 3, 4, 5]); 95 | 96 | verify_set(empty_sorted_set.removeAll(), []); 97 | verify_set(five_sorted_set.removeAll(), []); 98 | 99 | assert(empty_sorted_set.sort === empty_sorted_set.removeAll().sort); 100 | assert(empty_sorted_set.hash_fn === empty_sorted_set.removeAll().hash_fn); 101 | 102 | assert(five_sorted_set.sort === five_sorted_set.removeAll().sort); 103 | assert(five_sorted_set.hash_fn === five_sorted_set.removeAll().hash_fn); 104 | }); 105 | 106 | test("union", function () { 107 | verify_set(five_set.union(five_set), [1, 2, 3, 4, 5]); 108 | verify_set(five_set.union(Set([1, 2, 6, 9])), [1, 2, 3, 4, 5, 6, 9]); 109 | verify_set(Set([1, 2]).union(five_set), [1, 2, 3, 4, 5]); 110 | verify_set(Set([1, 2, 6]).union(five_set), [1, 2, 3, 4, 5, 6]); 111 | verify_set(five_set.union([1, 2, 6, 9]), [1, 2, 3, 4, 5, 6, 9]); 112 | }); 113 | 114 | test("intersect", function () { 115 | verify_set(five_set.intersect(five_set), [1, 2, 3, 4, 5]); 116 | verify_set(empty_set.intersect(five_set), []); 117 | verify_set(five_set.intersect(empty_set), []); 118 | verify_set(five_set.intersect([1, 3, 4]), [1, 3, 4]); 119 | verify_set(five_set.intersect([1, 3, 4, 6, 10, 20]), [1, 3, 4]); 120 | }); 121 | 122 | test("disjoint", function () { 123 | verify_set(five_set.disjoint(five_set), []); 124 | verify_set(five_set.disjoint(empty_set), [1, 2, 3, 4, 5]); 125 | verify_set(empty_set.disjoint(five_set), [1, 2, 3, 4, 5]); 126 | verify_set(five_set.disjoint([1, 2, 3]), [4, 5]); 127 | verify_set(five_set.disjoint([1, 2, 3, 6, 7, 8]), [4, 5, 6, 7, 8]); 128 | verify_set(five_set.disjoint([1, 2, 3, 3, 6, 7, 8]), [4, 5, 6, 7, 8]); 129 | verify_set(five_set.disjoint([1, 2, 3, 3, 3, 6, 7, 8]), [4, 5, 6, 7, 8]); 130 | verify_set(five_set.disjoint([1, 2, 3, 3, 6, 6, 6, 7, 8]), [4, 5, 6, 7, 8]); 131 | }); 132 | 133 | test("subtract", function () { 134 | verify_set(five_set.subtract(empty_set), [1, 2, 3, 4, 5]); 135 | verify_set(empty_set.subtract(five_set), []); 136 | verify_set(five_set.subtract(five_set), []); 137 | verify_set(five_set.subtract([1, 2, 3]), [4, 5]); 138 | verify_set(five_set.subtract([1, 2, 3, 6, 7, 9]), [4, 5]); 139 | }); 140 | 141 | test("complex elements", function () { 142 | var o = Set(); 143 | 144 | var m1 = {}; 145 | var m2 = {}; 146 | 147 | var i1 = Set(); 148 | var i2 = Set(); 149 | var i3 = Set([1, 2, 3]); 150 | 151 | o = o.add(m1); 152 | o = o.add(m2); 153 | o = o.add(i1); 154 | o = o.add(i2); 155 | o = o.add(i3); 156 | 157 | assert(o.has(m1)); 158 | assert(o.has(m2)); 159 | assert(o.has(i1)); 160 | assert(o.has(i2)); 161 | assert(o.has(i3)); 162 | 163 | o = o.remove(m1); 164 | o = o.remove(m2); 165 | o = o.remove(i1); 166 | o = o.remove(i3); 167 | 168 | assert(!o.has(m1)); 169 | assert(!o.has(m2)); 170 | assert(!o.has(i1)); 171 | assert(!o.has(i2)); 172 | assert(!o.has(i3)); 173 | }); 174 | 175 | test("=== when not modified", function () { 176 | assert(Set(five_set) === five_set); 177 | 178 | var x = SortedSet(simpleSort, five_set); 179 | assert(x !== five_set); 180 | assert(Set(x) !== x); 181 | assert(SortedSet(simpleSort, x) === x); 182 | 183 | var x = SortedSet(simpleSort, five_set); 184 | assert(SortedSet(otherSort, x) !== x); 185 | 186 | var x = SortedSet(otherSort, five_set); 187 | assert(SortedSet(simpleSort, x) !== x); 188 | 189 | var x = SortedSet(otherSort, five_set); 190 | assert(SortedSet(otherSort, x) === x); 191 | 192 | 193 | assert(empty_set.union(empty_set) === empty_set); 194 | assert(empty_set.union(five_set) !== five_set); 195 | assert(five_set.union(empty_set) === five_set); 196 | assert(five_set.union(five_set) === five_set); 197 | assert(five_set.union(Set([1, 2, 3])) === five_set); 198 | 199 | assert(Set(five_set) === five_set); 200 | assert(SortedSet(simpleSort, five_set) !== five_set); 201 | 202 | assert(empty_set.remove(1) === empty_set); 203 | 204 | var set1 = Set([Set([])]); 205 | 206 | assert(set1.add(Set([])) !== set1); 207 | 208 | assert(five_set.add(5) === five_set); 209 | assert(five_set.add(6) !== five_set); 210 | assert(five_set.remove(5) !== five_set); 211 | }); 212 | 213 | test("equal", function () { 214 | assert(!equal(empty_set, five_set)); 215 | assert(equal(empty_set, empty_set)); 216 | assert(equal(five_set, five_set)); 217 | assert(equal(Set(), Set())); 218 | assert(equal(Set([1]), Set([1]))); 219 | assert(equal(Set([Set([1])]), Set([Set([1])]))); 220 | assert(!equal(Set([Set([1])]), Set([Set([2])]))); 221 | 222 | assert(equal(SortedSet(simpleSort, [1, 2, 3]), 223 | SortedSet(simpleSort, [1, 2, 3]))); 224 | 225 | assert(!equal(SortedSet(simpleSort, [1, 2, 3]), 226 | SortedSet(otherSort, [1, 2, 3]))); 227 | 228 | assert(!equal(SortedSet(simpleSort, [1, 2, 3]), 229 | Set([1, 2, 3]))); 230 | }); 231 | 232 | test("toJS", function () { 233 | assert(deepEqual(toJS(empty_set), [])); 234 | assert(deepEqual(toJS(five_set), [1, 2, 3, 4, 5])); 235 | assert(deepEqual(toJS(Set([1, 2, Set([3])])), 236 | [[3], 1, 2])); 237 | }); 238 | 239 | test("toJSON", function () { 240 | verify_json(empty_set, []); 241 | verify_json(five_set, [1, 2, 3, 4, 5]); 242 | verify_json(Set([4, 5, Set([1, 2, 3])]), [[1, 2, 3], 4, 5]); 243 | 244 | assert_raises(function () { 245 | toJSON(SortedSet(simpleSort, [])); 246 | }, "Cannot convert SortedSet to JSON"); 247 | }); 248 | 249 | test("random elements", function () { 250 | var o = Set(); 251 | var a = []; 252 | 253 | var sort = o.sort; 254 | var hash_fn = o.hash_fn; 255 | 256 | // TODO utilities for these 257 | function push_sorted(a, x, sort) { 258 | for (var i = 0, l = a.length; i < l; ++i) { 259 | if (sort(hash_fn(x), hash_fn(a[i])) <= 0) { 260 | a.splice(i, 0, x); 261 | return; 262 | } 263 | } 264 | a.push(x); 265 | } 266 | 267 | function remove(a, x) { 268 | var index = a.indexOf(x); 269 | assert(index !== -1); 270 | a.splice(index, 1); 271 | } 272 | 273 | verify_set(o, a); 274 | 275 | random_list(200).forEach(function (i) { 276 | o = o.add(i); 277 | push_sorted(a, i, sort); 278 | verify_set(o, a); 279 | }); 280 | 281 | random_list(200).forEach(function (i) { 282 | o = o.remove(i); 283 | remove(a, i); 284 | verify_set(o, a); 285 | }); 286 | 287 | verify_set(o, []); 288 | }); 289 | 290 | test("each", function () { 291 | test_each(Set(), []); 292 | 293 | var four = Set([4]); 294 | test_each(Set([four, 1, 2, 3]), [four, 1, 2, 3]); 295 | }); 296 | 297 | test("toString", function () { 298 | assert("" + Set([0, -0]) === "(Set\n 0)"); 299 | assert("" + Set() === "(Set)"); 300 | assert("" + SortedSet(simpleSort) === "(SortedSet (Mutable 4))"); 301 | assert("" + Set([1, 2, 3, 4, 5]) === "(Set\n 1\n 2\n 3\n 4\n 5)"); 302 | assert("" + SortedSet(simpleSort, [1, 2, 3, 4, 5]) === "(SortedSet (Mutable 4)\n 1\n 2\n 3\n 4\n 5)"); 303 | assert("" + Set([Set([1, 2, 3])]) === "(Set\n (Set\n 1\n 2\n 3))"); 304 | assert("" + Set([{}]) === "(Set\n (Mutable 10))"); 305 | assert("" + Set([{}]) === "(Set\n (Mutable 11))"); 306 | }); 307 | 308 | // TODO 309 | /*test("zip", function () { 310 | var a = random_list(200); 311 | assert(deepEqual(toArray(zip(Set(a))), toArray(zip(a)))); 312 | });*/ 313 | }); 314 | } 315 | -------------------------------------------------------------------------------- /src/Test/Stack.js: -------------------------------------------------------------------------------- 1 | import { isStack, toJS, Stack, Queue, equal } from "../Immutable"; 2 | import { assert, context, test, assert_raises } from "./assert"; 3 | import { deepEqual, verify_json, test_each } from "./util"; 4 | 5 | function verify_stack(stack, array) { 6 | assert(isStack(stack)); 7 | 8 | assert(deepEqual(toJS(stack), array)); 9 | 10 | return stack; 11 | } 12 | 13 | export function test_Stack() { 14 | context("Stack", function () { 15 | var empty_stack = Stack(); 16 | var five_stack = Stack().push(1).push(2).push(3).push(4).push(5); 17 | 18 | test("isStack", function () { 19 | assert(!isStack(Queue())); 20 | assert(isStack(Stack())); 21 | }); 22 | 23 | test("verify", function () { 24 | verify_stack(empty_stack, []); 25 | verify_stack(five_stack, [1, 2, 3, 4, 5]); 26 | }); 27 | 28 | test("init", function () { 29 | verify_stack(Stack([1, 2, 3]), [1, 2, 3]); 30 | 31 | assert_raises(function () { 32 | Stack(null); 33 | }, "Cannot read property '(UUIDTag 6199065c-b518-4cb3-8b41-ab70a9769ec3)' of null"); 34 | 35 | verify_stack(Stack(), []); 36 | }); 37 | 38 | test("isEmpty", function () { 39 | assert(empty_stack.isEmpty()); 40 | assert(!five_stack.isEmpty()); 41 | }); 42 | 43 | test("size", function () { 44 | assert(empty_stack.size() === 0); 45 | assert(five_stack.size() === 5); 46 | }); 47 | 48 | test("peek", function () { 49 | assert_raises(function () { 50 | empty_stack.peek(); 51 | }, "Cannot peek from an empty stack"); 52 | 53 | assert(empty_stack.peek(50) === 50); 54 | 55 | assert(five_stack.peek() === 5); 56 | assert(five_stack.peek(50) === 5); 57 | }); 58 | 59 | test("push", function () { 60 | var x = empty_stack.push(10); 61 | 62 | verify_stack(empty_stack, []); 63 | verify_stack(x, [10]); 64 | 65 | assert(empty_stack.size() === 0); 66 | assert(x.size() === 1); 67 | assert(x.peek() === 10); 68 | 69 | verify_stack(five_stack.push(10), [1, 2, 3, 4, 5, 10]); 70 | verify_stack(five_stack.push(10).push(20), [1, 2, 3, 4, 5, 10, 20]); 71 | verify_stack(five_stack, [1, 2, 3, 4, 5]); 72 | 73 | verify_stack(Stack().push(5).push(4).push(3).push(2).push(1), 74 | [5, 4, 3, 2, 1]); 75 | }); 76 | 77 | test("pop", function () { 78 | assert_raises(function () { 79 | empty_stack.pop(); 80 | }, "Cannot pop from an empty stack"); 81 | 82 | verify_stack(five_stack.pop(), [1, 2, 3, 4]); 83 | verify_stack(five_stack.pop().pop(), [1, 2, 3]); 84 | }); 85 | 86 | test("removeAll", function () { 87 | verify_stack(empty_stack.removeAll(), []); 88 | verify_stack(five_stack.removeAll(), []); 89 | }); 90 | 91 | test("concat", function () { 92 | verify_stack(empty_stack.concat(empty_stack), []); 93 | verify_stack(five_stack.concat(five_stack), [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]); 94 | verify_stack(Stack([10, 20, 30]).concat(five_stack), [10, 20, 30, 1, 2, 3, 4, 5]); 95 | verify_stack(five_stack.concat(Stack([10, 20, 30])), [1, 2, 3, 4, 5, 10, 20, 30]); 96 | verify_stack(five_stack.concat([10, 20, 30]), [1, 2, 3, 4, 5, 10, 20, 30]); 97 | }); 98 | 99 | test("=== when not modified", function () { 100 | assert(Stack(five_stack) === five_stack); 101 | 102 | assert(empty_stack.concat(empty_stack) === empty_stack); 103 | assert(five_stack.concat(empty_stack) === five_stack); 104 | assert(empty_stack.concat(five_stack) !== five_stack); 105 | }); 106 | 107 | test("equal", function () { 108 | assert(equal(empty_stack, empty_stack)); 109 | assert(equal(five_stack, five_stack)); 110 | 111 | assert(equal(Stack([1, 2, 3]), Stack([1, 2, 3]))); 112 | assert(!equal(Stack([1, 2, 3]), Stack([1, 2, 4]))); 113 | assert(!equal(Stack([1, 2, 3]), Stack([1, 3, 2]))); 114 | 115 | assert(equal(Stack([1, 2, 3, 4, 5]), five_stack)); 116 | assert(equal(five_stack, Stack([1, 2, 3, 4, 5]))); 117 | 118 | assert(equal(Stack([Stack([1, 2, 3])]), Stack([Stack([1, 2, 3])]))); 119 | }); 120 | 121 | test("toJS", function () { 122 | assert(deepEqual(toJS(empty_stack), [])); 123 | assert(deepEqual(toJS(five_stack), [1, 2, 3, 4, 5])); 124 | assert(deepEqual(toJS(Stack([1, 2, Stack([3])])), [1, 2, [3]])); 125 | }); 126 | 127 | test("toJSON", function () { 128 | verify_json(empty_stack, []); 129 | verify_json(five_stack, [1, 2, 3, 4, 5]); 130 | verify_json(Stack([4, 5, Stack([1, 2, 3])]), [4, 5, [1, 2, 3]]); 131 | }); 132 | 133 | test("each", function () { 134 | test_each(Stack(), []); 135 | 136 | var x = Stack([3]); 137 | test_each(Stack([1, 2, x, 4]), [1, 2, x, 4]); 138 | }); 139 | 140 | test("toString", function () { 141 | assert("" + empty_stack === "(Stack)"); 142 | assert("" + Stack([1, 2, 3]) === "(Stack\n 1\n 2\n 3)"); 143 | assert("" + Stack([1, Stack([2]), 3]) === "(Stack\n 1\n (Stack\n 2)\n 3)"); 144 | }); 145 | 146 | /* 147 | // TODO code duplication 148 | test("zip", function () { 149 | var a = random_list(200); 150 | assert(deepEqual(toArray(zip(Stack(a))), toArray(zip(a)))); 151 | });*/ 152 | }); 153 | } 154 | -------------------------------------------------------------------------------- /src/Test/Tag.js: -------------------------------------------------------------------------------- 1 | import { Tag, UUIDTag, isTag, isUUIDTag, Dict, equal, Record, 2 | fromJS, toJS, toJSON, fromJSON } from "../Immutable"; 3 | import { assert, context, test, assert_raises } from "./assert"; 4 | import { deepEqual } from "./util"; 5 | 6 | export function test_Tag() { 7 | context("Tag", function () { 8 | var tag1 = Tag(); 9 | var tag2 = Tag(); 10 | var uuid_tag1 = UUIDTag("dc353abd-d920-4c17-b911-55bd1c78c06f"); 11 | var uuid_tag2 = UUIDTag("2a95bab0-ae96-4f07-b7a5-227fe3d394d4"); 12 | 13 | test("isTag", function () { 14 | assert(!isTag("foo")); 15 | assert(!isTag(Dict())); 16 | assert(isTag(tag1)); 17 | assert(isTag(tag2)); 18 | assert(isTag(uuid_tag1)); 19 | assert(isTag(uuid_tag2)); 20 | 21 | assert(!isUUIDTag("foo")); 22 | assert(!isUUIDTag(Dict())); 23 | assert(!isUUIDTag(tag1)); 24 | assert(!isUUIDTag(tag2)); 25 | assert(isUUIDTag(uuid_tag1)); 26 | assert(isUUIDTag(uuid_tag2)); 27 | }); 28 | 29 | test("toString", function () { 30 | assert("" + tag1 === "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)"); 31 | assert("" + tag2 === "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 2)"); 32 | assert("" + Tag() === "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 3)"); 33 | assert("" + uuid_tag1 === "(UUIDTag dc353abd-d920-4c17-b911-55bd1c78c06f)"); 34 | assert("" + uuid_tag2 === "(UUIDTag 2a95bab0-ae96-4f07-b7a5-227fe3d394d4)"); 35 | assert("" + UUIDTag("2a95bab0-ae96-4f07-b7a5-227fe3d394d4") === "(UUIDTag 2a95bab0-ae96-4f07-b7a5-227fe3d394d4)"); 36 | }); 37 | 38 | test("init", function () { 39 | assert_raises(function () { 40 | Tag(1); 41 | }, "Expected 0 arguments but got 1"); 42 | 43 | assert_raises(function () { 44 | Tag(1, 2); 45 | }, "Expected 0 arguments but got 2"); 46 | 47 | assert_raises(function () { 48 | UUIDTag(); 49 | }, "Expected 1 argument but got 0"); 50 | 51 | assert_raises(function () { 52 | UUIDTag(1, 2); 53 | }, "Expected 1 argument but got 2"); 54 | 55 | assert_raises(function () { 56 | UUIDTag("foo"); 57 | }, "Expected a lower-case UUID, but got: foo"); 58 | }); 59 | 60 | test("equal", function () { 61 | assert(!equal(tag1, tag2)); 62 | assert(!equal(tag1, uuid_tag1)); 63 | assert(equal(tag1, tag1)); 64 | assert(equal(tag2, tag2)); 65 | 66 | assert(!equal(uuid_tag1, uuid_tag2)); 67 | assert(equal(uuid_tag1, uuid_tag1)); 68 | assert(equal(uuid_tag2, uuid_tag2)); 69 | 70 | assert(equal(uuid_tag2, UUIDTag("2a95bab0-ae96-4f07-b7a5-227fe3d394d4"))); 71 | 72 | assert(!equal(Tag(), Tag())); 73 | assert(!equal(tag1, Tag())); 74 | }); 75 | 76 | test("===", function () { 77 | assert(tag1 !== tag2); 78 | assert(tag1 !== uuid_tag1); 79 | assert(tag1 === tag1); 80 | assert(tag2 === tag2); 81 | 82 | assert(uuid_tag1 !== uuid_tag2); 83 | assert(uuid_tag1 === uuid_tag1); 84 | assert(uuid_tag2 === uuid_tag2); 85 | 86 | assert(uuid_tag2 === UUIDTag("2a95bab0-ae96-4f07-b7a5-227fe3d394d4")); 87 | 88 | assert(Tag() !== Tag()); 89 | assert(tag1 !== Tag()); 90 | }); 91 | 92 | test("Dict", function () { 93 | var x = Dict(); 94 | 95 | x = x.set(tag1, 1); 96 | x = x.set(tag2, 2); 97 | x = x.set(uuid_tag1, 3); 98 | x = x.set(uuid_tag2, 4); 99 | 100 | assert(x.get(tag1) === 1); 101 | assert(x.get(tag2) === 2); 102 | assert(x.get(uuid_tag1) === 3); 103 | assert(x.get(uuid_tag2) === 4); 104 | 105 | assert("" + x === "(Dict\n (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1) = 1\n (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 2) = 2\n (UUIDTag 2a95bab0-ae96-4f07-b7a5-227fe3d394d4) = 4\n (UUIDTag dc353abd-d920-4c17-b911-55bd1c78c06f) = 3)"); 106 | assert(deepEqual(toJS(x), { 107 | "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)": 1, 108 | "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 2)": 2, 109 | "(UUIDTag 2a95bab0-ae96-4f07-b7a5-227fe3d394d4)": 4, 110 | "(UUIDTag dc353abd-d920-4c17-b911-55bd1c78c06f)": 3 111 | })); 112 | }); 113 | 114 | test("Record", function () { 115 | var x = Record([[tag1, 1], [tag2, 2], [uuid_tag1, 3], [uuid_tag2, 4]]); 116 | 117 | assert(x.get(tag1) === 1); 118 | assert(x.get(tag2) === 2); 119 | assert(x.get(uuid_tag1) === 3); 120 | assert(x.get(uuid_tag2) === 4); 121 | 122 | assert("" + x === "(Record\n (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1) = 1\n (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 2) = 2\n (UUIDTag 2a95bab0-ae96-4f07-b7a5-227fe3d394d4) = 4\n (UUIDTag dc353abd-d920-4c17-b911-55bd1c78c06f) = 3)"); 123 | assert(deepEqual(toJS(x), { 124 | "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)": 1, 125 | "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 2)": 2, 126 | "(UUIDTag dc353abd-d920-4c17-b911-55bd1c78c06f)": 3, 127 | "(UUIDTag 2a95bab0-ae96-4f07-b7a5-227fe3d394d4)": 4 128 | })); 129 | }); 130 | 131 | test("toJS", function () { 132 | assert(toJS(tag1) === tag1); 133 | assert(toJS(uuid_tag1) === uuid_tag1); 134 | 135 | assert(fromJS(tag1) === tag1); 136 | assert(fromJS(uuid_tag1) === uuid_tag1); 137 | }); 138 | 139 | test("toJSON", function () { 140 | assert_raises(function () { 141 | toJSON(tag1); 142 | }, "Cannot convert Tag to JSON, use UUIDTag instead: (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)"); 143 | 144 | assert(toJSON(uuid_tag1) === uuid_tag1); 145 | 146 | assert_raises(function () { 147 | fromJSON(tag1); 148 | }, "Cannot convert Tag from JSON, use UUIDTag instead: (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)"); 149 | 150 | assert(fromJSON(uuid_tag1) === uuid_tag1); 151 | 152 | 153 | var x = Dict([[tag1, 1]]); 154 | 155 | assert(deepEqual(toJS(x), { "(Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)": 1 })); 156 | 157 | assert_raises(function () { 158 | toJSON(x); 159 | }, "Cannot convert Tag to JSON, use UUIDTag instead: (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 1)"); 160 | 161 | 162 | var x = Dict([[uuid_tag1, 1]]); 163 | 164 | assert(deepEqual(toJS(x), { "(UUIDTag dc353abd-d920-4c17-b911-55bd1c78c06f)": 1 })); 165 | 166 | assert(equal(fromJSON(toJSON(x)), x)); 167 | }); 168 | }); 169 | } 170 | -------------------------------------------------------------------------------- /src/Test/Tuple.js: -------------------------------------------------------------------------------- 1 | import { isTuple, toJS, Tuple, List, equal, Type } from "../Immutable"; 2 | import { assert, context, test, assert_raises } from "./assert"; 3 | import { deepEqual, verify_json, test_each, random_list } from "./util"; 4 | 5 | function verify_tuple(tuple, array) { 6 | assert(isTuple(tuple)); 7 | 8 | assert(deepEqual(tuple._values, array)); 9 | assert(deepEqual(toJS(tuple), array)); 10 | 11 | return tuple; 12 | } 13 | 14 | export function test_Tuple() { 15 | context("Tuple", function () { 16 | var empty_tuple = Tuple(); 17 | var five_tuple = Tuple([1, 2, 3, 4, 5]); 18 | 19 | test("isTuple", function () { 20 | assert(!isTuple(List())); 21 | assert(isTuple(Tuple())); 22 | assert(isTuple(Type(500))); 23 | }); 24 | 25 | test("verify", function () { 26 | verify_tuple(empty_tuple, []); 27 | verify_tuple(five_tuple, [1, 2, 3, 4, 5]); 28 | }); 29 | 30 | test("init", function () { 31 | verify_tuple(Tuple([1, 2, 3]), [1, 2, 3]); 32 | 33 | assert_raises(function () { 34 | Tuple(null); 35 | }, "Cannot read property '(UUIDTag 6199065c-b518-4cb3-8b41-ab70a9769ec3)' of null"); 36 | 37 | verify_tuple(Tuple(), []); 38 | }); 39 | 40 | test("isEmpty", function () { 41 | assert(empty_tuple.isEmpty()); 42 | assert(!five_tuple.isEmpty()); 43 | }); 44 | 45 | test("size", function () { 46 | assert(empty_tuple.size() === 0); 47 | assert(five_tuple.size() === 5); 48 | }); 49 | 50 | test("get", function () { 51 | assert_raises(function () { 52 | empty_tuple.get(0); 53 | }, "Index 0 is not valid"); 54 | 55 | assert_raises(function () { 56 | empty_tuple.get(-1); 57 | }, "Index -1 is not valid"); 58 | 59 | assert(five_tuple.get(0) === 1); 60 | assert(five_tuple.get(4) === 5); 61 | 62 | assert_raises(function () { 63 | five_tuple.get(-1); 64 | }, "Index -1 is not valid"); 65 | 66 | assert_raises(function () { 67 | five_tuple.get(-2); 68 | }, "Index -2 is not valid"); 69 | }); 70 | 71 | test("set", function () { 72 | verify_tuple(five_tuple.set(0, 50), [50, 2, 3, 4, 5]); 73 | verify_tuple(five_tuple.set(4, 50), [1, 2, 3, 4, 50]); 74 | 75 | assert_raises(function () { 76 | empty_tuple.set(0, 50); 77 | }, "Index 0 is not valid"); 78 | 79 | assert_raises(function () { 80 | empty_tuple.set(-1, 50); 81 | }, "Index -1 is not valid"); 82 | 83 | assert_raises(function () { 84 | five_tuple.set(-1, 50); 85 | }, "Index -1 is not valid"); 86 | 87 | assert_raises(function () { 88 | five_tuple.set(-2, 50); 89 | }, "Index -2 is not valid"); 90 | }); 91 | 92 | test("modify", function () { 93 | var ran = false; 94 | 95 | assert_raises(function () { 96 | empty_tuple.modify(0, function () { ran = true; }); 97 | }, "Index 0 is not valid"); 98 | 99 | assert_raises(function () { 100 | empty_tuple.modify(-1, function () { ran = true; }); 101 | }, "Index -1 is not valid"); 102 | 103 | assert(ran === false); 104 | 105 | 106 | var ran = false; 107 | 108 | verify_tuple(five_tuple.modify(0, function (x) { 109 | ran = true; 110 | assert(x === 1); 111 | return x + 100; 112 | }), [101, 2, 3, 4, 5]); 113 | 114 | assert(ran === true); 115 | 116 | verify_tuple(five_tuple.modify(1, function (x) { return x + 100 }), [1, 102, 3, 4, 5]); 117 | 118 | assert_raises(function () { 119 | five_tuple.modify(-1, function (x) { return x + 100 }) 120 | }, "Index -1 is not valid"); 121 | 122 | assert_raises(function () { 123 | five_tuple.modify(-2, function (x) { return x + 100 }) 124 | }, "Index -2 is not valid"); 125 | }); 126 | 127 | test("=== when not modified", function () { 128 | assert(Tuple(five_tuple) === five_tuple); 129 | 130 | var tuple1 = Tuple([Tuple([])]); 131 | 132 | assert(five_tuple.set(0, 1) === five_tuple); 133 | assert(five_tuple.set(0, 2) !== five_tuple); 134 | 135 | assert(tuple1.modify(0, function () { 136 | return Tuple([]); 137 | }) !== tuple1); 138 | 139 | assert(five_tuple.modify(0, function () { 140 | return 1; 141 | }) === five_tuple); 142 | 143 | assert(five_tuple.modify(0, function () { 144 | return 2; 145 | }) !== five_tuple); 146 | 147 | assert(five_tuple.modify(1, function () { 148 | return 2; 149 | }) === five_tuple); 150 | 151 | assert(five_tuple.modify(1, function () { 152 | return 3; 153 | }) !== five_tuple); 154 | 155 | assert(five_tuple.modify(4, function () { 156 | return 5; 157 | }) === five_tuple); 158 | 159 | assert(five_tuple.modify(4, function () { 160 | return 6; 161 | }) !== five_tuple); 162 | }); 163 | 164 | test("equal", function () { 165 | assert(equal(empty_tuple, empty_tuple)); 166 | assert(equal(five_tuple, five_tuple)); 167 | 168 | assert(equal(Tuple([1, 2, 3]), Tuple([1, 2, 3]))); 169 | assert(!equal(Tuple([1, 2, 3]), Tuple([1, 2, 3, 4]))); 170 | assert(!equal(Tuple([1, 2, 3]), Tuple([1, 2, 4]))); 171 | assert(!equal(Tuple([1, 2, 3]), Tuple([1, 3, 2]))); 172 | 173 | assert(equal(Tuple([1, 2, 3, 4, 5]), five_tuple)); 174 | assert(equal(five_tuple, Tuple([1, 2, 3, 4, 5]))); 175 | 176 | assert(equal(Tuple([Tuple([1, 2, 3])]), Tuple([Tuple([1, 2, 3])]))); 177 | }); 178 | 179 | test("toJS", function () { 180 | assert(deepEqual(toJS(empty_tuple), [])); 181 | assert(deepEqual(toJS(five_tuple), [1, 2, 3, 4, 5])); 182 | assert(deepEqual(toJS(Tuple([1, 2, Tuple([3])])), [1, 2, [3]])); 183 | }); 184 | 185 | test("toJSON", function () { 186 | verify_json(empty_tuple, []); 187 | verify_json(five_tuple, [1, 2, 3, 4, 5]); 188 | verify_json(Tuple([4, 5, Tuple([1, 2, 3])]), [4, 5, [1, 2, 3]]); 189 | }); 190 | 191 | test("each", function () { 192 | test_each(Tuple(), []); 193 | 194 | var x = Tuple([4]); 195 | test_each(Tuple([1, 2, 3, x]), [1, 2, 3, x]); 196 | 197 | var expected = random_list(200); 198 | test_each(Tuple(expected), expected); 199 | }); 200 | 201 | test("toString", function () { 202 | assert("" + empty_tuple === "(Tuple)"); 203 | assert("" + Tuple([1, 2, 3]) === "(Tuple\n 1\n 2\n 3)"); 204 | assert("" + Tuple([1, Tuple([2]), 3]) === "(Tuple\n 1\n (Tuple\n 2)\n 3)"); 205 | }); 206 | 207 | /* 208 | // TODO code duplication 209 | test("zip", function () { 210 | assert(deepEqual(toArray(zip(Tuple())), toArray(zip([])))); 211 | 212 | assert(deepEqual(toArray(zip(Tuple([1, 2, 3, 4, 5]))), [[1], [2], [3], [4], [5]])); 213 | 214 | var a = random_list(200); 215 | assert(deepEqual(toArray(zip(Tuple(a))), toArray(zip(a)))); 216 | });*/ 217 | }); 218 | } 219 | -------------------------------------------------------------------------------- /src/Test/Type.js: -------------------------------------------------------------------------------- 1 | import { isType, toJS, Type, Tuple, equal } from "../Immutable"; 2 | import { assert, context, test, assert_raises } from "./assert"; 3 | import { deepEqual, verify_json, test_each, random_list } from "./util"; 4 | 5 | function verify(x, type, array) { 6 | assert(isType(x)); 7 | 8 | assert(x.type() === type); 9 | assert(deepEqual(x._values, array)); 10 | assert(deepEqual(toJS(x), { type: type, values: array })); 11 | 12 | return x; 13 | } 14 | 15 | export function test_Type() { 16 | context("Type", function () { 17 | var type = 500; 18 | var empty = Type(type, []); 19 | var five = Type(type, [1, 2, 3, 4, 5]); 20 | 21 | test("isType", function () { 22 | assert(!isType(Tuple())); 23 | assert(isType(empty)); 24 | }); 25 | 26 | test("verify", function () { 27 | verify(empty, type, []); 28 | verify(five, type, [1, 2, 3, 4, 5]); 29 | }); 30 | 31 | test("init", function () { 32 | verify(Type(type), type, []); 33 | verify(Type(type, [1, 2, 3]), type, [1, 2, 3]); 34 | verify(Type(200, [1, 2, 3]), 200, [1, 2, 3]); 35 | 36 | assert_raises(function () { 37 | Type(); 38 | }, "Expected 1 to 2 arguments but got 0"); 39 | }); 40 | 41 | test("isEmpty", function () { 42 | assert(empty.isEmpty()); 43 | assert(!five.isEmpty()); 44 | }); 45 | 46 | test("size", function () { 47 | assert(empty.size() === 0); 48 | assert(five.size() === 5); 49 | }); 50 | 51 | test("get", function () { 52 | assert_raises(function () { 53 | empty.get(0); 54 | }, "Index 0 is not valid"); 55 | 56 | assert_raises(function () { 57 | empty.get(-1); 58 | }, "Index -1 is not valid"); 59 | 60 | assert(five.get(0) === 1); 61 | assert(five.get(4) === 5); 62 | 63 | assert_raises(function () { 64 | five.get(-1); 65 | }, "Index -1 is not valid"); 66 | 67 | assert_raises(function () { 68 | five.get(-2); 69 | }, "Index -2 is not valid"); 70 | }); 71 | 72 | test("set", function () { 73 | verify(five.set(0, 50), type, [50, 2, 3, 4, 5]); 74 | verify(five.set(4, 50), type, [1, 2, 3, 4, 50]); 75 | 76 | assert_raises(function () { 77 | empty.set(0, 50); 78 | }, "Index 0 is not valid"); 79 | 80 | assert_raises(function () { 81 | empty.set(-1, 50); 82 | }, "Index -1 is not valid"); 83 | 84 | assert_raises(function () { 85 | five.set(-1, 50); 86 | }, "Index -1 is not valid"); 87 | 88 | assert_raises(function () { 89 | five.set(-2, 50); 90 | }, "Index -2 is not valid"); 91 | }); 92 | 93 | test("modify", function () { 94 | var ran = false; 95 | 96 | assert_raises(function () { 97 | empty.modify(0, function () { ran = true; }); 98 | }, "Index 0 is not valid"); 99 | 100 | assert_raises(function () { 101 | empty.modify(-1, function () { ran = true; }); 102 | }, "Index -1 is not valid"); 103 | 104 | assert(ran === false); 105 | 106 | 107 | var ran = false; 108 | 109 | verify(five.modify(0, function (x) { 110 | ran = true; 111 | assert(x === 1); 112 | return x + 100; 113 | }), type, [101, 2, 3, 4, 5]); 114 | 115 | assert(ran === true); 116 | 117 | verify(five.modify(1, function (x) { return x + 100 }), type, [1, 102, 3, 4, 5]); 118 | 119 | assert_raises(function () { 120 | five.modify(-1, function (x) { return x + 100 }) 121 | }, "Index -1 is not valid"); 122 | 123 | assert_raises(function () { 124 | five.modify(-2, function (x) { return x + 100 }) 125 | }, "Index -2 is not valid"); 126 | }); 127 | 128 | test("=== when not modified", function () { 129 | assert(Type(type, five) === five); 130 | assert(Type(200, five) !== five); 131 | 132 | assert(five.set(0, 1) === five); 133 | assert(five.set(0, 2) !== five); 134 | 135 | var type1 = Type(type, [Type(type)]); 136 | 137 | assert(type1.modify(0, function () { 138 | return Type(type); 139 | }) !== type1); 140 | 141 | assert(five.modify(0, function () { 142 | return 1; 143 | }) === five); 144 | 145 | assert(five.modify(0, function () { 146 | return 2; 147 | }) !== five); 148 | 149 | assert(five.modify(1, function () { 150 | return 2; 151 | }) === five); 152 | 153 | assert(five.modify(1, function () { 154 | return 3; 155 | }) !== five); 156 | 157 | assert(five.modify(4, function () { 158 | return 5; 159 | }) === five); 160 | 161 | assert(five.modify(4, function () { 162 | return 6; 163 | }) !== five); 164 | }); 165 | 166 | test("equal", function () { 167 | assert(equal(empty, empty)); 168 | assert(equal(five, five)); 169 | 170 | assert(equal(Type(type, [1, 2, 3]), Type(type, [1, 2, 3]))); 171 | assert(!equal(Type(type, [1, 2, 3]), Type(200, [1, 2, 3]))); 172 | assert(!equal(Type(type, [1, 2, 3]), Type(type, [1, 2, 3, 4]))); 173 | assert(!equal(Type(type, [1, 2, 3]), Type(type, [1, 2, 4]))); 174 | assert(!equal(Type(type, [1, 2, 3]), Type(type, [1, 3, 2]))); 175 | 176 | assert(equal(Type(type, [1, 2, 3, 4, 5]), five)); 177 | assert(equal(five, Type(type, [1, 2, 3, 4, 5]))); 178 | 179 | assert(equal(Type(type, [Type(type, [1, 2, 3])]), Type(type, [Type(type, [1, 2, 3])]))); 180 | }); 181 | 182 | test("toJS", function () { 183 | assert(deepEqual(toJS(empty), { type: type, values: [] })); 184 | assert(deepEqual(toJS(five), { type: type, values: [1, 2, 3, 4, 5] })); 185 | assert(deepEqual(toJS(Type(type, [1, 2, Type(type, [3])])), { type: type, values: [1, 2, { type: type, values: [3] }] })); 186 | }); 187 | 188 | test("toJSON", function () { 189 | verify_json(empty, { type: type, values: [] }); 190 | verify_json(five, { type: type, values: [1, 2, 3, 4, 5] }); 191 | verify_json(Type(type, [4, 5, Type(type, [1, 2, 3])]), { type: type, values: [4, 5, { type: type, values: [1, 2, 3] }] }); 192 | }); 193 | 194 | test("each", function () { 195 | test_each(Type(type), []); 196 | 197 | var x = Type(type, [4]); 198 | test_each(Type(type, [1, 2, 3, x]), [1, 2, 3, x]); 199 | 200 | var expected = random_list(200); 201 | test_each(Type(type, expected), expected); 202 | }); 203 | 204 | test("toString", function () { 205 | assert("" + empty === "(Type 500)"); 206 | assert("" + Type(type, [1, 2, 3]) === "(Type 500\n 1\n 2\n 3)"); 207 | assert("" + Type(200, [1, 2, 3]) === "(Type 200\n 1\n 2\n 3)"); 208 | assert("" + Type(type, [1, Type(200, [2]), 3]) === "(Type 500\n 1\n (Type 200\n 2)\n 3)"); 209 | }); 210 | 211 | /* 212 | // TODO code duplication 213 | test("zip", function () { 214 | assert(deepEqual(toArray(zip(Tuple())), toArray(zip([])))); 215 | 216 | assert(deepEqual(toArray(zip(Tuple([1, 2, 3, 4, 5]))), [[1], [2], [3], [4], [5]])); 217 | 218 | var a = random_list(200); 219 | assert(deepEqual(toArray(zip(Tuple(a))), toArray(zip(a)))); 220 | });*/ 221 | }); 222 | } 223 | -------------------------------------------------------------------------------- /src/Test/assert.js: -------------------------------------------------------------------------------- 1 | var SUCCEEDED = 0; 2 | var FAILED = 0; 3 | var CONTEXT = null; 4 | 5 | export function assert(x) { 6 | if (arguments.length !== 1) { 7 | throw new Error("Invalid argument length"); 8 | } 9 | if (x) { 10 | ++SUCCEEDED; 11 | } else { 12 | throw new Error("Failed: " + x); 13 | } 14 | } 15 | 16 | export function assert_raises(f, message) { 17 | try { 18 | f(); 19 | throw new Error("Expected an error, but it did not happen"); 20 | } catch (e) { 21 | if (e.message === message) { 22 | ++SUCCEEDED; 23 | } else { 24 | throw new Error("Expected \"" + message + "\" but got \"" + e.message + "\""); 25 | } 26 | } 27 | } 28 | 29 | export function context(s, f) { 30 | var old_context = CONTEXT; 31 | CONTEXT = s; 32 | try { 33 | f(); 34 | } finally { 35 | CONTEXT = old_context; 36 | } 37 | } 38 | 39 | export function test(s, f) { 40 | try { 41 | f(); 42 | } catch (e) { 43 | ++FAILED; 44 | console.log(""); 45 | console.log("*** " + (CONTEXT ? CONTEXT + "." : "") + s + " FAILED"); 46 | if (e.stack) { 47 | console.log(e.stack); 48 | } else { 49 | console.log(e); 50 | } 51 | console.log(""); 52 | } 53 | } 54 | 55 | export function test_run(f) { 56 | var time_start = Date.now(); 57 | 58 | f(); 59 | 60 | var time_end = Date.now(); 61 | 62 | console.log("SUCCEEDED: " + SUCCEEDED + ", FAILED: " + FAILED + ", TOOK: " + (time_end - time_start) + "ms"); 63 | } 64 | -------------------------------------------------------------------------------- /src/Test/iter.js: -------------------------------------------------------------------------------- 1 | import { each, Tag, Tuple, Record, isTuple, toJS, map, toArray, 2 | keep, any, all, partition, toIterator, Iterable, findIndex, 3 | indexOf, find, reverse, foldl, foldr, join, zip, take, skip, 4 | range, repeat, List } from "../Immutable"; 5 | import { assert, context, test, assert_raises } from "./assert"; 6 | import { deepEqual } from "./util"; 7 | 8 | export function test_iter() { 9 | test("each", function () { 10 | var ran = false; 11 | 12 | assert_raises(function () { 13 | each(Tag(), function () { 14 | ran = true; 15 | }); 16 | }, "Cannot iter: (Tag 48de6fff-9d11-472d-a76f-ed77a59a5cbc 12)"); 17 | 18 | assert(ran === false); 19 | 20 | 21 | var ran = false; 22 | each([], function () { 23 | ran = true; 24 | }); 25 | assert(ran === false); 26 | 27 | var a = []; 28 | 29 | each([1, 2, 3], function (x) { 30 | a.push(x); 31 | }); 32 | 33 | assert(deepEqual(a, [1, 2, 3])); 34 | 35 | 36 | var a = []; 37 | 38 | each("foo", function (x) { 39 | a.push(x); 40 | }); 41 | 42 | assert(deepEqual(a, ["f", "o", "o"])); 43 | 44 | 45 | var a = []; 46 | 47 | each(Tuple([1, 2, 3]), function (x) { 48 | a.push(x); 49 | }); 50 | 51 | assert(deepEqual(a, [1, 2, 3])); 52 | 53 | 54 | var a = []; 55 | 56 | each(Record([["foo", 1], ["bar", 2]]), function (x) { 57 | assert(isTuple(x)); 58 | a.push(toJS(x)); 59 | }); 60 | 61 | assert(deepEqual(a, [["bar", 2], ["foo", 1]])); 62 | }); 63 | 64 | test("map", function () { 65 | var x = map([], function (x) { return x + 10 }); 66 | assert(!Array.isArray(x)); 67 | assert(deepEqual(toArray(x), [])); 68 | 69 | var x = map([1, 2, 3], function (x) { return x + 10 }); 70 | assert(!Array.isArray(x)); 71 | assert(deepEqual(toArray(x), [11, 12, 13])); 72 | 73 | var x = map(Tuple([1, 2, 3]), function (x) { return x + 10 }); 74 | assert(!Array.isArray(x)); 75 | assert(deepEqual(toArray(x), [11, 12, 13])); 76 | 77 | var x = map(Record([["foo", 1], ["bar", 2]]), function (x) { return [x.get(0), x.get(1) + 10] }); 78 | assert(!Array.isArray(x)); 79 | assert(deepEqual(toArray(x), [["bar", 12], ["foo", 11]])); 80 | }); 81 | 82 | test("keep", function () { 83 | var x = keep([], function (x) { return x > 3 }); 84 | assert(!Array.isArray(x)); 85 | assert(deepEqual(toArray(x), [])); 86 | 87 | var x = keep([1, 2, 3, 4, 5], function (x) { return x > 3 }); 88 | assert(!Array.isArray(x)); 89 | assert(deepEqual(toArray(x), [4, 5])); 90 | }); 91 | 92 | test("any", function () { 93 | assert(any([], function (x) { return x > 3 }) === false); 94 | assert(any([1, 2, 3], function (x) { return x > 3 }) === false); 95 | assert(any([1, 2, 3, 4], function (x) { return x > 3 }) === true); 96 | }); 97 | 98 | test("all", function () { 99 | assert(all([], function (x) { return x < 3 }) === true); 100 | assert(all([1, 2], function (x) { return x < 3 }) === true); 101 | assert(all([1, 2, 3], function (x) { return x < 3 }) === false); 102 | }); 103 | 104 | test("partition", function () { 105 | var x = partition([], function (x) { 106 | return x < 5; 107 | }); 108 | 109 | assert(isTuple(x)); 110 | var yes = x.get(0); 111 | var no = x.get(1); 112 | assert(!Array.isArray(yes)); 113 | assert(!Array.isArray(no)); 114 | assert(deepEqual(toArray(yes), [])); 115 | assert(deepEqual(toArray(no), [])); 116 | 117 | 118 | var x = partition([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], function (x) { 119 | return x < 5; 120 | }); 121 | 122 | assert(isTuple(x)); 123 | var yes = x.get(0); 124 | var no = x.get(1); 125 | assert(!Array.isArray(yes)); 126 | assert(!Array.isArray(no)); 127 | assert(deepEqual(toArray(yes), [1, 2, 3, 4, 0])); 128 | assert(deepEqual(toArray(no), [5, 6, 7, 8, 9])); 129 | 130 | 131 | var x = partition([1, 2, 3, 4], function (x) { 132 | return x < 5; 133 | }); 134 | 135 | assert(isTuple(x)); 136 | var yes = x.get(0); 137 | var no = x.get(1); 138 | assert(!Array.isArray(yes)); 139 | assert(!Array.isArray(no)); 140 | assert(deepEqual(toArray(yes), [1, 2, 3, 4])); 141 | assert(deepEqual(toArray(no), [])); 142 | 143 | 144 | var x = partition([5, 6, 7, 8, 9], function (x) { 145 | return x < 5; 146 | }); 147 | 148 | assert(isTuple(x)); 149 | var yes = x.get(0); 150 | var no = x.get(1); 151 | assert(!Array.isArray(yes)); 152 | assert(!Array.isArray(no)); 153 | assert(deepEqual(toArray(yes), [])); 154 | assert(deepEqual(toArray(no), [5, 6, 7, 8, 9])); 155 | }); 156 | 157 | test("toIterator", function () { 158 | var iterator = toIterator([1, 2, 3]); 159 | assert(typeof iterator.next === "function"); 160 | assert(deepEqual(iterator.next(), { value: 1 })); 161 | assert(deepEqual(iterator.next(), { value: 2 })); 162 | assert(deepEqual(iterator.next(), { value: 3 })); 163 | assert(deepEqual(iterator.next(), { done: true })); 164 | 165 | 166 | var iterator = toIterator(List([1, 2, 3])); 167 | assert(typeof iterator.next === "function"); 168 | assert(deepEqual(iterator.next(), { value: 1 })); 169 | assert(deepEqual(iterator.next(), { value: 2 })); 170 | assert(deepEqual(iterator.next(), { value: 3 })); 171 | assert(deepEqual(iterator.next(), { done: true })); 172 | }); 173 | 174 | test("Iterable", function () { 175 | var iterable = Iterable(function () { 176 | assert(this === void 0); 177 | 178 | var i = 0; 179 | return { 180 | next: function () { 181 | if (i < 4) { 182 | return { value: i++ }; 183 | } else { 184 | return { done: true }; 185 | } 186 | } 187 | }; 188 | }); 189 | 190 | assert(deepEqual(toArray(iterable), [0, 1, 2, 3])); 191 | 192 | var iterator = toIterator(iterable); 193 | assert(typeof iterator.next === "function"); 194 | assert(deepEqual(iterator.next(), { value: 0 })); 195 | assert(deepEqual(iterator.next(), { value: 1 })); 196 | assert(deepEqual(iterator.next(), { value: 2 })); 197 | assert(deepEqual(iterator.next(), { value: 3 })); 198 | assert(deepEqual(iterator.next(), { done: true })); 199 | }); 200 | 201 | test("findIndex", function () { 202 | var x = findIndex([1, 2, 3, 4, 5], function (x) { return x > 3 }); 203 | assert(x === 3); 204 | 205 | assert_raises(function () { 206 | findIndex([1, 2, 3, 4, 5], function (x) { return x > 5 }); 207 | }, "Did not find anything"); 208 | 209 | var x = findIndex([1, 2, 3, 4, 5], function (x) { return x > 5 }, 500); 210 | assert(x === 500); 211 | }); 212 | 213 | test("indexOf", function () { 214 | var x = indexOf([1, 2, 3, 4, 5], 4); 215 | assert(x === 3); 216 | 217 | assert_raises(function () { 218 | indexOf([1, 2, 3, 4, 5], 6); 219 | }, "Did not find anything"); 220 | 221 | var x = indexOf([1, 2, 3, 4, 5], 6, 500); 222 | assert(x === 500); 223 | 224 | var x = [Tuple([1, 2, 3]), Record({ foo: 1, bar: 2 })]; 225 | 226 | assert_raises(function () { 227 | indexOf(x, Tuple([1, 2, 4])) 228 | }, "Did not find anything"); 229 | 230 | assert(indexOf(x, Tuple([1, 2, 4]), -1) === -1); 231 | assert(indexOf(x, Tuple([1, 2, 3])) === 0); 232 | }); 233 | 234 | test("find", function () { 235 | var x = find([1, 2, 3, 4, 5], function (x) { return x > 3 }); 236 | assert(x === 4); 237 | 238 | assert_raises(function () { 239 | find([1, 2, 3, 4, 5], function (x) { return x > 5 }); 240 | }, "Did not find anything"); 241 | 242 | var x = find([1, 2, 3, 4, 5], function (x) { return x > 5 }, 500); 243 | assert(x === 500); 244 | }); 245 | 246 | test("reverse", function () { 247 | var x = reverse([]); 248 | assert(!Array.isArray(x)); 249 | assert(deepEqual(toArray(x), [])); 250 | 251 | var x = reverse([1, 2, 3]); 252 | assert(!Array.isArray(x)); 253 | assert(deepEqual(toArray(x), [3, 2, 1])); 254 | 255 | var x = reverse(Tuple([1, 2, 3])); 256 | assert(!Array.isArray(x)); 257 | assert(deepEqual(toArray(x), [3, 2, 1])); 258 | 259 | var x = reverse(map(Record([["bar", 2], ["foo", 1]]), function (x) { 260 | assert(isTuple(x)); 261 | return toArray(x); 262 | })); 263 | assert(!Array.isArray(x)); 264 | assert(deepEqual(toArray(x), [["foo", 1], ["bar", 2]])); 265 | }); 266 | 267 | test("foldl", function () { 268 | var init = "0"; 269 | var ran = false; 270 | var out = foldl(["1", "2", "3"], init, function (x, y) { 271 | assert(x === init); 272 | assert(y === "1" || y === "2" || y === "3"); 273 | ran = true; 274 | init = "(" + x + " " + y + ")"; 275 | return init; 276 | }); 277 | assert(out === init); 278 | assert(out === "(((0 1) 2) 3)"); 279 | assert(ran === true); 280 | 281 | 282 | var init = 0; 283 | var ran = false; 284 | var out = foldl([], init, function () { 285 | ran = true; 286 | }); 287 | 288 | assert(out === init); 289 | assert(ran === false); 290 | }); 291 | 292 | test("foldr", function () { 293 | var init = "0"; 294 | var ran = false; 295 | var out = foldr(["1", "2", "3"], init, function (x, y) { 296 | assert(y === init); 297 | assert(x === "1" || x === "2" || x === "3"); 298 | ran = true; 299 | init = "(" + x + " " + y + ")"; 300 | return init; 301 | }); 302 | assert(out === init); 303 | assert(out === "(1 (2 (3 0)))"); 304 | assert(ran === true); 305 | 306 | 307 | var init = 0; 308 | var ran = false; 309 | var out = foldr([], init, function () { 310 | ran = true; 311 | }); 312 | 313 | assert(out === init); 314 | assert(ran === false); 315 | }); 316 | 317 | test("join", function () { 318 | assert(join([]) === ""); 319 | assert(join([], " ") === ""); 320 | assert(join([1, 2, 3]) === "123"); 321 | assert(join("123") === "123"); 322 | assert(join(Tuple([1, 2, 3])) === "123"); 323 | assert(join([1, 2, 3], " ") === "1 2 3"); 324 | assert(join("123", " ") === "1 2 3"); 325 | assert(join("123", " --- ") === "1 --- 2 --- 3"); 326 | }); 327 | 328 | test("zip", function () { 329 | function mapper(x) { 330 | assert(!Array.isArray(x)); 331 | 332 | x = map(x, function (x) { 333 | assert(isTuple(x)); 334 | return toArray(x); 335 | }); 336 | 337 | assert(!Array.isArray(x)); 338 | 339 | return x; 340 | } 341 | 342 | var x = mapper(zip([[1, 2, 3], [4, 5, 6], [7, 8, 9]])); 343 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 344 | 345 | var x = mapper(zip(List([List([1, 2, 3]), List([4, 5, 6]), List([7, 8, 9])]))); 346 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 347 | 348 | var x = mapper(zip(Tuple([Tuple([1, 2, 3]), Tuple([4, 5, 6]), Tuple([7, 8, 9])]))); 349 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 350 | 351 | 352 | var x = mapper(zip([[1, 2, 3, 0], [4, 5, 6], [7, 8, 9]])); 353 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 354 | 355 | var x = mapper(zip([[1, 2, 3], [4, 5, 6, 0], [7, 8, 9]])); 356 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 357 | 358 | var x = mapper(zip([[1, 2, 3], [4, 5, 6], [7, 8, 9, 0]])); 359 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 360 | 361 | var x = mapper(zip([[1, 2, 3, 0], [4, 5, 6], [7, 8, 9, 0]])); 362 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 363 | 364 | var x = mapper(zip([[1, 2, 3, 0], [4, 5, 6, 0], [7, 8, 9]])); 365 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9]])); 366 | 367 | 368 | var x = mapper(zip([[1, 2, 3, 0], [4, 5, 6], [7, 8, 9]], 50)); 369 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9], [0, 50, 50]])); 370 | 371 | var x = mapper(zip([[1, 2, 3], [4, 5, 6, 0], [7, 8, 9]], 50)); 372 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9], [50, 0, 50]])); 373 | 374 | var x = mapper(zip([[1, 2, 3], [4, 5, 6], [7, 8, 9, 0]], 50)); 375 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9], [50, 50, 0]])); 376 | 377 | var x = mapper(zip([[1, 2, 3, 0], [4, 5, 6], [7, 8, 9, 0]], 50)); 378 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9], [0, 50, 0]])); 379 | 380 | var x = mapper(zip([[1, 2, 3, 0], [4, 5, 6, 0], [7, 8, 9]], 50)); 381 | assert(deepEqual(toArray(x), [[1, 4, 7], [2, 5, 8], [3, 6, 9], [0, 0, 50]])); 382 | }); 383 | 384 | test("toArray", function () { 385 | var x = [1, 2, 3, 4, 5]; 386 | assert(toArray(x) === x); 387 | 388 | assert(deepEqual(toArray("foo"), ["f", "o", "o"])); 389 | assert(deepEqual(toArray(Tuple([1, 2, 3])), [1, 2, 3])); 390 | 391 | var x = map(Record([["foo", 1], ["bar", 2]]), function (x) { 392 | assert(isTuple(x)); 393 | return toArray(x); 394 | }); 395 | assert(deepEqual(toArray(x), [["bar", 2], ["foo", 1]])); 396 | }); 397 | 398 | test("take", function () { 399 | assert(deepEqual(toArray(take([1, 2, 3, 4, 5], 0)), [])); 400 | assert(deepEqual(toArray(take([1, 2, 3, 4, 5], 2)), [1, 2])); 401 | assert(deepEqual(toArray(take([1, 2, 3, 4, 5], 200)), [1, 2, 3, 4, 5])); 402 | 403 | assert_raises(function () { 404 | take([1, 2, 3, 4, 5], -5); 405 | }, "Count cannot be negative: -5"); 406 | 407 | assert_raises(function () { 408 | take([1, 2, 3, 4, 5], 5.1); 409 | }, "Count must be an integer: 5.1"); 410 | }); 411 | 412 | test("skip", function () { 413 | assert(deepEqual(toArray(skip([1, 2, 3, 4, 5], 0)), [1, 2, 3, 4, 5])); 414 | assert(deepEqual(toArray(skip([1, 2, 3, 4, 5], 2)), [3, 4, 5])); 415 | assert(deepEqual(toArray(skip([1, 2, 3, 4, 5], 200)), [])); 416 | 417 | assert_raises(function () { 418 | skip([1, 2, 3, 4, 5], -5); 419 | }, "Count cannot be negative: -5"); 420 | 421 | assert_raises(function () { 422 | skip([1, 2, 3, 4, 5], 5.1); 423 | }, "Count must be an integer: 5.1"); 424 | }); 425 | 426 | test("range", function () { 427 | assert(deepEqual(toArray(take(range(), 12)), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); 428 | assert(deepEqual(toArray(take(range(6), 12)), [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])); 429 | 430 | assert(deepEqual(toArray(range(0, 10)), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])); 431 | assert(deepEqual(toArray(range(5, 5)), [])); 432 | assert(deepEqual(toArray(range(4, 5)), [4])); 433 | assert(deepEqual(toArray(range(5, 4)), [5])); 434 | assert(deepEqual(toArray(range(10, 0)), [10, 9, 8, 7, 6, 5, 4, 3, 2, 1])); 435 | 436 | assert(deepEqual(toArray(range(0, 10, 2)), [0, 2, 4, 6, 8])); 437 | assert(deepEqual(toArray(range(10, 0, 2)), [10, 8, 6, 4, 2])); 438 | assert(deepEqual(toArray(range(4.2, 6.9, 0.5)), [4.2, 4.7, 5.2, 5.7, 6.2, 6.7])); 439 | assert(deepEqual(toArray(range(-10, -2)), [-10, -9, -8, -7, -6, -5, -4, -3])); 440 | 441 | assert(deepEqual(toArray(range(-0, 0)), [])); 442 | assert(deepEqual(toArray(range(0, -0)), [])); 443 | assert(deepEqual(toArray(range(0, 1)), [0])); 444 | assert(deepEqual(toArray(range(-0, 1)), [-0])); 445 | 446 | assert(deepEqual(toArray(range(0, 0.5, 0.1)), [0, 0.1, 0.2, 0.30000000000000004, 0.4])); 447 | assert(deepEqual(toArray(take(range(5, 4, 0), 5)), [5, 5, 5, 5, 5])); 448 | 449 | assert_raises(function () { 450 | range(5, 4, -1); 451 | }, "Step cannot be negative: -1"); 452 | }); 453 | 454 | test("repeat", function () { 455 | assert(deepEqual(toArray(repeat(1, 5)), [1, 1, 1, 1, 1])); 456 | assert(deepEqual(toArray(repeat(1, 0)), [])); 457 | assert(deepEqual(toArray(repeat(1, 2)), [1, 1])); 458 | assert(deepEqual(toArray(take(repeat(1), 12)), [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])); 459 | 460 | assert_raises(function () { 461 | repeat(1, 0.5); 462 | }, "Count must be an integer: 0.5"); 463 | 464 | assert_raises(function () { 465 | repeat(1, -5); 466 | }, "Count cannot be negative: -5"); 467 | }); 468 | } 469 | -------------------------------------------------------------------------------- /src/Test/misc.js: -------------------------------------------------------------------------------- 1 | import { isImmutable, Ref, Dict, Set, List, Queue, Stack, 2 | SortedDict, simpleSort, SortedSet, Tag, Tuple, 3 | UUIDTag, Record, isIterable, toJS, fromJS, toJSON, 4 | fromJSON, deref, equal, Type } from "../Immutable"; 5 | import { assert, context, test, assert_raises } from "./assert"; 6 | import { deepEqual } from "./util"; 7 | import { verify_dict } from "./Dict"; 8 | import { verify_list } from "./List"; 9 | 10 | export function test_misc() { 11 | test("isImmutable", function () { 12 | assert(!isImmutable({})); 13 | assert(!isImmutable([])); 14 | assert(!isImmutable(Ref(5))); 15 | 16 | assert(isImmutable(null)); 17 | assert(isImmutable(5)); 18 | assert(isImmutable("foo")); 19 | assert(isImmutable(Object.freeze({}))); 20 | assert(isImmutable(Dict())); 21 | assert(isImmutable(Set())); 22 | assert(isImmutable(List())); 23 | assert(isImmutable(Queue())); 24 | assert(isImmutable(Stack())); 25 | assert(isImmutable(SortedDict(simpleSort))); 26 | assert(isImmutable(SortedSet(simpleSort))); 27 | assert(isImmutable(Tag())); 28 | assert(isImmutable(Tuple())); 29 | assert(isImmutable(Type(500))); 30 | assert(isImmutable(UUIDTag("051eca86-038c-43c8-85cf-01e20f394501"))); 31 | 32 | var Foo = Record({}); 33 | assert(isImmutable(Foo)); 34 | }); 35 | 36 | test("isIterable", function () { 37 | assert(!isIterable({})); 38 | assert(!isIterable(Ref(5))); 39 | assert(!isIterable(5)); 40 | assert(!isIterable(null)); 41 | assert(!isIterable(Object.freeze({}))); 42 | assert(!isIterable(Tag())); 43 | assert(!isIterable(UUIDTag("051eca86-038c-43c8-85cf-01e20f394501"))); 44 | 45 | assert(isIterable([])); 46 | assert(isIterable("foo")); 47 | assert(isIterable(Dict())); 48 | assert(isIterable(Set())); 49 | assert(isIterable(List())); 50 | assert(isIterable(Queue())); 51 | assert(isIterable(Stack())); 52 | assert(isIterable(SortedDict(simpleSort))); 53 | assert(isIterable(SortedSet(simpleSort))); 54 | assert(isIterable(Tuple())); 55 | assert(isIterable(Type(500))); 56 | 57 | var Foo = Record({}); 58 | assert(isIterable(Foo)); 59 | }); 60 | 61 | test("toJS", function () { 62 | var x = { foo: 1 }; 63 | assert(toJS(x) !== x); 64 | assert(deepEqual(toJS(x), { foo: 1 })); 65 | 66 | var x = Object.create(null); 67 | assert(toJS(x) === x); 68 | 69 | var x = [5]; 70 | assert(toJS(x) !== x); 71 | assert(deepEqual(toJS(x), [5])); 72 | 73 | var x = new Date(); 74 | assert(toJS(x) === x); 75 | 76 | var x = /foo/; 77 | assert(toJS(x) === x); 78 | 79 | assert(toJS("foo") === "foo"); 80 | assert(toJS(5) === 5); 81 | 82 | var x = Ref(5); 83 | assert(toJS(x) === x); 84 | 85 | function Foo() { 86 | this.bar = {}; 87 | } 88 | var x = new Foo(); 89 | assert(toJS(x) === x); 90 | assert(deepEqual(toJS(x), new Foo())); 91 | 92 | 93 | var x = { 94 | foo: [Tuple([Set([1]), 2, 3]), Record({ foo: List([1]), bar: 2 })] 95 | }; 96 | assert(toJS(x) !== x); 97 | assert(deepEqual(toJS(x), { foo: [[[1], 2, 3], { foo: [1], bar: 2 }] })); 98 | }); 99 | 100 | test("fromJS", function () { 101 | verify_dict(fromJS({ foo: 1 }), { foo: 1 }); 102 | verify_list(fromJS([1, 2, 3]), [1, 2, 3]); 103 | 104 | verify_dict(fromJS({ foo: { bar: 1 } }), { foo: { bar: 1 } }); 105 | verify_list(fromJS([1, [2], 3]), [1, [2], 3]); 106 | 107 | verify_dict(fromJS({ foo: { bar: 1 } }).get("foo"), { bar: 1 }); 108 | verify_list(fromJS([1, [2], 3]).get(1), [2]); 109 | 110 | var x = Object.create(null); 111 | assert(fromJS(x) === x); 112 | 113 | var x = new Date(); 114 | assert(fromJS(x) === x); 115 | 116 | var x = /foo/; 117 | assert(fromJS(x) === x); 118 | 119 | assert(fromJS("foo") === "foo"); 120 | assert(fromJS(5) === 5); 121 | 122 | var x = Ref(5); 123 | assert(fromJS(x) === x); 124 | 125 | function Foo() { 126 | this.bar = {}; 127 | } 128 | var x = new Foo(); 129 | assert(fromJS(x) === x); 130 | assert(deepEqual(fromJS(x), new Foo())); 131 | }); 132 | 133 | test("toJSON", function () { 134 | var x = { foo: 1 }; 135 | assert(toJSON(x) !== x); 136 | assert(deepEqual(toJSON(x), { foo: 1 })); 137 | 138 | assert_raises(function () { 139 | toJSON(Object.create(null)); 140 | }, "Cannot convert object to primitive value"); 141 | 142 | var x = [5]; 143 | assert(toJSON(x) !== x); 144 | assert(deepEqual(toJSON(x), [5])); 145 | 146 | assert(toJSON("foo") === "foo"); 147 | assert(toJSON(5) === 5); 148 | assert(toJSON(5.5) === 5.5); 149 | assert(toJSON(true) === true); 150 | assert(toJSON(null) === null); 151 | 152 | var x = new Date(2000, 0, 1); 153 | assert(toJSON(x) !== x); 154 | assert(deepEqual(toJSON(x), "2000-01-01T10:00:00.000Z")); 155 | 156 | assert_raises(function () { 157 | toJSON(/foo/); 158 | }, "Cannot convert to JSON: /foo/"); 159 | 160 | assert_raises(function () { 161 | toJSON(NaN); 162 | }, "Cannot convert to JSON: NaN"); 163 | 164 | assert_raises(function () { 165 | toJSON(Infinity); 166 | }, "Cannot convert to JSON: Infinity"); 167 | 168 | assert_raises(function () { 169 | toJSON(-Infinity); 170 | }, "Cannot convert to JSON: -Infinity"); 171 | 172 | assert_raises(function () { 173 | toJSON(undefined); 174 | }, "Cannot convert to JSON: undefined"); 175 | 176 | assert_raises(function () { 177 | function Foo() { 178 | this.foo = 1; 179 | } 180 | var x = new Foo(); 181 | toJSON(x); 182 | }, "Cannot convert to JSON: [object Object]"); 183 | 184 | assert_raises(function () { 185 | toJSON(Ref(5)); 186 | }, "Cannot convert to JSON: (Ref 17)"); 187 | 188 | 189 | var x = {}; 190 | x.toJSON = function () { 191 | return "foo"; 192 | }; 193 | 194 | assert(toJSON(x) !== x); 195 | assert(deepEqual(toJSON(x), "foo")); 196 | 197 | 198 | var x = {}; 199 | x.toJSON = function () { 200 | return function (){}; 201 | }; 202 | 203 | assert_raises(function () { 204 | toJSON(x); 205 | }, "Cannot convert to JSON: function (){}"); 206 | 207 | 208 | var x = {}; 209 | x.toJSON = 5; 210 | assert(toJSON(x) !== x); 211 | assert(deepEqual(toJSON(x), { toJSON: 5 })); 212 | 213 | 214 | var x = { 215 | test: [Tuple([1, 2, 3]), Record({ foo: 1, bar: 2 })] 216 | }; 217 | assert(toJSON(x) !== x); 218 | assert(deepEqual(toJSON(x), { 219 | test: [toJSON(Tuple([1, 2, 3])), toJSON(Record({ foo: 1, bar: 2 }))] 220 | })); 221 | 222 | assert(JSON.stringify(Record({ foo: 1})) === '{"(UUIDTag 89d8297c-d95e-4ce9-bc9b-6b6f73fa6a37)":"Record","keys":["foo"],"values":[1]}'); 223 | assert(deepEqual(JSON.stringify(Record({ foo: 1})), 224 | JSON.stringify(toJSON(Record({ foo: 1 }))))); 225 | }); 226 | 227 | test("fromJSON", function () { 228 | var x = { foo: 1 }; 229 | assert(fromJSON(x) !== x); 230 | assert(deepEqual(fromJSON(x), { foo: 1 })); 231 | 232 | assert_raises(function () { 233 | fromJSON(Object.create(null)); 234 | }, "Cannot convert object to primitive value"); 235 | 236 | var x = [5]; 237 | assert(fromJSON(x) !== x); 238 | assert(deepEqual(fromJSON(x), [5])); 239 | 240 | assert(fromJSON("foo") === "foo"); 241 | assert(fromJSON(5) === 5); 242 | assert(fromJSON(5.5) === 5.5); 243 | assert(fromJSON(true) === true); 244 | assert(fromJSON(null) === null); 245 | 246 | assert_raises(function () { 247 | fromJSON(new Date(2000, 0, 1)); 248 | }, "Cannot convert from JSON: Sat Jan 01 2000 00:00:00 GMT-1000 (HST)"); 249 | 250 | assert_raises(function () { 251 | fromJSON(/foo/); 252 | }, "Cannot convert from JSON: /foo/"); 253 | 254 | assert_raises(function () { 255 | fromJSON(NaN); 256 | }, "Cannot convert from JSON: NaN"); 257 | 258 | assert_raises(function () { 259 | fromJSON(Infinity); 260 | }, "Cannot convert from JSON: Infinity"); 261 | 262 | assert_raises(function () { 263 | fromJSON(-Infinity); 264 | }, "Cannot convert from JSON: -Infinity"); 265 | 266 | assert_raises(function () { 267 | fromJSON(undefined); 268 | }, "Cannot convert from JSON: undefined"); 269 | 270 | assert_raises(function () { 271 | function Foo() { 272 | this.foo = 1; 273 | } 274 | var x = new Foo(); 275 | fromJSON(x); 276 | }, "Cannot convert from JSON: [object Object]"); 277 | 278 | assert_raises(function () { 279 | fromJSON(Ref(5)); 280 | }, "Cannot convert from JSON: (Ref 18)"); 281 | 282 | 283 | var x = toJSON({ 284 | test: [Tuple([1, 2, 3]), Record({ foo: 1, bar: 2 })] 285 | }); 286 | assert(fromJSON(x) !== x); 287 | assert(deepEqual(fromJSON(x), { 288 | test: [Tuple([1, 2, 3]), Record({ foo: 1, bar: 2 })] 289 | })); 290 | assert(equal(fromJSON(x.test[0]), Tuple([1, 2, 3]))); 291 | assert(equal(fromJSON(x.test[1]), Record({ foo: 1, bar: 2 }))); 292 | }); 293 | 294 | test("deref", function () { 295 | assert(deref(5) === 5); 296 | 297 | var x = Dict(); 298 | assert(deref(x) === x); 299 | 300 | assert(deref(Ref(5)) === 5); 301 | assert(deref(Ref(x)) === x); 302 | }); 303 | 304 | test("equal", function () { 305 | assert(equal(0, 0)); 306 | assert(equal(-0, -0)); 307 | assert(equal(0, -0)); 308 | assert(equal(-0, 0)); 309 | assert(equal(1, 1)); 310 | assert(equal(null, null)); 311 | assert(equal(void 0, void 0)); 312 | assert(equal(NaN, NaN)); 313 | assert(equal(true, true)); 314 | assert(equal(false, false)); 315 | assert(equal("foo", "foo")); 316 | 317 | var x = {}; 318 | assert(equal(x, x)); 319 | 320 | assert(!equal(1, 2)); 321 | assert(!equal(null, void 0)); 322 | assert(!equal(void 0, null)); 323 | assert(!equal(NaN, 0)); 324 | assert(!equal(NaN, 1)); 325 | assert(!equal(NaN, "foo")); 326 | assert(!equal(true, false)); 327 | assert(!equal(false, true)); 328 | assert(!equal("foo", "foo2")); 329 | assert(!equal({}, {})); 330 | }); 331 | } 332 | -------------------------------------------------------------------------------- /src/Test/shim.js: -------------------------------------------------------------------------------- 1 | import { UUIDTag /*Symbol_uuid*/ } from "../Immutable/Tag"; 2 | 3 | if (typeof global.Symbol === "undefined") { 4 | global.Symbol = { 5 | iterator: UUIDTag("4d6d99dc-20e8-4db1-8acd-c05c1121326e") 6 | }; 7 | 8 | /*var id = 0; 9 | var registry = {}; 10 | 11 | global.Symbol = function () { 12 | return "(Symbol " + Symbol_uuid + " " + (++id) + ")"; 13 | }; 14 | 15 | global.Symbol.for = function (s) { 16 | if (typeof s !== "string") { 17 | throw new Error("Must be string"); 18 | } 19 | 20 | var x = registry[x]; 21 | if (x == null) { 22 | x = registry[s] = global.Symbol(); 23 | } 24 | return x; 25 | }; 26 | 27 | global.Symbol.keyFor = function (x) { 28 | return registry[s]; 29 | }; 30 | 31 | global.Symbol.iterator = global.Symbol();*/ 32 | } 33 | -------------------------------------------------------------------------------- /src/Test/util.js: -------------------------------------------------------------------------------- 1 | import { each, isTuple, fromJSON, toJSON, equal, toJS } from "../Immutable"; 2 | import { nil } from "../Immutable/static"; 3 | import { assert } from "./assert"; 4 | 5 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random 6 | export function random_int(min, max) { 7 | return Math.floor(Math.random() * (max - min)) + min; 8 | } 9 | 10 | export function otherSort(x, y) { 11 | if (x === y) { 12 | return 0; 13 | } else if (x < y) { 14 | return -1; 15 | } else { 16 | return 1; 17 | } 18 | } 19 | 20 | // http://bost.ocks.org/mike/shuffle/ 21 | // TODO test whether this algorithm has statistical bias or not 22 | // TODO this is only needed for "test/test.js" 23 | function shuffle(array) { 24 | var i = array.length; 25 | 26 | while (i) { 27 | var j = random_int(0, i); 28 | --i; 29 | var temp = array[i]; 30 | array[i] = array[j]; 31 | array[j] = temp; 32 | } 33 | } 34 | 35 | export function random_list(max) { 36 | var out = []; 37 | for (var i = 0; i < max; ++i) { 38 | out.push(i); 39 | } 40 | shuffle(out); 41 | return out; 42 | } 43 | 44 | 45 | //var { zip, toArray } = require('sjs:sequence'); 46 | 47 | // TODO code duplication with Immutable/util 48 | function isObject(x) { 49 | return Object(x) === x; 50 | } 51 | 52 | var hasOwnProperty = {}.hasOwnProperty; 53 | 54 | /*function shallowEqual(x, y) { 55 | if (Array.isArray(x) && Array.isArray(y)) { 56 | if (x.length === y.length) { 57 | for (var i = 0, l = x.length; i < l; ++i) { 58 | if (x[i] !== y[i]) { 59 | return false; 60 | } 61 | } 62 | return true; 63 | } else { 64 | return false; 65 | } 66 | } else { 67 | return x === y; 68 | } 69 | }*/ 70 | 71 | export function deepEqual(x, y) { 72 | if (x === y) { 73 | return true; 74 | 75 | } else if (Array.isArray(x) && Array.isArray(y)) { 76 | if (x.length === y.length) { 77 | for (var i = 0, l = x.length; i < l; ++i) { 78 | if (!deepEqual(x[i], y[i])) { 79 | return false; 80 | } 81 | } 82 | return true; 83 | } else { 84 | return false; 85 | } 86 | 87 | } else if (isObject(x) && isObject(y)) { 88 | if (Object.getPrototypeOf(x) === Object.getPrototypeOf(y)) { 89 | var x_keys = Object.getOwnPropertyNames(x); 90 | var y_keys = Object.getOwnPropertyNames(y); 91 | 92 | for (var i = 0, l = x_keys.length; i < l; ++i) { 93 | var s = x_keys[i]; 94 | if (hasOwnProperty.call(y, s)) { 95 | if (!deepEqual(x[s], y[s])) { 96 | return false; 97 | } 98 | } else { 99 | return false; 100 | } 101 | } 102 | 103 | for (var i = 0, l = y_keys.length; i < l; ++i) { 104 | var s = y_keys[i]; 105 | if (!hasOwnProperty.call(x, s)) { 106 | return false; 107 | } 108 | } 109 | 110 | return true; 111 | 112 | } else { 113 | return false; 114 | } 115 | 116 | } else { 117 | return false; 118 | } 119 | } 120 | 121 | // TODO test that this works correctly 122 | export function verify_tree(tree) { 123 | var sort = tree.sort; 124 | var hash_fn = tree.hash_fn; 125 | 126 | function loop(node, lt, gt) { 127 | if (node !== nil) { 128 | var left = node.left; 129 | var right = node.right; 130 | 131 | assert(node.depth === Math.max(left.depth, right.depth) + 1); 132 | 133 | var diff = left.depth - right.depth; 134 | assert(diff === -1 || diff === 0 || diff === 1); 135 | 136 | // Every left node must be lower than the parent node 137 | lt.forEach(function (parent) { 138 | assert(sort(hash_fn(node.key), hash_fn(parent.key)) < 0); 139 | }); 140 | 141 | // Every right node must be greater than the parent node 142 | gt.forEach(function (parent) { 143 | assert(sort(hash_fn(node.key), hash_fn(parent.key)) > 0); 144 | }); 145 | 146 | loop(left, lt.concat([node]), gt); 147 | loop(right, lt, gt.concat([node])); 148 | } 149 | } 150 | loop(tree.root, [], []); 151 | } 152 | 153 | export function test_each(input, expected) { 154 | var a = []; 155 | each(input, function (x) { 156 | a.push(x); 157 | }); 158 | assert(deepEqual(a, expected)); 159 | } 160 | 161 | export function test_each_dict(input, expected) { 162 | var a = []; 163 | each(input, function (x) { 164 | assert(isTuple(x)); 165 | a.push(x._values); 166 | }); 167 | assert(deepEqual(a, expected)); 168 | } 169 | 170 | export function verify_json_equal(x) { 171 | var y = toJSON(x); 172 | assert(y !== x); 173 | 174 | var z = fromJSON(y); 175 | assert(z !== y); 176 | 177 | assert(equal(x, z)); 178 | } 179 | 180 | 181 | export function verify_json(x, expected) { 182 | var y = toJSON(x); 183 | assert(y !== x); 184 | 185 | var z = fromJSON(y); 186 | assert(z !== y); 187 | 188 | assert(equal(x, z)); 189 | assert(deepEqual(toJS(x), expected)); 190 | assert(deepEqual(toJS(z), expected)); 191 | } 192 | --------------------------------------------------------------------------------