2021-03-02 08:50:23 +00:00
|
|
|
import { Dictionary, IdentitySet, Record, Tuple, Value, is, preserves } from 'preserves';
|
2021-02-24 20:48:55 +00:00
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
2021-02-17 19:57:15 +00:00
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
if ('stackTraceLimit' in Error) {
|
|
|
|
Error.stackTraceLimit = Infinity;
|
|
|
|
}
|
|
|
|
|
2021-02-23 13:35:23 +00:00
|
|
|
export type Assertion = Value<Ref>;
|
2021-02-22 18:37:47 +00:00
|
|
|
export type Handle = number;
|
2021-02-22 09:12:10 +00:00
|
|
|
export type ExitReason = null | { ok: true } | { ok: false, err: Error };
|
2021-02-23 14:41:54 +00:00
|
|
|
export type LocalAction = (t: Turn) => void;
|
2021-02-17 19:57:15 +00:00
|
|
|
|
2021-02-22 09:12:10 +00:00
|
|
|
export interface Entity {
|
2021-02-23 15:16:15 +00:00
|
|
|
assert(turn: Turn, assertion: Assertion, handle: Handle): void;
|
|
|
|
retract(turn: Turn, handle: Handle): void;
|
|
|
|
message(turn: Turn, body: Assertion): void;
|
|
|
|
sync(turn: Turn, peer: Ref): void;
|
2021-02-17 19:57:15 +00:00
|
|
|
}
|
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
export interface Ref {
|
|
|
|
readonly relay: Actor;
|
|
|
|
readonly target: Partial<Entity>;
|
|
|
|
readonly attenuation?: Attenuation;
|
|
|
|
}
|
|
|
|
|
2021-02-25 10:41:26 +00:00
|
|
|
export type Attenuation = Array<RewriteStage>; // array of stages, each a list of alternatives
|
|
|
|
export type RewriteStage = Array<Rewrite>;
|
2021-02-24 20:48:55 +00:00
|
|
|
export type Rewrite = { pattern: Pattern, template: Template };
|
|
|
|
|
2021-02-25 21:24:21 +00:00
|
|
|
export const _CRec = Symbol.for('rec');
|
|
|
|
export const CRec = Record.makeConstructor<{label: Assertion, arity: number}, Ref>()(
|
|
|
|
_CRec, ['label', 'arity']);
|
|
|
|
|
|
|
|
export const _CArr = Symbol.for('arr');
|
|
|
|
export const CArr = Record.makeConstructor<{arity: number}, Ref>()(
|
|
|
|
_CArr, ['arity']);
|
|
|
|
|
|
|
|
export const _CDict = Symbol.for('dict');
|
|
|
|
export const CDict = Record.makeConstructor<{}, Ref>()(
|
|
|
|
_CDict, []);
|
|
|
|
|
2021-02-25 22:23:51 +00:00
|
|
|
export type ConstructorSpec =
|
2021-03-02 11:40:50 +00:00
|
|
|
| Record<typeof _CRec, [Assertion, number], Ref>
|
2021-02-25 22:23:51 +00:00
|
|
|
| Record<typeof _CArr, [number], Ref>
|
|
|
|
| Record<typeof _CDict, [], Ref>;
|
2021-02-25 21:24:21 +00:00
|
|
|
|
|
|
|
export const _PDiscard = Symbol.for('_');
|
|
|
|
export const PDiscard = Record.makeConstructor<{}, Ref>()(
|
|
|
|
_PDiscard, []);
|
|
|
|
|
|
|
|
export const _PBind = Symbol.for('bind');
|
2021-02-25 22:23:51 +00:00
|
|
|
export const PBind = Record.makeConstructor<{name: string, pattern: Pattern}, Ref>()(
|
2021-02-25 21:24:21 +00:00
|
|
|
_PBind, ['name', 'pattern']);
|
|
|
|
|
|
|
|
export const _PAnd = Symbol.for('and');
|
2021-02-25 22:23:51 +00:00
|
|
|
export const PAnd = Record.makeConstructor<{patterns: Array<Pattern>}, Ref>()(
|
2021-02-25 21:24:21 +00:00
|
|
|
_PAnd, ['patterns']);
|
|
|
|
|
|
|
|
export const _PNot = Symbol.for('not');
|
2021-02-25 22:23:51 +00:00
|
|
|
export const PNot = Record.makeConstructor<{pattern: Pattern}, Ref>()(
|
2021-02-25 21:24:21 +00:00
|
|
|
_PNot, ['pattern']);
|
|
|
|
|
|
|
|
export const _Lit = Symbol.for('lit');
|
|
|
|
export const Lit = Record.makeConstructor<{value: Assertion}, Ref>()(
|
|
|
|
_Lit, ['value']);
|
|
|
|
|
|
|
|
export const _PCompound = Symbol.for('compound');
|
|
|
|
export const PCompound =
|
2021-03-02 08:50:23 +00:00
|
|
|
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Pattern, Ref>}, Ref>()(
|
2021-02-25 21:24:21 +00:00
|
|
|
_PCompound, ['ctor', 'members']);
|
|
|
|
|
2021-02-25 22:23:51 +00:00
|
|
|
export type Pattern =
|
|
|
|
| Record<typeof _PDiscard, [], Ref>
|
|
|
|
| Record<typeof _PBind, [string, Pattern], Ref>
|
|
|
|
| Record<typeof _PAnd, [Pattern[]], Ref>
|
|
|
|
| Record<typeof _PNot, [Pattern], Ref>
|
|
|
|
| Record<typeof _Lit, [Assertion], Ref>
|
2021-03-02 08:50:23 +00:00
|
|
|
| Record<typeof _PCompound, [ConstructorSpec, Dictionary<Pattern, Ref>], Ref>;
|
2021-02-25 21:24:21 +00:00
|
|
|
|
|
|
|
export const _TRef = Symbol.for('ref');
|
|
|
|
export const TRef = Record.makeConstructor<{name: string}, Ref>()(
|
|
|
|
_TRef, ['name']);
|
|
|
|
|
|
|
|
export const _TCompound = Symbol.for('compound');
|
|
|
|
export const TCompound =
|
2021-03-02 08:50:23 +00:00
|
|
|
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Template, Ref>}, Ref>()(
|
2021-02-25 21:24:21 +00:00
|
|
|
_TCompound, ['ctor', 'members']);
|
|
|
|
|
2021-02-25 22:23:51 +00:00
|
|
|
export type Template =
|
|
|
|
| Record<typeof _TRef, [string], Ref>
|
|
|
|
| Record<typeof _Lit, [Assertion], Ref>
|
2021-03-02 08:50:23 +00:00
|
|
|
| Record<typeof _TCompound, [ConstructorSpec, Dictionary<Template, Ref>], Ref>;
|
2021-02-24 20:48:55 +00:00
|
|
|
|
|
|
|
export type Bindings = { [name: string]: Assertion };
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
export function isRef(v: any): v is Ref {
|
|
|
|
return 'relay' in v && v.relay instanceof Actor && 'target' in v;
|
|
|
|
}
|
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
let nextActorId = 0;
|
2021-03-02 11:41:06 +00:00
|
|
|
// export function __setNextActorId(v: number) {
|
|
|
|
// nextActorId = v;
|
|
|
|
// }
|
2021-03-02 08:50:23 +00:00
|
|
|
|
2021-02-22 18:51:19 +00:00
|
|
|
export class Actor {
|
2021-03-02 08:50:23 +00:00
|
|
|
readonly id = nextActorId++;
|
2021-02-23 14:53:42 +00:00
|
|
|
readonly outbound: Map<Handle, Ref>;
|
2021-02-17 19:57:15 +00:00
|
|
|
exitReason: ExitReason = null;
|
2021-03-02 08:50:23 +00:00
|
|
|
// readonly exitHooks: Array<LocalAction> = [];
|
2021-02-17 19:57:15 +00:00
|
|
|
|
2021-02-23 14:53:42 +00:00
|
|
|
constructor(initialAssertions = new Map<Handle, Ref>()) {
|
2021-02-22 09:12:10 +00:00
|
|
|
this.outbound = initialAssertions;
|
2021-02-17 19:57:15 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
// atExit(a: LocalAction): void {
|
|
|
|
// this.exitHooks.push(a);
|
|
|
|
// }
|
|
|
|
|
2021-02-22 18:37:47 +00:00
|
|
|
terminateWith(t: Turn, reason: Exclude<ExitReason, null>) {
|
2021-02-23 10:09:41 +00:00
|
|
|
if (this.exitReason !== null) return;
|
2021-02-22 21:30:36 +00:00
|
|
|
this.exitReason = reason;
|
2021-03-02 08:50:23 +00:00
|
|
|
if (!this.exitReason.ok) {
|
|
|
|
console.error(`Actor ${this.id} crashed:`, this.exitReason.err);
|
|
|
|
}
|
|
|
|
// this.exitHooks.forEach(hook => hook(t));
|
|
|
|
queueMicrotask(() =>
|
|
|
|
t.freshen(t =>
|
|
|
|
this.outbound.forEach((peer, h) => t._retract(peer, h))));
|
2021-02-17 19:57:15 +00:00
|
|
|
}
|
2021-02-22 09:12:10 +00:00
|
|
|
|
|
|
|
execute(proc: () => void): void {
|
|
|
|
queueMicrotask(() => {
|
2021-02-23 10:09:41 +00:00
|
|
|
if (this.exitReason !== null) return;
|
2021-02-22 21:30:36 +00:00
|
|
|
try {
|
|
|
|
proc();
|
|
|
|
} catch (err) {
|
|
|
|
Turn.for(this, t => this.terminateWith(t, { ok: false, err }));
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2021-02-17 19:57:15 +00:00
|
|
|
}
|
|
|
|
|
2021-02-22 09:12:10 +00:00
|
|
|
let nextHandle = 0;
|
2021-03-02 08:50:23 +00:00
|
|
|
function allocateHandle(): Handle {
|
|
|
|
return nextHandle++;
|
|
|
|
}
|
2021-02-17 19:57:15 +00:00
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
export function _sync_impl(turn: Turn, e: Partial<Entity>, peer: Ref): void {
|
2021-02-23 15:16:15 +00:00
|
|
|
e.sync ? e.sync!(turn, peer) : turn.message(peer, true);
|
|
|
|
}
|
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
let nextTurnId = 0;
|
|
|
|
|
2021-02-22 09:12:10 +00:00
|
|
|
export class Turn {
|
2021-03-02 08:50:23 +00:00
|
|
|
readonly id = nextTurnId++;
|
2021-02-23 09:56:12 +00:00
|
|
|
readonly actor: Actor;
|
2021-03-02 08:50:23 +00:00
|
|
|
queues: Map<Actor, LocalAction[]> | null = new Map();
|
2021-02-22 09:12:10 +00:00
|
|
|
|
2021-02-23 10:17:38 +00:00
|
|
|
static for(actor: Actor, f: LocalAction): void {
|
2021-02-22 09:12:10 +00:00
|
|
|
const t = new Turn(actor);
|
|
|
|
f(t);
|
2021-03-02 08:50:23 +00:00
|
|
|
t.queues!.forEach((q, a) => a.execute(() => q.forEach(f => Turn.for(a, f))));
|
|
|
|
t.queues = null;
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 09:56:12 +00:00
|
|
|
private constructor(actor: Actor) {
|
2021-02-22 09:12:10 +00:00
|
|
|
this.actor = actor;
|
2021-02-17 19:57:15 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
ref<T extends Partial<Entity>>(e: T): Ref {
|
2021-02-23 14:53:42 +00:00
|
|
|
return { relay: this.actor, target: e };
|
2021-02-22 18:37:47 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 10:17:38 +00:00
|
|
|
spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
|
2021-02-23 14:40:43 +00:00
|
|
|
this.enqueue(this.actor, () => {
|
2021-02-23 14:53:42 +00:00
|
|
|
const newOutbound = new Map<Handle, Ref>();
|
2021-02-23 10:13:41 +00:00
|
|
|
initialAssertions.forEach(key => {
|
|
|
|
newOutbound.set(key, this.actor.outbound.get(key)!); // we trust initialAssertions
|
|
|
|
this.actor.outbound.delete(key);
|
|
|
|
});
|
|
|
|
const child = new Actor(newOutbound);
|
2021-02-22 09:12:10 +00:00
|
|
|
child.execute(() => Turn.for(child, bootProc));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-22 18:37:47 +00:00
|
|
|
quit(): void {
|
2021-02-23 14:40:43 +00:00
|
|
|
this.enqueue(this.actor, t => this.actor.terminateWith(t, { ok: true }));
|
2021-02-22 18:37:47 +00:00
|
|
|
}
|
|
|
|
|
2021-03-02 08:50:23 +00:00
|
|
|
crash(err: Error): void {
|
|
|
|
this.enqueue(this.actor, t => this.actor.terminateWith(t, { ok: false, err }));
|
|
|
|
}
|
|
|
|
|
2021-02-23 14:38:57 +00:00
|
|
|
assert(ref: Ref, assertion: Assertion): Handle {
|
2021-03-02 08:50:23 +00:00
|
|
|
const h = allocateHandle();
|
2021-02-24 21:21:14 +00:00
|
|
|
const a = runRewrites(ref.attenuation, assertion);
|
|
|
|
if (a !== null) {
|
|
|
|
this.enqueue(ref.relay, t => {
|
2021-02-24 20:48:55 +00:00
|
|
|
this.actor.outbound.set(h, ref);
|
|
|
|
ref.target.assert?.(t, a, h);
|
2021-02-24 21:21:14 +00:00
|
|
|
});
|
|
|
|
}
|
2021-02-22 09:12:10 +00:00
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
retract(h: Handle): void {
|
2021-02-24 20:48:55 +00:00
|
|
|
const peer = this.actor.outbound.get(h);
|
|
|
|
if (peer === void 0) return;
|
|
|
|
this._retract(peer, h);
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 14:38:57 +00:00
|
|
|
replace(ref: Ref, h: Handle | undefined, assertion: Assertion): Handle {
|
|
|
|
const newHandle = this.assert(ref, assertion);
|
2021-02-22 18:37:47 +00:00
|
|
|
if (h !== void 0) this.retract(h);
|
|
|
|
return newHandle;
|
|
|
|
}
|
|
|
|
|
2021-02-23 14:38:57 +00:00
|
|
|
_retract(ref: Ref, handle: Handle): void {
|
|
|
|
this.enqueue(ref.relay, t => {
|
2021-02-23 09:56:12 +00:00
|
|
|
this.actor.outbound.delete(handle);
|
2021-02-23 14:53:42 +00:00
|
|
|
ref.target.retract?.(t, handle);
|
2021-02-22 09:12:10 +00:00
|
|
|
});
|
2021-02-17 19:57:15 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 14:38:57 +00:00
|
|
|
sync(ref: Ref): Promise<Turn> {
|
2021-03-02 08:50:23 +00:00
|
|
|
return new Promise(resolve => this._sync(ref, this.ref({ message: resolve })));
|
|
|
|
}
|
|
|
|
|
|
|
|
_sync(ref: Ref, peer: Ref): void {
|
|
|
|
this.enqueue(ref.relay, t => _sync_impl(t, ref.target, peer));
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 14:38:57 +00:00
|
|
|
message(ref: Ref, assertion: Assertion): void {
|
2021-02-24 21:19:37 +00:00
|
|
|
const a = runRewrites(ref.attenuation, assertion);
|
2021-02-24 20:48:55 +00:00
|
|
|
if (a !== null) this.enqueue(ref.relay, t => ref.target.message?.(t, assertion));
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 14:40:43 +00:00
|
|
|
enqueue(relay: Actor, a: LocalAction): void {
|
2021-03-02 08:50:23 +00:00
|
|
|
if (this.queues === null) {
|
|
|
|
throw new Error("Attempt to reuse a committed Turn");
|
|
|
|
}
|
2021-02-23 14:40:43 +00:00
|
|
|
this.queues.get(relay)?.push(a) ?? this.queues.set(relay, [a]);
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
2021-03-02 08:50:23 +00:00
|
|
|
|
|
|
|
freshen(a: LocalAction): void {
|
|
|
|
if (this.queues !== null) {
|
|
|
|
throw new Error("Attempt to freshen a non-stale Turn");
|
|
|
|
}
|
|
|
|
Turn.for(this.actor, a);
|
|
|
|
}
|
2021-02-22 09:12:10 +00:00
|
|
|
}
|
2021-02-23 15:16:15 +00:00
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
//---------------------------------------------------------------------------
|
2021-02-23 15:16:15 +00:00
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
export function match(p: Pattern, v: Assertion): Bindings | null {
|
2021-02-25 10:43:38 +00:00
|
|
|
let bindings: Bindings = {};
|
2021-02-23 15:16:15 +00:00
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
function walk(p: Pattern, v: Assertion): boolean {
|
2021-02-25 21:24:21 +00:00
|
|
|
switch (p.label) {
|
|
|
|
case _PDiscard:
|
|
|
|
return true;
|
|
|
|
case _PBind:
|
2021-02-25 22:23:51 +00:00
|
|
|
if (walk(PBind._.pattern(p), v)) {
|
2021-02-25 21:24:21 +00:00
|
|
|
bindings[PBind._.name(p)] = v;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
case _PAnd:
|
2021-02-25 22:23:51 +00:00
|
|
|
for (const pp of PAnd._.patterns(p)) {
|
2021-02-25 21:24:21 +00:00
|
|
|
if (!walk(pp, v)) return false;
|
2021-02-24 20:48:55 +00:00
|
|
|
}
|
|
|
|
return true;
|
2021-02-25 21:24:21 +00:00
|
|
|
case _PNot: {
|
|
|
|
const savedBindings = bindings;
|
|
|
|
bindings = {};
|
2021-02-25 22:23:51 +00:00
|
|
|
const result = !walk(PNot._.pattern(p), v)
|
2021-02-25 21:24:21 +00:00
|
|
|
bindings = savedBindings;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
case _Lit:
|
|
|
|
return is(Lit._.value(p), v);
|
|
|
|
case _PCompound: {
|
|
|
|
const ctor = PCompound._.ctor(p);
|
2021-02-25 22:23:51 +00:00
|
|
|
const members = PCompound._.members(p);
|
2021-02-25 21:24:21 +00:00
|
|
|
switch (ctor.label) {
|
|
|
|
case _CRec:
|
2021-03-02 08:50:23 +00:00
|
|
|
if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false;
|
2021-02-25 21:24:21 +00:00
|
|
|
if (!is(CRec._.label(ctor), v.label)) return false;
|
|
|
|
if (CRec._.arity(ctor) !== v.length) return false;
|
|
|
|
for (const [key, pp] of members) {
|
|
|
|
if (typeof key !== 'number') return false;
|
|
|
|
if (!walk(pp, v[key])) return false;
|
2021-02-25 10:43:38 +00:00
|
|
|
}
|
2021-02-25 21:24:21 +00:00
|
|
|
return true;
|
|
|
|
case _CArr:
|
|
|
|
if (!Array.isArray(v)) return false;
|
|
|
|
if ('label' in v) return false;
|
|
|
|
if (CArr._.arity(ctor) !== v.length) return false;
|
|
|
|
for (const [key, pp] of members) {
|
|
|
|
if (typeof key !== 'number') return false;
|
|
|
|
if (!walk(pp, v[key])) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
case _CDict:
|
|
|
|
if (!Dictionary.isDictionary<Assertion, Ref>(v)) return false;
|
|
|
|
for (const [key, pp] of members) {
|
|
|
|
const vv = v.get(key);
|
|
|
|
if (vv === void 0) return false;
|
|
|
|
if (!walk(pp, vv)) return false;
|
|
|
|
}
|
|
|
|
return true;
|
2021-02-24 20:48:55 +00:00
|
|
|
}
|
2021-02-25 21:24:21 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-23 15:16:15 +00:00
|
|
|
}
|
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
return walk(p, v) ? bindings : null;
|
|
|
|
}
|
2021-02-23 15:16:15 +00:00
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
export function instantiate(t: Template, b: Bindings): Assertion {
|
2021-02-25 21:24:21 +00:00
|
|
|
function walk(t: Template): Assertion {
|
|
|
|
switch (t.label) {
|
|
|
|
case _TRef: {
|
|
|
|
const v = b[TRef._.name(t)];
|
|
|
|
if (v === void 0) throw new Error(`Unbound reference: ${TRef._.name(t)}`);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
case _Lit:
|
|
|
|
return Lit._.value(t);
|
|
|
|
case _TCompound: {
|
|
|
|
const ctor = TCompound._.ctor(t);
|
2021-02-25 22:23:51 +00:00
|
|
|
const members = TCompound._.members(t);
|
2021-02-25 21:24:21 +00:00
|
|
|
switch (ctor.label) {
|
|
|
|
case _CRec: {
|
2021-02-25 22:23:51 +00:00
|
|
|
const v = Record<Assertion, any, Ref>(CRec._.label(ctor), []);
|
2021-02-25 21:24:21 +00:00
|
|
|
v.length = CRec._.arity(ctor);
|
|
|
|
for (const [key, tt] of members) {
|
|
|
|
v[key as number] = walk(tt);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
case _CArr: {
|
|
|
|
const v = [];
|
|
|
|
v.length = CArr._.arity(ctor);
|
|
|
|
for (const [key, tt] of members) {
|
|
|
|
v[key as number] = walk(tt);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
case _CDict: {
|
|
|
|
const v = new Dictionary<Assertion, Ref>();
|
|
|
|
for (const [key, tt] of members) {
|
|
|
|
v.set(key, walk(tt));
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return walk(t);
|
2021-02-24 20:48:55 +00:00
|
|
|
}
|
2021-02-23 15:16:15 +00:00
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
export function rewrite(r: Rewrite, v: Assertion): Assertion | null {
|
|
|
|
const bindings = match(r.pattern, v);
|
|
|
|
if (bindings === null) return null;
|
|
|
|
return instantiate(r.template, bindings);
|
|
|
|
}
|
2021-02-23 15:16:15 +00:00
|
|
|
|
2021-02-25 10:41:26 +00:00
|
|
|
export function examineAlternatives(alternatives: RewriteStage, v: Assertion): Assertion | null {
|
|
|
|
for (const r of alternatives) {
|
|
|
|
const w = rewrite(r, v);
|
|
|
|
if (w !== null) return w;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-02-24 21:19:37 +00:00
|
|
|
export function runRewrites(a: Attenuation | undefined, v: Assertion): Assertion | null {
|
|
|
|
if (a !== void 0) {
|
2021-02-25 10:41:26 +00:00
|
|
|
for (const stage of a) {
|
|
|
|
const w = examineAlternatives(stage, v);
|
2021-02-24 21:19:37 +00:00
|
|
|
if (w === null) return null;
|
|
|
|
v = w;
|
|
|
|
}
|
2021-02-23 15:16:15 +00:00
|
|
|
}
|
2021-02-24 20:48:55 +00:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2021-02-25 10:41:26 +00:00
|
|
|
export function rfilter(... patterns: Pattern[]): RewriteStage {
|
2021-02-25 21:24:21 +00:00
|
|
|
return patterns.map(p => ({ pattern: PBind('a', p), template: TRef('a') }));
|
2021-02-23 15:16:15 +00:00
|
|
|
}
|
|
|
|
|
2021-02-24 20:48:55 +00:00
|
|
|
export function attenuate(ref: Ref, ... a: Attenuation): Ref {
|
|
|
|
return { ... ref, attenuation: [... a, ... (ref.attenuation ?? [])] };
|
2021-02-23 15:16:15 +00:00
|
|
|
}
|