UNPKG

@connectv/core

Version:

agent-based reactive programming library for typescript/javascript

170 lines 5.58 kB
import { map, share } from 'rxjs/operators'; import { Emission } from '../shared/emission'; import { Control } from '../pin/control'; import { Pin } from '../pin/pin'; import group from '../pin/group'; import _map from '../pin/map'; import filter, { block } from '../pin/filter'; import pipe from '../pin/pipe'; import { Agent } from './agent'; /** * * Creates a sequence token that denotes events happening between `min` and * `max` number of times. * * @param min the minimum number of times the event should happen * @param max the maximum number of times the event should happen * */ export function range(min, max) { return { accepts(_, list) { return max === undefined || list.length < max; }, complete(list) { return list.length >= min; } }; } /** * * Creates a sequence token that denotes events happening a * specified number of times exactly * * @param c the number of times the event should happen. * */ export function count(c) { return range(c, c); } /** * * Sequence token denoting an event that may or may not happen (multiple times). * */ export const maybesome = range(0); /** * * Sequence token denoting an event that happens at least once. * */ export const some = range(1); /** * * Represents [sequence](https://connective.dev/docs/sequence) agents. * */ export class Sequence extends Agent { /** * * @param tokens the tokens denoting the sequence of desired events. Each token must be * - A `SequenceToken`, * - A number, meaning that an event should happen that number of times exactly, * - `'+'` meaning the event should happen at least once, * - `'*'` meaning the event may or may not happen one or multiple times. * */ constructor(tokens) { super({ inputs: tokens.map((_, index) => index.toString()), outputs: ['out'] }); this._head = 0; this._control = new Control(); this._relay = new Pin(); this.tokens = tokens.map(t => { if (typeof t == 'number') return count(t); if (t === '+') return some; if (t === '*') return maybesome; return t; }); tokens.forEach((_, index) => { this.in(index).to(pipe(map(e => { this._take(e, index, true); return e; }))).to(this._relay); }); this.reset(); } _take(emission, index, retry = false) { if (index == this._head) { if (this.tokens[index].accepts(emission, this._seq[index])) this._seq[index].push(emission); else { this.reset(); if (retry) this._take(emission, index); } } else { if (index < this._head) { this.reset(); this._take(emission, index); } else { if (this._seek(index)) this._take(emission, index, retry); else { this.reset(); if (this._seek(index)) this._take(emission, index, retry); } } } } _seek(index) { for (let i = this._head; i < index; i++) if (!this.tokens[i].complete(this._seq[i])) return false; this._head = index; return true; } get _complete() { return this._seq.every((e, index) => this.tokens[index].complete(e)); } reset() { this._seq = this.tokens.map(_ => []); this._head = 0; return this; } createOutput(label) { this.checkOutput(label); return group(this._control.to(_map(() => this.reset())).to(block()), this._relay.to(filter(() => this._complete))).to(pipe(map(() => { let _vals = this._seq.map(_comp => (_comp.length == 1) ? (_comp[0].value) : (_comp.map(_ => _.value))); let _emission = Emission.from(this._seq.reduce((all, list) => all.concat(list), []), (this.tokens.length == 1) ? _vals[0] : _vals); return _emission; }), share())); } createEntries() { return (this.signature.inputs || []).map(i => this.in(i)); } createExits() { return [this.output]; } clear() { this.reset(); this._control.clear(); this._relay.clear(); return super.clear(); } /** * * Resets the sequence being tracked when receiving emissions * on `.control`. * */ get control() { return this._control; } /** * * Shortcut for `.out('out')`, which will emit completed sequences. * [Read this](https://connective.dev/docs/sequence#signature) for more details. * */ get output() { return this.out('out'); } } /** * * Creates a [sequence](https://connective.dev/docs/sequence) agent. * Sequence agents can determine if a specific sequence of events has occured. * [Checkout the docs](https://connective.dev/docs/sequence) for examples and further information. * * @param tokens the tokens denoting the sequence of desired events. Each token must be * - A `SequenceToken`, * - A number, meaning that an event should happen that number of times exactly, * - `'+'` meaning the event should happen at least once, * - `'*'` meaning the event may or may not happen one or multiple times. * */ export function sequence(...tokens) { return new Sequence(tokens); } export default sequence; //# sourceMappingURL=sequence.js.map