import type { Assertion, Handle, Ref, Turn } from "./actor.js"; import { Bytes, Dictionary, DoubleFloat, IdentityMap, is, isPointer, Record, SingleFloat, Tuple } from "@preserves/core"; import { Alts, Attenuation, CArr, CDict, CRec, Caveat, ConstructorSpec, Lit, PAnd, PAtom, PBind, PCompound, PCompoundMembers, PDiscard, PNot, PPointer, Pattern, Rewrite, TRef, Template, _val, } 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._variant) { case 'PDiscard': return true; case 'PAtom': switch (p.value._variant) { case 'Boolean': return typeof v === 'boolean'; case 'ByteString': return Bytes.isBytes(v); case 'Double': return DoubleFloat.isDouble(v); case 'Float': return SingleFloat.isSingle(v); case 'SignedInteger': return typeof v === 'number'; case 'String': return typeof v === 'string'; case 'Symbol': return typeof v === 'symbol'; } case 'PPointer': return isPointer(v); case 'PBind': if (walk(p.value.pattern, v)) { bindings[p.value.name.asPreservesText()] = v; return true; } return false; case 'PAnd': for (const pp of p.value.patterns) { if (!walk(pp, v)) return false; } return true; case 'PNot': { const savedBindings = bindings; bindings = {}; const result = !walk(p.value.pattern, v) bindings = savedBindings; return result; } case 'Lit': return is(p.value.value, v); case 'PCompound': { const ctor = p.value.ctor; const members = p.value.members; switch (ctor._variant) { case 'CRec': if (!Record.isRecord, Ref>(v)) return false; if (!is(ctor.value.label, v.label)) return false; if (ctor.value.arity !== 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 (ctor.value.arity !== 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(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; } } default: ((_p : never) => {})(p); return false; } } return walk(p, v) ? bindings : null; } export function instantiate(t: Template, b: Bindings): Assertion { function walk(t: Template): Assertion { switch (t._variant) { case 'TRef': { const n = t.value.name.asPreservesText() const v = b[n]; if (v === void 0) throw new Error(`Unbound reference: ${n}`); return v; } case 'Lit': return t.value.value as Assertion; case 'TCompound': { const ctor = t.value.ctor; const members = t.value.members; switch (ctor._variant) { case 'CRec': { const v = [] as unknown as Record; v.length = ctor.value.arity; v.label = ctor.value.label; for (const [key, tt] of members) { v[key as number] = walk(tt); } return v; } case 'CArr': { const v = []; v.length = ctor.value.arity; for (const [key, tt] of members) { v[key as number] = walk(tt); } return v; } case 'CDict': { 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(r.pattern, v); if (bindings === null) return null; return instantiate(r.template, bindings); } export function examineAlternatives(cav: Caveat, v: Assertion): Assertion | null { if (cav._variant === 'Alts') { for (const r of cav.value.alternatives) { const w = rewrite(r, v); if (w !== null) return w; } return null; } else { return rewrite(cav.value, 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({ pattern: Pattern.PBind(PBind({ name: _a, pattern: p })), template: Template.TRef(TRef(_a)) })); return ps.length === 1 ? Caveat.Rewrite(ps[0]) : Caveat.Alts(Alts(ps)); } export function attenuate(ref: Ref, ... a: Attenuation): Ref { if (a.length === 0) return 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 }; } export function pRec(label: _val, ... members: Array): Pattern { return Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CRec(CRec({ label: label, arity: members.length })), members: PCompoundMembers(new Dictionary( members.map((p, i) => [i, p] as const).filter(e => e[1]._variant !== 'PDiscard')))})); } export function pArr(... members: Array): Pattern { return Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CArr(CArr(members.length)), members: PCompoundMembers(new Dictionary( members.map((p, i) => [i, p] as const).filter(e => e[1]._variant !== 'PDiscard')))})); } export function pDict(... entries: [_val, Pattern][]): Pattern { return Pattern.PCompound(PCompound({ ctor: ConstructorSpec.CDict(CDict()), members: PCompoundMembers(new Dictionary(entries))})); } export function pLit(value: _val): Pattern { return Pattern.Lit(Lit(value)); } export function pNot(p: Pattern): Pattern { return Pattern.PNot(PNot(p)); } export function pAnd(... ps: Pattern[]): Pattern { return Pattern.PAnd(PAnd(ps)); } export function pBind(name: symbol, pattern: Pattern): Pattern { return Pattern.PBind(PBind({ name, pattern })); } export function pPointer(): Pattern { return Pattern.PPointer(PPointer()); } export function pSymbol(): Pattern { return Pattern.PAtom(PAtom.Symbol()); } export function pByteString(): Pattern { return Pattern.PAtom(PAtom.ByteString()); } export function pString(): Pattern { return Pattern.PAtom(PAtom.String()); } export function pSignedInteger(): Pattern { return Pattern.PAtom(PAtom.SignedInteger()); } export function pDouble(): Pattern { return Pattern.PAtom(PAtom.Double()); } export function pFloat(): Pattern { return Pattern.PAtom(PAtom.Float()); } export function pBoolean(): Pattern { return Pattern.PAtom(PAtom.Boolean()); } export function pDiscard(): Pattern { return Pattern.PDiscard(PDiscard()); }