import type { Assertion, Handle, Ref, Turn } from "./actor.js"; import { Dictionary, IdentityMap, is, Record, Tuple } from "@preserves/core"; 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'; export * from './gen/sturdy.js'; export type Bindings = { [name: string]: Assertion }; export function match(p: Pattern, v: Assertion): Bindings | null { let bindings: Bindings = {}; function walk(p: Pattern, v: Assertion): boolean { switch (p.label) { case $__: return true; case $bind: if (walk(PBind._.pattern(p), v)) { bindings[PBind._.name(p).asPreservesText()] = v; return true; } return false; case $and: for (const pp of PAnd._.patterns(p)) { if (!walk(pp, v)) return false; } return true; case $not: { const savedBindings = bindings; bindings = {}; const result = !walk(PNot._.pattern(p), v) bindings = savedBindings; return result; } case $lit: return is(Lit._.value(p), v); case $compound: { const ctor = PCompound._.ctor(p); const members = PCompound._.members(p); switch (ctor.label) { case $rec: if (!Record.isRecord, 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 $arr: 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 $dict: if (!Dictionary.isDictionary(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 $ref: { const n = TRef._.name(t).asPreservesText() const v = b[n]; if (v === void 0) throw new Error(`Unbound reference: ${n}`); return v; } case $lit: return Lit._.value(t) as Assertion; case $compound: { const ctor = TCompound._.ctor(t); const members = TCompound._.members(t); switch (ctor.label) { case $rec: { const v = Record( CRec._.label(ctor) as Assertion, [] as Assertion[], ); v.length = CRec._.arity(ctor); for (const [key, tt] of members) { v[key as number] = walk(tt); } return v; } case $arr: { const v = []; v.length = CArr._.arity(ctor); for (const [key, tt] of members) { v[key as number] = walk(tt); } return v; } case $dict: { const v = new Dictionary(); 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(Rewrite._.pattern(r), v); if (bindings === null) return null; return instantiate(Rewrite._.template(r), bindings); } export function examineAlternatives(cav: Caveat, v: Assertion): Assertion | null { if (cav.label === $or) { for (const r of Alts._.alternatives(cav)) { const w = rewrite(r, v); if (w !== null) return w; } return null; } else { return rewrite(cav, v); } } 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; } const _a = Symbol.for('a'); 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); } 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(); 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 }; }