2021-03-11 21:57:18 +00:00
|
|
|
import type { Assertion, Handle, Ref, Turn } from "./actor.js";
|
2021-03-11 20:56:35 +00:00
|
|
|
import { Dictionary, IdentityMap, is, Record, Tuple } from "@preserves/core";
|
2021-03-04 18:54:12 +00:00
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
import {
|
|
|
|
$__,
|
|
|
|
$and,
|
|
|
|
$arr,
|
|
|
|
$bind,
|
|
|
|
$compound,
|
|
|
|
$dict,
|
|
|
|
$lit,
|
|
|
|
$not,
|
|
|
|
$or,
|
|
|
|
$rec,
|
|
|
|
$ref,
|
|
|
|
Alts,
|
|
|
|
Attenuation,
|
|
|
|
CArr,
|
|
|
|
CRec,
|
|
|
|
Caveat,
|
|
|
|
Lit,
|
|
|
|
PAnd,
|
|
|
|
PBind,
|
|
|
|
PCompound,
|
|
|
|
PNot,
|
|
|
|
Pattern,
|
|
|
|
Rewrite,
|
|
|
|
TCompound,
|
|
|
|
TRef,
|
|
|
|
Template,
|
|
|
|
} from './gen/sturdy.js';
|
2021-03-11 20:56:35 +00:00
|
|
|
export * from './gen/sturdy.js';
|
2021-03-04 18:54:12 +00:00
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export type Bindings = { [name: string]: Assertion };
|
|
|
|
|
|
|
|
export function match(p: Pattern, v: Assertion): Bindings | null {
|
2021-03-04 18:54:12 +00:00
|
|
|
let bindings: Bindings = {};
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
function walk(p: Pattern, v: Assertion): boolean {
|
2021-03-04 18:54:12 +00:00
|
|
|
switch (p.label) {
|
2021-03-11 21:57:18 +00:00
|
|
|
case $__:
|
2021-03-04 18:54:12 +00:00
|
|
|
return true;
|
2021-03-11 21:57:18 +00:00
|
|
|
case $bind:
|
|
|
|
if (walk(PBind._.pattern(p), v)) {
|
|
|
|
bindings[PBind._.name(p).asPreservesText()] = v;
|
2021-03-04 18:54:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2021-03-11 21:57:18 +00:00
|
|
|
case $and:
|
|
|
|
for (const pp of PAnd._.patterns(p)) {
|
2021-03-04 18:54:12 +00:00
|
|
|
if (!walk(pp, v)) return false;
|
|
|
|
}
|
|
|
|
return true;
|
2021-03-11 21:57:18 +00:00
|
|
|
case $not: {
|
2021-03-04 18:54:12 +00:00
|
|
|
const savedBindings = bindings;
|
|
|
|
bindings = {};
|
2021-03-11 21:57:18 +00:00
|
|
|
const result = !walk(PNot._.pattern(p), v)
|
2021-03-04 18:54:12 +00:00
|
|
|
bindings = savedBindings;
|
|
|
|
return result;
|
|
|
|
}
|
2021-03-11 21:57:18 +00:00
|
|
|
case $lit:
|
|
|
|
return is(Lit._.value(p), v);
|
|
|
|
case $compound: {
|
|
|
|
const ctor = PCompound._.ctor(p);
|
|
|
|
const members = PCompound._.members(p);
|
2021-03-04 18:54:12 +00:00
|
|
|
switch (ctor.label) {
|
2021-03-11 21:57:18 +00:00
|
|
|
case $rec:
|
2021-03-04 18:54:12 +00:00
|
|
|
if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false;
|
2021-03-11 21:57:18 +00:00
|
|
|
if (!is(CRec._.label(ctor), v.label)) return false;
|
|
|
|
if (CRec._.arity(ctor) !== v.length) return false;
|
2021-03-04 18:54:12 +00:00
|
|
|
for (const [key, pp] of members) {
|
|
|
|
if (typeof key !== 'number') return false;
|
|
|
|
if (!walk(pp, v[key])) return false;
|
|
|
|
}
|
|
|
|
return true;
|
2021-03-11 21:57:18 +00:00
|
|
|
case $arr:
|
2021-03-04 18:54:12 +00:00
|
|
|
if (!Array.isArray(v)) return false;
|
|
|
|
if ('label' in v) return false;
|
2021-03-11 21:57:18 +00:00
|
|
|
if (CArr._.arity(ctor) !== v.length) return false;
|
2021-03-04 18:54:12 +00:00
|
|
|
for (const [key, pp] of members) {
|
|
|
|
if (typeof key !== 'number') return false;
|
|
|
|
if (!walk(pp, v[key])) return false;
|
|
|
|
}
|
|
|
|
return true;
|
2021-03-11 21:57:18 +00:00
|
|
|
case $dict:
|
2021-03-04 18:54:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export function instantiate(t: Template, b: Bindings): Assertion {
|
|
|
|
function walk(t: Template): Assertion {
|
2021-03-04 18:54:12 +00:00
|
|
|
switch (t.label) {
|
2021-03-11 21:57:18 +00:00
|
|
|
case $ref: {
|
|
|
|
const n = TRef._.name(t).asPreservesText()
|
2021-03-05 20:37:58 +00:00
|
|
|
const v = b[n];
|
|
|
|
if (v === void 0) throw new Error(`Unbound reference: ${n}`);
|
2021-03-04 18:54:12 +00:00
|
|
|
return v;
|
|
|
|
}
|
2021-03-11 21:57:18 +00:00
|
|
|
case $lit:
|
|
|
|
return Lit._.value(t) as Assertion;
|
|
|
|
case $compound: {
|
|
|
|
const ctor = TCompound._.ctor(t);
|
|
|
|
const members = TCompound._.members(t);
|
2021-03-04 18:54:12 +00:00
|
|
|
switch (ctor.label) {
|
2021-03-11 21:57:18 +00:00
|
|
|
case $rec: {
|
2021-03-10 22:49:34 +00:00
|
|
|
const v = Record(
|
2021-03-11 21:57:18 +00:00
|
|
|
CRec._.label(ctor) as Assertion,
|
2021-03-10 22:49:34 +00:00
|
|
|
[] as Assertion[],
|
2021-03-04 18:54:12 +00:00
|
|
|
);
|
2021-03-11 21:57:18 +00:00
|
|
|
v.length = CRec._.arity(ctor);
|
2021-03-04 18:54:12 +00:00
|
|
|
for (const [key, tt] of members) {
|
|
|
|
v[key as number] = walk(tt);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
2021-03-11 21:57:18 +00:00
|
|
|
case $arr: {
|
2021-03-04 18:54:12 +00:00
|
|
|
const v = [];
|
2021-03-11 21:57:18 +00:00
|
|
|
v.length = CArr._.arity(ctor);
|
2021-03-04 18:54:12 +00:00
|
|
|
for (const [key, tt] of members) {
|
|
|
|
v[key as number] = walk(tt);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
2021-03-11 21:57:18 +00:00
|
|
|
case $dict: {
|
2021-03-04 18:54:12 +00:00
|
|
|
const v = new Dictionary<Assertion, Ref>();
|
|
|
|
for (const [key, tt] of members) {
|
|
|
|
v.set(key as Assertion, walk(tt));
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return walk(t);
|
|
|
|
}
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export function rewrite(r: Rewrite, v: Assertion): Assertion | null {
|
|
|
|
const bindings = match(Rewrite._.pattern(r), v);
|
2021-03-04 18:54:12 +00:00
|
|
|
if (bindings === null) return null;
|
2021-03-11 21:57:18 +00:00
|
|
|
return instantiate(Rewrite._.template(r), bindings);
|
2021-03-04 18:54:12 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export function examineAlternatives(cav: Caveat, v: Assertion): Assertion | null {
|
|
|
|
if (cav.label === $or) {
|
|
|
|
for (const r of Alts._.alternatives(cav)) {
|
2021-03-11 20:56:35 +00:00
|
|
|
const w = rewrite(r, v);
|
|
|
|
if (w !== null) return w;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return rewrite(cav, v);
|
2021-03-04 18:54:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export function runRewrites(a: Attenuation | undefined, v: Assertion): Assertion | null {
|
2021-03-04 18:54:12 +00:00
|
|
|
if (a !== void 0) {
|
|
|
|
for (const stage of a) {
|
|
|
|
const w = examineAlternatives(stage, v);
|
|
|
|
if (w === null) return null;
|
|
|
|
v = w;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2021-03-05 20:37:58 +00:00
|
|
|
const _a = Symbol.for('a');
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export function rfilter(... patterns: Pattern[]): Caveat {
|
|
|
|
const ps = patterns.map(p => Rewrite(PBind(_a, p), TRef(_a)));
|
|
|
|
return ps.length === 1 ? ps[0] : Alts(ps);
|
2021-03-04 18:54:12 +00:00
|
|
|
}
|
|
|
|
|
2021-03-11 21:57:18 +00:00
|
|
|
export function attenuate(ref: Ref, ... a: Attenuation): Ref {
|
2021-03-04 18:54:12 +00:00
|
|
|
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 };
|
|
|
|
}
|