addon-sdk/source/lib/sdk/util/sequence.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:554d8037070a
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 */
5 "use strict";
6
7 module.metadata = {
8 "stability": "experimental"
9 };
10
11 // Disclamer:
12 // In this module we'll have some common argument / variable names
13 // to hint their type or behavior.
14 //
15 // - `f` stands for "function" that is intended to be side effect
16 // free.
17 // - `p` stands for "predicate" that is function which returns logical
18 // true or false and is intended to be side effect free.
19 // - `x` / `y` single item of the sequence.
20 // - `xs` / `ys` sequence of `x` / `y` items where `x` / `y` signifies
21 // type of the items in sequence, so sequence is not of the same item.
22 // - `_` used for argument(s) or variable(s) who's values are ignored.
23
24 const { complement, flip, identity } = require("../lang/functional");
25 const { iteratorSymbol } = require("../util/iteration");
26 const { isArray, isArguments, isMap, isSet,
27 isString, isBoolean, isNumber } = require("../lang/type");
28
29 const Sequence = function Sequence(iterator) {
30 if (iterator.isGenerator && iterator.isGenerator())
31 this[iteratorSymbol] = iterator;
32 else
33 throw TypeError("Expected generator argument");
34 };
35 exports.Sequence = Sequence;
36
37 const polymorphic = dispatch => x =>
38 x === null ? dispatch.null(null) :
39 x === void(0) ? dispatch.void(void(0)) :
40 isArray(x) ? (dispatch.array || dispatch.indexed)(x) :
41 isString(x) ? (dispatch.string || dispatch.indexed)(x) :
42 isArguments(x) ? (dispatch.arguments || dispatch.indexed)(x) :
43 isMap(x) ? dispatch.map(x) :
44 isSet(x) ? dispatch.set(x) :
45 isNumber(x) ? dispatch.number(x) :
46 isBoolean(x) ? dispatch.boolean(x) :
47 dispatch.default(x);
48
49 const nogen = function*() {};
50 const empty = () => new Sequence(nogen);
51 exports.empty = empty;
52
53 const seq = polymorphic({
54 null: empty,
55 void: empty,
56 array: identity,
57 string: identity,
58 arguments: identity,
59 map: identity,
60 set: identity,
61 default: x => x instanceof Sequence ? x : new Sequence(x)
62 });
63 exports.seq = seq;
64
65
66
67
68 // Function to cast seq to string.
69 const string = (...etc) => "".concat(...etc);
70 exports.string = string;
71
72 // Function for casting seq to plain object.
73 const object = (...pairs) => {
74 let result = {};
75 for (let [key, value] of pairs)
76 result[key] = value;
77
78 return result;
79 };
80 exports.object = object;
81
82 // Takes `getEnumerator` function that returns `nsISimpleEnumerator`
83 // and creates lazy sequence of it's items. Note that function does
84 // not take `nsISimpleEnumerator` itslef because that would allow
85 // single iteration, which would not be consistent with rest of the
86 // lazy sequences.
87 const fromEnumerator = getEnumerator => seq(function* () {
88 const enumerator = getEnumerator();
89 while (enumerator.hasMoreElements())
90 yield enumerator.getNext();
91 });
92 exports.fromEnumerator = fromEnumerator;
93
94 // Takes `object` and returns lazy sequence of own `[key, value]`
95 // pairs (does not include inherited and non enumerable keys).
96 const pairs = polymorphic({
97 null: empty,
98 void: empty,
99 map: identity,
100 indexed: indexed => seq(function* () {
101 const count = indexed.length;
102 let index = 0;
103 while (index < count) {
104 yield [index, indexed[index]];
105 index = index + 1;
106 }
107 }),
108 default: object => seq(function* () {
109 for (let key of Object.keys(object))
110 yield [key, object[key]];
111 })
112 });
113 exports.pairs = pairs;
114
115
116 const keys = polymorphic({
117 null: empty,
118 void: empty,
119 indexed: indexed => seq(function* () {
120 const count = indexed.length;
121 let index = 0;
122 while (index < count) {
123 yield index;
124 index = index + 1;
125 }
126 }),
127 map: map => seq(function* () {
128 for (let [key, _] of map)
129 yield key;
130 }),
131 default: object => seq(function* () {
132 for (let key of Object.keys(object))
133 yield key;
134 })
135 });
136 exports.keys = keys;
137
138
139 const values = polymorphic({
140 null: empty,
141 void: empty,
142 set: identity,
143 indexed: indexed => seq(function* () {
144 const count = indexed.length;
145 let index = 0;
146 while (index < count) {
147 yield indexed[index];
148 index = index + 1;
149 }
150 }),
151 map: map => seq(function* () {
152 for (let [_, value] of map) yield value;
153 }),
154 default: object => seq(function* () {
155 for (let key of Object.keys(object)) yield object[key];
156 })
157 });
158 exports.values = values;
159
160
161
162 // Returns a lazy sequence of `x`, `f(x)`, `f(f(x))` etc.
163 // `f` must be free of side-effects. Note that returned
164 // sequence is infinite so it must be consumed partially.
165 //
166 // Implements clojure iterate:
167 // http://clojuredocs.org/clojure_core/clojure.core/iterate
168 const iterate = (f, x) => seq(function* () {
169 let state = x;
170 while (true) {
171 yield state;
172 state = f(state);
173 }
174 });
175 exports.iterate = iterate;
176
177 // Returns a lazy sequence of the items in sequence for which `p(item)`
178 // returns `true`. `p` must be free of side-effects.
179 //
180 // Implements clojure filter:
181 // http://clojuredocs.org/clojure_core/clojure.core/filter
182 const filter = (p, sequence) => seq(function* () {
183 if (sequence !== null && sequence !== void(0)) {
184 for (let item of sequence) {
185 if (p(item))
186 yield item;
187 }
188 }
189 });
190 exports.filter = filter;
191
192 // Returns a lazy sequence consisting of the result of applying `f` to the
193 // set of first items of each sequence, followed by applying f to the set
194 // of second items in each sequence, until any one of the sequences is
195 // exhausted. Any remaining items in other sequences are ignored. Function
196 // `f` should accept number-of-sequences arguments.
197 //
198 // Implements clojure map:
199 // http://clojuredocs.org/clojure_core/clojure.core/map
200 const map = (f, ...sequences) => seq(function* () {
201 const count = sequences.length;
202 // Optimize a single sequence case
203 if (count === 1) {
204 let [sequence] = sequences;
205 if (sequence !== null && sequence !== void(0)) {
206 for (let item of sequence)
207 yield f(item);
208 }
209 }
210 else {
211 // define args array that will be recycled on each
212 // step to aggregate arguments to be passed to `f`.
213 let args = [];
214 // define inputs to contain started generators.
215 let inputs = [];
216
217 let index = 0;
218 while (index < count) {
219 inputs[index] = sequences[index][iteratorSymbol]();
220 index = index + 1;
221 }
222
223 // Run loop yielding of applying `f` to the set of
224 // items at each step until one of the `inputs` is
225 // exhausted.
226 let done = false;
227 while (!done) {
228 let index = 0;
229 let value = void(0);
230 while (index < count && !done) {
231 ({ done, value }) = inputs[index].next();
232
233 // If input is not exhausted yet store value in args.
234 if (!done) {
235 args[index] = value;
236 index = index + 1;
237 }
238 }
239
240 // If none of the inputs is exhasted yet, `args` contain items
241 // from each input so we yield application of `f` over them.
242 if (!done)
243 yield f(...args);
244 }
245 }
246 });
247 exports.map = map;
248
249 // Returns a lazy sequence of the intermediate values of the reduction (as
250 // per reduce) of sequence by `f`, starting with `initial` value if provided.
251 //
252 // Implements clojure reductions:
253 // http://clojuredocs.org/clojure_core/clojure.core/reductions
254 const reductions = (...params) => {
255 const count = params.length;
256 let hasInitial = false;
257 let f, initial, source;
258 if (count === 2) {
259 ([f, source]) = params;
260 }
261 else if (count === 3) {
262 ([f, initial, source]) = params;
263 hasInitial = true;
264 }
265 else {
266 throw Error("Invoked with wrong number of arguments: " + count);
267 }
268
269 const sequence = seq(source);
270
271 return seq(function* () {
272 let started = hasInitial;
273 let result = void(0);
274
275 // If initial is present yield it.
276 if (hasInitial)
277 yield (result = initial);
278
279 // For each item of the sequence accumulate new result.
280 for (let item of sequence) {
281 // If nothing has being yield yet set result to first
282 // item and yield it.
283 if (!started) {
284 started = true;
285 yield (result = item);
286 }
287 // Otherwise accumulate new result and yield it.
288 else {
289 yield (result = f(result, item));
290 }
291 }
292
293 // If nothing has being yield yet it's empty sequence and no
294 // `initial` was provided in which case we need to yield `f()`.
295 if (!started)
296 yield f();
297 });
298 };
299 exports.reductions = reductions;
300
301 // `f` should be a function of 2 arguments. If `initial` is not supplied,
302 // returns the result of applying `f` to the first 2 items in sequence, then
303 // applying `f` to that result and the 3rd item, etc. If sequence contains no
304 // items, `f` must accept no arguments as well, and reduce returns the
305 // result of calling f with no arguments. If sequence has only 1 item, it
306 // is returned and `f` is not called. If `initial` is supplied, returns the
307 // result of applying `f` to `initial` and the first item in sequence, then
308 // applying `f` to that result and the 2nd item, etc. If sequence contains no
309 // items, returns `initial` and `f` is not called.
310 //
311 // Implements clojure reduce:
312 // http://clojuredocs.org/clojure_core/clojure.core/reduce
313 const reduce = (...args) => {
314 const xs = reductions(...args);
315 let x;
316 for (x of xs) void(0);
317 return x;
318 };
319 exports.reduce = reduce;
320
321 const each = (f, sequence) => {
322 for (let x of seq(sequence)) void(f(x));
323 };
324 exports.each = each;
325
326
327 const inc = x => x + 1;
328 // Returns the number of items in the sequence. `count(null)` && `count()`
329 // returns `0`. Also works on strings, arrays, Maps & Sets.
330
331 // Implements clojure count:
332 // http://clojuredocs.org/clojure_core/clojure.core/count
333 const count = polymorphic({
334 null: _ => 0,
335 void: _ => 0,
336 indexed: indexed => indexed.length,
337 map: map => map.size,
338 set: set => set.size,
339 default: xs => reduce(inc, 0, xs)
340 });
341 exports.count = count;
342
343 // Returns `true` if sequence has no items.
344
345 // Implements clojure empty?:
346 // http://clojuredocs.org/clojure_core/clojure.core/empty_q
347 const isEmpty = sequence => {
348 // Treat `null` and `undefined` as empty sequences.
349 if (sequence === null || sequence === void(0))
350 return true;
351
352 // If contains any item non empty so return `false`.
353 for (let _ of sequence)
354 return false;
355
356 // If has not returned yet, there was nothing to iterate
357 // so it's empty.
358 return true;
359 };
360 exports.isEmpty = isEmpty;
361
362 const and = (a, b) => a && b;
363
364 // Returns true if `p(x)` is logical `true` for every `x` in sequence, else
365 // `false`.
366 //
367 // Implements clojure every?:
368 // http://clojuredocs.org/clojure_core/clojure.core/every_q
369 const isEvery = (p, sequence) => {
370 if (sequence !== null && sequence !== void(0)) {
371 for (let item of sequence) {
372 if (!p(item))
373 return false;
374 }
375 }
376 return true;
377 };
378 exports.isEvery = isEvery;
379
380 // Returns the first logical true value of (p x) for any x in sequence,
381 // else `null`.
382 //
383 // Implements clojure some:
384 // http://clojuredocs.org/clojure_core/clojure.core/some
385 const some = (p, sequence) => {
386 if (sequence !== null && sequence !== void(0)) {
387 for (let item of sequence) {
388 if (p(item))
389 return true;
390 }
391 }
392 return null;
393 };
394 exports.some = some;
395
396 // Returns a lazy sequence of the first `n` items in sequence, or all items if
397 // there are fewer than `n`.
398 //
399 // Implements clojure take:
400 // http://clojuredocs.org/clojure_core/clojure.core/take
401 const take = (n, sequence) => n <= 0 ? empty() : seq(function* () {
402 let count = n;
403 for (let item of sequence) {
404 yield item;
405 count = count - 1;
406 if (count === 0) break;
407 }
408 });
409 exports.take = take;
410
411 // Returns a lazy sequence of successive items from sequence while
412 // `p(item)` returns `true`. `p` must be free of side-effects.
413 //
414 // Implements clojure take-while:
415 // http://clojuredocs.org/clojure_core/clojure.core/take-while
416 const takeWhile = (p, sequence) => seq(function* () {
417 for (let item of sequence) {
418 if (!p(item))
419 break;
420
421 yield item;
422 }
423 });
424 exports.takeWhile = takeWhile;
425
426 // Returns a lazy sequence of all but the first `n` items in
427 // sequence.
428 //
429 // Implements clojure drop:
430 // http://clojuredocs.org/clojure_core/clojure.core/drop
431 const drop = (n, sequence) => seq(function* () {
432 if (sequence !== null && sequence !== void(0)) {
433 let count = n;
434 for (let item of sequence) {
435 if (count > 0)
436 count = count - 1;
437 else
438 yield item;
439 }
440 }
441 });
442 exports.drop = drop;
443
444 // Returns a lazy sequence of the items in sequence starting from the
445 // first item for which `p(item)` returns falsy value.
446 //
447 // Implements clojure drop-while:
448 // http://clojuredocs.org/clojure_core/clojure.core/drop-while
449 const dropWhile = (p, sequence) => seq(function* () {
450 let keep = false;
451 for (let item of sequence) {
452 keep = keep || !p(item);
453 if (keep) yield item;
454 }
455 });
456 exports.dropWhile = dropWhile;
457
458 // Returns a lazy sequence representing the concatenation of the
459 // suplied sequences.
460 //
461 // Implements clojure conact:
462 // http://clojuredocs.org/clojure_core/clojure.core/concat
463 const concat = (...sequences) => seq(function* () {
464 for (let sequence of sequences)
465 for (let item of sequence)
466 yield item;
467 });
468 exports.concat = concat;
469
470 // Returns the first item in the sequence.
471 //
472 // Implements clojure first:
473 // http://clojuredocs.org/clojure_core/clojure.core/first
474 const first = sequence => {
475 if (sequence !== null && sequence !== void(0)) {
476 for (let item of sequence)
477 return item;
478 }
479 return null;
480 };
481 exports.first = first;
482
483 // Returns a possibly empty sequence of the items after the first.
484 //
485 // Implements clojure rest:
486 // http://clojuredocs.org/clojure_core/clojure.core/rest
487 const rest = sequence => drop(1, sequence);
488 exports.rest = rest;
489
490 // Returns the value at the index. Returns `notFound` or `undefined`
491 // if index is out of bounds.
492 const nth = (xs, n, notFound) => {
493 if (n >= 0) {
494 if (isArray(xs) || isArguments(xs) || isString(xs)) {
495 return n < xs.length ? xs[n] : notFound;
496 }
497 else if (xs !== null && xs !== void(0)) {
498 let count = n;
499 for (let x of xs) {
500 if (count <= 0)
501 return x;
502
503 count = count - 1;
504 }
505 }
506 }
507 return notFound;
508 };
509 exports.nth = nth;
510
511 // Return the last item in sequence, in linear time.
512 // If `sequence` is an array or string or arguments
513 // returns in constant time.
514 // Implements clojure last:
515 // http://clojuredocs.org/clojure_core/clojure.core/last
516 const last = polymorphic({
517 null: _ => null,
518 void: _ => null,
519 indexed: indexed => indexed[indexed.length - 1],
520 map: xs => reduce((_, x) => x, xs),
521 set: xs => reduce((_, x) => x, xs),
522 default: xs => reduce((_, x) => x, xs)
523 });
524 exports.last = last;
525
526 // Return a lazy sequence of all but the last `n` (default 1) items
527 // from the give `xs`.
528 //
529 // Implements clojure drop-last:
530 // http://clojuredocs.org/clojure_core/clojure.core/drop-last
531 const dropLast = flip((xs, n=1) => seq(function* () {
532 let ys = [];
533 for (let x of xs) {
534 ys.push(x);
535 if (ys.length > n)
536 yield ys.shift();
537 }
538 }));
539 exports.dropLast = dropLast;
540
541 // Returns a lazy sequence of the elements of `xs` with duplicates
542 // removed
543 //
544 // Implements clojure distinct
545 // http://clojuredocs.org/clojure_core/clojure.core/distinct
546 const distinct = sequence => seq(function* () {
547 let items = new Set();
548 for (let item of sequence) {
549 if (!items.has(item)) {
550 items.add(item);
551 yield item;
552 }
553 }
554 });
555 exports.distinct = distinct;
556
557 // Returns a lazy sequence of the items in `xs` for which
558 // `p(x)` returns false. `p` must be free of side-effects.
559 //
560 // Implements clojure remove
561 // http://clojuredocs.org/clojure_core/clojure.core/remove
562 const remove = (p, xs) => filter(complement(p), xs);
563 exports.remove = remove;
564
565 // Returns the result of applying concat to the result of
566 // `map(f, xs)`. Thus function `f` should return a sequence.
567 //
568 // Implements clojure mapcat
569 // http://clojuredocs.org/clojure_core/clojure.core/mapcat
570 const mapcat = (f, sequence) => seq(function* () {
571 const sequences = map(f, sequence);
572 for (let sequence of sequences)
573 for (let item of sequence)
574 yield item;
575 });
576 exports.mapcat = mapcat;

mercurial