Split out rewrite.ts
This commit is contained in:
parent
b72fb6c309
commit
e23b93e913
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^14.14.31",
|
"@types/node": "^14.14.31",
|
||||||
|
"rollup": "^2.40.0",
|
||||||
"typescript": "^4.1.5"
|
"typescript": "^4.1.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -9,6 +10,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"compile": "npx tsc",
|
"compile": "npx tsc",
|
||||||
"compile:watch": "npx tsc -w",
|
"compile:watch": "npx tsc -w",
|
||||||
"clean": "rm -rf lib"
|
"rollup": "npx rollup -c",
|
||||||
|
"rollup:watch": "npx rollup -c -w",
|
||||||
|
"clean": "rm -rf lib index.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
253
src/actor.ts
253
src/actor.ts
|
@ -1,4 +1,5 @@
|
||||||
import { Dictionary, IdentitySet, Record, Tuple, Value, is, IdentityMap } from 'preserves';
|
import { Dictionary, IdentitySet, Record, Tuple, Value, is, IdentityMap } from 'preserves';
|
||||||
|
import { Attenuation, runRewrites } from './rewrite.js';
|
||||||
import { queueTask } from './task.js';
|
import { queueTask } from './task.js';
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -25,74 +26,6 @@ export interface Ref {
|
||||||
readonly attenuation?: Attenuation;
|
readonly attenuation?: Attenuation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Attenuation = Array<RewriteStage>; // array of stages, each a list of alternatives
|
|
||||||
export type RewriteStage = Array<Rewrite>;
|
|
||||||
export type Rewrite = { pattern: Pattern, template: Template };
|
|
||||||
|
|
||||||
export const _CRec = Symbol.for('rec');
|
|
||||||
export const CRec = Record.makeConstructor<{label: Value<never>, arity: number}, never>()(
|
|
||||||
_CRec, ['label', 'arity']);
|
|
||||||
|
|
||||||
export const _CArr = Symbol.for('arr');
|
|
||||||
export const CArr = Record.makeConstructor<{arity: number}, never>()(
|
|
||||||
_CArr, ['arity']);
|
|
||||||
|
|
||||||
export const _CDict = Symbol.for('dict');
|
|
||||||
export const CDict = Record.makeConstructor<{}, never>()(
|
|
||||||
_CDict, []);
|
|
||||||
|
|
||||||
export type ConstructorSpec =
|
|
||||||
| Record<typeof _CRec, [Value<never>, number], never>
|
|
||||||
| Record<typeof _CArr, [number], never>
|
|
||||||
| Record<typeof _CDict, [], never>;
|
|
||||||
|
|
||||||
export const _PDiscard = Symbol.for('_');
|
|
||||||
export const PDiscard = Record.makeConstructor<{}, never>()(
|
|
||||||
_PDiscard, []);
|
|
||||||
|
|
||||||
export const _PBind = Symbol.for('bind');
|
|
||||||
export const PBind = Record.makeConstructor<{name: string, pattern: Pattern}, never>()(
|
|
||||||
_PBind, ['name', 'pattern']);
|
|
||||||
|
|
||||||
export const _PAnd = Symbol.for('and');
|
|
||||||
export const PAnd = Record.makeConstructor<{patterns: Array<Pattern>}, never>()(
|
|
||||||
_PAnd, ['patterns']);
|
|
||||||
|
|
||||||
export const _PNot = Symbol.for('not');
|
|
||||||
export const PNot = Record.makeConstructor<{pattern: Pattern}, never>()(
|
|
||||||
_PNot, ['pattern']);
|
|
||||||
|
|
||||||
export const _Lit = Symbol.for('lit');
|
|
||||||
export const Lit = Record.makeConstructor<{value: Value<never>}, never>()(
|
|
||||||
_Lit, ['value']);
|
|
||||||
|
|
||||||
export const _PCompound = Symbol.for('compound');
|
|
||||||
export const PCompound =
|
|
||||||
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Pattern, never>}, never>()(
|
|
||||||
_PCompound, ['ctor', 'members']);
|
|
||||||
|
|
||||||
export type Pattern =
|
|
||||||
| Record<typeof _PDiscard, [], never>
|
|
||||||
| Record<typeof _PBind, [string, Pattern], never>
|
|
||||||
| Record<typeof _PAnd, [Pattern[]], never>
|
|
||||||
| Record<typeof _PNot, [Pattern], never>
|
|
||||||
| Record<typeof _Lit, [Value<never>], never>
|
|
||||||
| Record<typeof _PCompound, [ConstructorSpec, Dictionary<Pattern, never>], never>;
|
|
||||||
|
|
||||||
export const _TRef = Symbol.for('ref');
|
|
||||||
export const TRef = Record.makeConstructor<{name: string}, never>()(
|
|
||||||
_TRef, ['name']);
|
|
||||||
|
|
||||||
export const _TCompound = Symbol.for('compound');
|
|
||||||
export const TCompound =
|
|
||||||
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Template, never>}, never>()(
|
|
||||||
_TCompound, ['ctor', 'members']);
|
|
||||||
|
|
||||||
export type Template =
|
|
||||||
| Record<typeof _TRef, [string], never>
|
|
||||||
| Record<typeof _Lit, [Value<never>], never>
|
|
||||||
| Record<typeof _TCompound, [ConstructorSpec, Dictionary<Template, never>], never>;
|
|
||||||
|
|
||||||
export type Bindings = { [name: string]: Assertion };
|
export type Bindings = { [name: string]: Assertion };
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
@ -133,9 +66,6 @@ export class Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
let nextHandle = 0;
|
let nextHandle = 0;
|
||||||
function allocateHandle(): Handle {
|
|
||||||
return nextHandle++;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function _sync_impl(turn: Turn, e: Partial<Entity>, peer: Ref): void {
|
export function _sync_impl(turn: Turn, e: Partial<Entity>, peer: Ref): void {
|
||||||
e.sync ? e.sync!(turn, peer) : turn.message(peer, true);
|
e.sync ? e.sync!(turn, peer) : turn.message(peer, true);
|
||||||
|
@ -188,7 +118,7 @@ export class Turn {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ref: Ref, assertion: Assertion): Handle {
|
assert(ref: Ref, assertion: Assertion): Handle {
|
||||||
const h = allocateHandle();
|
const h = nextHandle++;
|
||||||
this._assert(ref, assertion, h);
|
this._assert(ref, assertion, h);
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
@ -251,182 +181,3 @@ export class Turn {
|
||||||
Turn.for(this.actor, a);
|
Turn.for(this.actor, a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
export function match(p: Pattern, v: Assertion): Bindings | null {
|
|
||||||
let bindings: Bindings = {};
|
|
||||||
|
|
||||||
function walk(p: Pattern, v: Assertion): boolean {
|
|
||||||
switch (p.label) {
|
|
||||||
case _PDiscard:
|
|
||||||
return true;
|
|
||||||
case _PBind:
|
|
||||||
if (walk(PBind._.pattern(p), v)) {
|
|
||||||
bindings[PBind._.name(p)] = v;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case _PAnd:
|
|
||||||
for (const pp of PAnd._.patterns(p)) {
|
|
||||||
if (!walk(pp, v)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case _PNot: {
|
|
||||||
const savedBindings = bindings;
|
|
||||||
bindings = {};
|
|
||||||
const result = !walk(PNot._.pattern(p), v)
|
|
||||||
bindings = savedBindings;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
case _Lit:
|
|
||||||
return is(Lit._.value(p), v);
|
|
||||||
case _PCompound: {
|
|
||||||
const ctor = PCompound._.ctor(p);
|
|
||||||
const members = PCompound._.members(p);
|
|
||||||
switch (ctor.label) {
|
|
||||||
case _CRec:
|
|
||||||
if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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 as Assertion);
|
|
||||||
if (vv === void 0) return false;
|
|
||||||
if (!walk(pp, vv)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return walk(p, v) ? bindings : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function instantiate(t: Template, b: Bindings): Assertion {
|
|
||||||
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) as Assertion;
|
|
||||||
case _TCompound: {
|
|
||||||
const ctor = TCompound._.ctor(t);
|
|
||||||
const members = TCompound._.members(t);
|
|
||||||
switch (ctor.label) {
|
|
||||||
case _CRec: {
|
|
||||||
const v = Record<Assertion, any, Ref>(
|
|
||||||
CRec._.label(ctor) as Assertion,
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
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 as Assertion, walk(tt));
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return walk(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function runRewrites(a: Attenuation | undefined, v: Assertion): Assertion | null {
|
|
||||||
if (a !== void 0) {
|
|
||||||
for (const stage of a) {
|
|
||||||
const w = examineAlternatives(stage, v);
|
|
||||||
if (w === null) return null;
|
|
||||||
v = w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function rfilter(... patterns: Pattern[]): RewriteStage {
|
|
||||||
return patterns.map(p => ({ pattern: PBind('a', p), template: TRef('a') }));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function attenuate(ref: Ref, ... a: Attenuation): Ref {
|
|
||||||
return { ... ref, attenuation: [... a, ... (ref.attenuation ?? [])] };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function forwarder(t: Turn, ref: Ref): { proxy: Ref, revoker: Ref } {
|
|
||||||
let underlying: Ref | null = ref;
|
|
||||||
let handleMap = new IdentityMap<Handle, Handle>();
|
|
||||||
let proxy = t.ref({
|
|
||||||
assert(turn: Turn, assertion: Assertion, handle: Handle): void {
|
|
||||||
if (underlying === null) return;
|
|
||||||
handleMap.set(handle, turn.assert(underlying, assertion));
|
|
||||||
},
|
|
||||||
retract(turn: Turn, handle: Handle): void {
|
|
||||||
if (underlying === null) return;
|
|
||||||
turn.retract(handleMap.get(handle));
|
|
||||||
handleMap.delete(handle);
|
|
||||||
},
|
|
||||||
message(turn: Turn, body: Assertion): void {
|
|
||||||
if (underlying === null) return;
|
|
||||||
turn.message(underlying, body);
|
|
||||||
},
|
|
||||||
sync(turn: Turn, peer: Ref): void {
|
|
||||||
if (underlying === null) return;
|
|
||||||
turn._sync(underlying, peer);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
let revoker = t.ref({
|
|
||||||
message(turn: Turn, _body: Assertion): void {
|
|
||||||
underlying = null;
|
|
||||||
handleMap.forEach(h => turn.retract(h));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { proxy, revoker };
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { Actor, Assertion, attenuate, CRec, Lit, Pattern, PCompound, rfilter, Turn } from './actor.js';
|
import { Actor, Assertion, Turn } from './actor.js';
|
||||||
import { Dictionary, Record } from 'preserves';
|
import { Dictionary, Record } from 'preserves';
|
||||||
import { Dataspace, Observe } from './dataspace.js';
|
import { Dataspace, Observe } from './dataspace.js';
|
||||||
import { Worker } from 'worker_threads';
|
import { Worker } from 'worker_threads';
|
||||||
import { Relay, spawnRelay } from './relay.js';
|
import { Relay, spawnRelay } from './relay.js';
|
||||||
import { BoxState, SetBox } from './box-protocol.js';
|
import { BoxState, SetBox } from './box-protocol.js';
|
||||||
|
import { attenuate, CRec, Lit, Pattern, PCompound, rfilter } from './rewrite.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const Instance = Record.makeConstructor<{moduleName: string, arg: Assertion}>()(
|
const Instance = Record.makeConstructor<{moduleName: string, arg: Assertion}>()(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Attenuation, Handle, Pattern, Ref, Template } from './actor.js';
|
import { Handle, Ref } from './actor.js';
|
||||||
|
import { Attenuation, Pattern, Template } from './rewrite.js';
|
||||||
import { Record, Value } from 'preserves';
|
import { Record, Value } from 'preserves';
|
||||||
|
|
||||||
export const _Assert = Symbol.for('assert');
|
export const _Assert = Symbol.for('assert');
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Actor, Assertion, attenuate, Entity, Handle, Ref, Turn } from './actor.js';
|
import { Actor, Assertion, Entity, Handle, Ref, Turn } from './actor.js';
|
||||||
import { BytesLike, canonicalString, Decoder, encode, FlexMap, IdentityMap, mapPointers, underlying, Value } from 'preserves';
|
import { BytesLike, canonicalString, Decoder, encode, FlexMap, IdentityMap, mapPointers, underlying, Value } from 'preserves';
|
||||||
import {
|
import {
|
||||||
EncodedAttenuation,
|
EncodedAttenuation,
|
||||||
|
@ -18,6 +18,7 @@ import {
|
||||||
yourRef,
|
yourRef,
|
||||||
} from './protocol.js';
|
} from './protocol.js';
|
||||||
import { queueTask } from './task.js';
|
import { queueTask } from './task.js';
|
||||||
|
import { attenuate } from './rewrite.js';
|
||||||
|
|
||||||
export class SyncPeerEntity implements Entity {
|
export class SyncPeerEntity implements Entity {
|
||||||
readonly relay: Relay;
|
readonly relay: Relay;
|
||||||
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
import type { Assertion, Bindings, Handle, Ref, Turn } from "./actor.js";
|
||||||
|
import { Dictionary, IdentityMap, is, Record, Tuple, Value } from "preserves";
|
||||||
|
|
||||||
|
export type Attenuation = Array<RewriteStage>; // array of stages, each a list of alternatives
|
||||||
|
export type RewriteStage = Array<Rewrite>;
|
||||||
|
export type Rewrite = { pattern: Pattern, template: Template };
|
||||||
|
|
||||||
|
export const _CRec = Symbol.for('rec');
|
||||||
|
export const CRec = Record.makeConstructor<{label: Value<never>, arity: number}, never>()(
|
||||||
|
_CRec, ['label', 'arity']);
|
||||||
|
|
||||||
|
export const _CArr = Symbol.for('arr');
|
||||||
|
export const CArr = Record.makeConstructor<{arity: number}, never>()(
|
||||||
|
_CArr, ['arity']);
|
||||||
|
|
||||||
|
export const _CDict = Symbol.for('dict');
|
||||||
|
export const CDict = Record.makeConstructor<{}, never>()(
|
||||||
|
_CDict, []);
|
||||||
|
|
||||||
|
export type ConstructorSpec =
|
||||||
|
| Record<typeof _CRec, [Value<never>, number], never>
|
||||||
|
| Record<typeof _CArr, [number], never>
|
||||||
|
| Record<typeof _CDict, [], never>;
|
||||||
|
|
||||||
|
export const _PDiscard = Symbol.for('_');
|
||||||
|
export const PDiscard = Record.makeConstructor<{}, never>()(
|
||||||
|
_PDiscard, []);
|
||||||
|
|
||||||
|
export const _PBind = Symbol.for('bind');
|
||||||
|
export const PBind = Record.makeConstructor<{name: string, pattern: Pattern}, never>()(
|
||||||
|
_PBind, ['name', 'pattern']);
|
||||||
|
|
||||||
|
export const _PAnd = Symbol.for('and');
|
||||||
|
export const PAnd = Record.makeConstructor<{patterns: Array<Pattern>}, never>()(
|
||||||
|
_PAnd, ['patterns']);
|
||||||
|
|
||||||
|
export const _PNot = Symbol.for('not');
|
||||||
|
export const PNot = Record.makeConstructor<{pattern: Pattern}, never>()(
|
||||||
|
_PNot, ['pattern']);
|
||||||
|
|
||||||
|
export const _Lit = Symbol.for('lit');
|
||||||
|
export const Lit = Record.makeConstructor<{value: Value<never>}, never>()(
|
||||||
|
_Lit, ['value']);
|
||||||
|
|
||||||
|
export const _PCompound = Symbol.for('compound');
|
||||||
|
export const PCompound =
|
||||||
|
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Pattern, never>}, never>()(
|
||||||
|
_PCompound, ['ctor', 'members']);
|
||||||
|
|
||||||
|
export type Pattern =
|
||||||
|
| Record<typeof _PDiscard, [], never>
|
||||||
|
| Record<typeof _PBind, [string, Pattern], never>
|
||||||
|
| Record<typeof _PAnd, [Pattern[]], never>
|
||||||
|
| Record<typeof _PNot, [Pattern], never>
|
||||||
|
| Record<typeof _Lit, [Value<never>], never>
|
||||||
|
| Record<typeof _PCompound, [ConstructorSpec, Dictionary<Pattern, never>], never>;
|
||||||
|
|
||||||
|
export const _TRef = Symbol.for('ref');
|
||||||
|
export const TRef = Record.makeConstructor<{name: string}, never>()(
|
||||||
|
_TRef, ['name']);
|
||||||
|
|
||||||
|
export const _TCompound = Symbol.for('compound');
|
||||||
|
export const TCompound =
|
||||||
|
Record.makeConstructor<{ctor: ConstructorSpec, members: Dictionary<Template, never>}, never>()(
|
||||||
|
_TCompound, ['ctor', 'members']);
|
||||||
|
|
||||||
|
export type Template =
|
||||||
|
| Record<typeof _TRef, [string], never>
|
||||||
|
| Record<typeof _Lit, [Value<never>], never>
|
||||||
|
| Record<typeof _TCompound, [ConstructorSpec, Dictionary<Template, never>], never>;
|
||||||
|
|
||||||
|
export function match(p: Pattern, v: Assertion): Bindings | null {
|
||||||
|
let bindings: Bindings = {};
|
||||||
|
|
||||||
|
function walk(p: Pattern, v: Assertion): boolean {
|
||||||
|
switch (p.label) {
|
||||||
|
case _PDiscard:
|
||||||
|
return true;
|
||||||
|
case _PBind:
|
||||||
|
if (walk(PBind._.pattern(p), v)) {
|
||||||
|
bindings[PBind._.name(p)] = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
case _PAnd:
|
||||||
|
for (const pp of PAnd._.patterns(p)) {
|
||||||
|
if (!walk(pp, v)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case _PNot: {
|
||||||
|
const savedBindings = bindings;
|
||||||
|
bindings = {};
|
||||||
|
const result = !walk(PNot._.pattern(p), v)
|
||||||
|
bindings = savedBindings;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
case _Lit:
|
||||||
|
return is(Lit._.value(p), v);
|
||||||
|
case _PCompound: {
|
||||||
|
const ctor = PCompound._.ctor(p);
|
||||||
|
const members = PCompound._.members(p);
|
||||||
|
switch (ctor.label) {
|
||||||
|
case _CRec:
|
||||||
|
if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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 as Assertion);
|
||||||
|
if (vv === void 0) return false;
|
||||||
|
if (!walk(pp, vv)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return walk(p, v) ? bindings : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function instantiate(t: Template, b: Bindings): Assertion {
|
||||||
|
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) as Assertion;
|
||||||
|
case _TCompound: {
|
||||||
|
const ctor = TCompound._.ctor(t);
|
||||||
|
const members = TCompound._.members(t);
|
||||||
|
switch (ctor.label) {
|
||||||
|
case _CRec: {
|
||||||
|
const v = Record<Assertion, any, Ref>(
|
||||||
|
CRec._.label(ctor) as Assertion,
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
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 as Assertion, walk(tt));
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return walk(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runRewrites(a: Attenuation | undefined, v: Assertion): Assertion | null {
|
||||||
|
if (a !== void 0) {
|
||||||
|
for (const stage of a) {
|
||||||
|
const w = examineAlternatives(stage, v);
|
||||||
|
if (w === null) return null;
|
||||||
|
v = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rfilter(... patterns: Pattern[]): RewriteStage {
|
||||||
|
return patterns.map(p => ({ pattern: PBind('a', p), template: TRef('a') }));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function attenuate(ref: Ref, ... a: Attenuation): Ref {
|
||||||
|
return { ... ref, attenuation: [... a, ... (ref.attenuation ?? [])] };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function forwarder(t: Turn, ref: Ref): { proxy: Ref, revoker: Ref } {
|
||||||
|
let underlying: Ref | null = ref;
|
||||||
|
let handleMap = new IdentityMap<Handle, Handle>();
|
||||||
|
let proxy = t.ref({
|
||||||
|
assert(turn: Turn, assertion: Assertion, handle: Handle): void {
|
||||||
|
if (underlying === null) return;
|
||||||
|
handleMap.set(handle, turn.assert(underlying, assertion));
|
||||||
|
},
|
||||||
|
retract(turn: Turn, handle: Handle): void {
|
||||||
|
if (underlying === null) return;
|
||||||
|
turn.retract(handleMap.get(handle));
|
||||||
|
handleMap.delete(handle);
|
||||||
|
},
|
||||||
|
message(turn: Turn, body: Assertion): void {
|
||||||
|
if (underlying === null) return;
|
||||||
|
turn.message(underlying, body);
|
||||||
|
},
|
||||||
|
sync(turn: Turn, peer: Ref): void {
|
||||||
|
if (underlying === null) return;
|
||||||
|
turn._sync(underlying, peer);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let revoker = t.ref({
|
||||||
|
message(turn: Turn, _body: Assertion): void {
|
||||||
|
underlying = null;
|
||||||
|
handleMap.forEach(h => turn.retract(h));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { proxy, revoker };
|
||||||
|
}
|
|
@ -9,7 +9,7 @@
|
||||||
"declarationDir": "./lib",
|
"declarationDir": "./lib",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"module": "commonjs",
|
"module": "esnext",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true
|
"strict": true
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue