Created
April 4, 2021 09:13
-
-
Save xuhen/e338045fbb022d0fc250fff240fdcf91 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Pushes the items of `iterable` into `sink`, a generator. | |
| * It uses the generator method `next()` to do so. | |
| */ | |
| function send(iterable: Iterable<any>, sink: any, config?: { log: boolean }) { | |
| for (const x of iterable) { | |
| sink.next(x); | |
| if (config && config.log) { | |
| console.log('===>', x); | |
| } | |
| } | |
| sink.return(); // signal end of stream | |
| } | |
| /** | |
| * This generator logs everything that it receives via `next()`. | |
| */ | |
| const logItems = coroutine(function* (): any { | |
| try { | |
| while (true) { | |
| const item = yield; // receive item via `next()` | |
| console.log(item); | |
| } | |
| } finally { | |
| console.log('DONE'); | |
| } | |
| }); | |
| /** | |
| * Receives a sequence of characters (via the generator object | |
| * method `next()`), groups them into words and pushes them | |
| * into the generator `sink`. | |
| */ | |
| const tokenize = coroutine(function* (sink: any): any { | |
| try { | |
| while (true) { | |
| // (A) | |
| let ch = yield; // (B) | |
| if (isWordChar(ch)) { | |
| // A word has started | |
| let word = ''; | |
| try { | |
| do { | |
| word += ch; | |
| ch = yield; // (C) | |
| } while (isWordChar(ch)); | |
| } finally { | |
| // The word is finished. | |
| // We get here if | |
| // - the loop terminates normally | |
| // - the loop is terminated via `return()` in line C | |
| sink.next(word); // (D) | |
| } | |
| } | |
| // Ignore all other characters | |
| } | |
| } finally { | |
| // We only get here if the infinite loop is terminated | |
| // via `return()` (in line B or C). | |
| // Forward `return()` to `sink` so that it is also | |
| // aware of the end of stream. | |
| sink.return(); | |
| } | |
| }); | |
| function isWordChar(ch: string) { | |
| return /^[A-Za-z0-9]$/.test(ch); | |
| } | |
| /** | |
| * Returns a function that, when called, | |
| * returns a generator object that is immediately | |
| * ready for input via `next()` | |
| */ | |
| function coroutine(generatorFunction: any) { | |
| return function (...args: any[]) { | |
| const generatorObject = generatorFunction(...args); | |
| generatorObject.next(); | |
| return generatorObject; | |
| }; | |
| } | |
| /** | |
| * Receives a sequence of strings (via the generator object | |
| * method `next()`) and pushes only those strings to the generator | |
| * `sink` that are “numbers” (consist only of decimal digits). | |
| */ | |
| const extractNumbers = coroutine(function* (sink: any): any { | |
| try { | |
| while (true) { | |
| const word = yield; | |
| if (/^[0-9]+$/.test(word)) { | |
| sink.next(Number(word)); | |
| } | |
| } | |
| } finally { | |
| // Only reached via `return()`, forward. | |
| sink.return(); | |
| } | |
| }); | |
| /** | |
| * Receives a sequence of numbers (via the generator object | |
| * method `next()`). For each number, it pushes the total sum | |
| * so far to the generator `sink`. | |
| */ | |
| const addNumbers = coroutine(function* (sink: any): any { | |
| let sum = 0; | |
| try { | |
| while (true) { | |
| sum += yield; | |
| sink.next(sum); | |
| } | |
| } finally { | |
| // We received an end-of-stream | |
| sink.return(); // signal end of stream | |
| } | |
| }); | |
| const INPUT = '2 apples and 5 oranges.'; | |
| const CHAIN = tokenize(extractNumbers(addNumbers(logItems()))); | |
| // send(INPUT, CHAIN); | |
| const CHAIN2 = tokenize( | |
| extractNumbers(addNumbers(logItems({ prefix: '-> ' }))) | |
| ); | |
| send(INPUT, CHAIN2, { log: true }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment