Adapt to syndicate-protocols changes

This commit is contained in:
Tony Garnock-Jones 2021-12-13 20:20:31 +01:00
parent d737b96465
commit 90a6e2f41a
6 changed files with 178 additions and 138 deletions

View File

@ -1,7 +1,7 @@
/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { canonicalString, is, KeyedDictionary, Record, RecordConstructorInfo, Value } from '@preserves/core'; import { canonicalString, Dictionary, is, Record, RecordConstructorInfo, Value } from '@preserves/core';
import { AnyValue, Ref } from './actor.js'; import { AnyValue, Ref } from './actor.js';
import * as P from '../gen/dataspacePatterns.js'; import * as P from '../gen/dataspacePatterns.js';
@ -24,9 +24,9 @@ export function classOfValue(v: any): Shape | null {
export function classOfCtor(v: P.DCompound): Shape { export function classOfCtor(v: P.DCompound): Shape {
switch (v._variant) { switch (v._variant) {
case 'rec': case 'rec':
return canonicalString(v.ctor.label) + '/' + v.ctor.arity; return canonicalString(v.label) + '/' + v.fields.length;
case 'arr': case 'arr':
return '' + v.ctor.arity; return '' + v.items.length;
case 'dict': case 'dict':
return '{}'; return '{}';
} }
@ -59,14 +59,20 @@ export function analysePattern(p: P.Pattern): PatternAnalysis {
}; };
const path: Path = []; const path: Path = [];
function walkKey(p: P.Pattern, key: AnyValue) {
path.push(key);
walk(p);
path.pop();
}
function walk(p: P.Pattern) { function walk(p: P.Pattern) {
switch (p._variant) { switch (p._variant) {
case 'DCompound': case 'DCompound':
p.value.members.forEach((v, k) => { switch (p.value._variant) {
path.push(k); case 'rec': p.value.fields.forEach(walkKey); break;
walk(v); case 'arr': p.value.items.forEach(walkKey); break;
path.pop(); case 'dict': p.value.entries.forEach(walkKey); break;
}); }
break; break;
case 'DBind': case 'DBind':
result.capturePaths.push(path.slice()); result.capturePaths.push(path.slice());
@ -76,7 +82,7 @@ export function analysePattern(p: P.Pattern): PatternAnalysis {
break; break;
case 'DLit': case 'DLit':
result.constPaths.push(path.slice()); result.constPaths.push(path.slice());
result.constValues.push(p.value.value); result.constValues.push(P.fromAnyAtom(p.value.value));
break; break;
} }
} }
@ -102,10 +108,21 @@ export function match(p: P.Pattern, v: AnyValue): Array<AnyValue> | false {
const pcls = classOfCtor(p.value); const pcls = classOfCtor(p.value);
const vcls = classOfValue(v); const vcls = classOfValue(v);
if (pcls !== vcls) return false; if (pcls !== vcls) return false;
for (const [stepIndex, pp] of p.value.members.entries()) { let items: Array<P.Pattern>;
const vv = step(v, stepIndex); switch (p.value._variant) {
if (vv === void 0) return false; case 'dict':
if (!walk(pp, vv)) return false; for (const [stepIndex, pp] of p.value.entries.entries()) {
const vv = step(v, stepIndex);
if (vv === void 0 || !walk(pp, vv)) return false;
}
return true;
case 'rec': items = p.value.fields; break;
case 'arr': items = p.value.items; break;
}
let index = 0;
for (const pp of items) {
const vv = step(v, index++);
if (vv === void 0 || !walk(pp, vv)) return false;
} }
return true; return true;
} }
@ -121,11 +138,16 @@ export function isCompletelyConcrete(p: P.Pattern): boolean {
case 'DBind': return false; case 'DBind': return false;
case 'DDiscard': return false; case 'DDiscard': return false;
case 'DLit': return true; case 'DLit': return true;
case 'DCompound': case 'DCompound': switch (p.value._variant) {
for (const pp of p.value.members.values()) { case 'rec': return p.value.fields.every(isCompletelyConcrete);
if (!walk(pp)) return false; case 'arr': return p.value.items.every(isCompletelyConcrete);
case 'dict': {
for (const pp of p.value.entries.values()) {
if (!walk(pp)) return false;
}
return true;
} }
return true; }
} }
} }
return walk(p); return walk(p);
@ -137,11 +159,19 @@ export function withoutCaptures(p: P.Pattern): P.Pattern {
case 'DBind': return walk(p.value.pattern); case 'DBind': return walk(p.value.pattern);
case 'DDiscard': return p; case 'DDiscard': return p;
case 'DLit': return p; case 'DLit': return p;
case 'DCompound': return P.Pattern.DCompound({ case 'DCompound':
_variant: p.value._variant, switch (p.value._variant) {
ctor: p.value.ctor, case 'rec':
members: p.value.members.mapEntries(e => [e[0], walk(e[1])]), return P.Pattern.DCompound(P.DCompound.rec({
} as P.DCompound); label: p.value.label,
fields: p.value.fields.map(walk),
}));
case 'arr':
return P.Pattern.DCompound(P.DCompound.arr(p.value.items.map(walk)));
case 'dict':
return P.Pattern.DCompound(P.DCompound.dict(p.value.entries.mapEntries(
e => [e[0], walk(e[1])])));
}
} }
} }
return walk(p); return walk(p);
@ -161,38 +191,67 @@ export function discard(): P.Pattern {
export const _ = discard(); export const _ = discard();
export function lit(v: AnyValue): P.Pattern { export function lit(v: AnyValue): P.Pattern {
return P.Pattern.DLit(P.DLit(v)); if (Array.isArray(v)) {
if ('label' in v) {
return P.Pattern.DCompound(P.DCompound.rec({
label: v.label,
fields: v.map(lit),
}));
} else {
return P.Pattern.DCompound(P.DCompound.arr(v.map(lit)));
}
} else if (Map.isMap(v)) {
return P.Pattern.DCompound(P.DCompound.dict(v.mapEntries(
e => [e[0], lit(e[1])])));
} else if (Set.isSet(v)) {
throw new Error("Cannot express literal set in pattern");
} else {
return P.Pattern.DLit(P.DLit(P.asAnyAtom(v)));
}
} }
function indexedMembers(items: P.Pattern[]): KeyedDictionary<number, P.Pattern, Ref> { export function drop_lit(p: P.Pattern): AnyValue | null {
const members = new KeyedDictionary<number, P.Pattern, Ref>(); const e = new Error();
items.forEach((p, i) => { function walk(p: P.Pattern): AnyValue {
if (!is(p, _)) members.set(i, p); switch (p._variant) {
}); case 'DCompound':
return members; switch (p.value._variant) {
case 'rec': {
const v = [] as unknown as Record<AnyValue, AnyValue[], Ref>;
v.label = p.value.label;
p.value.fields.forEach(tt => v.push(walk(tt)));
return v;
}
case 'arr':
return p.value.items.map(walk);
case 'dict': {
const v = new Dictionary<Ref, AnyValue>();
p.value.entries.forEach((pp, key) => v.set(key, walk(pp)));
return v;
}
}
case 'DLit':
return P.fromAnyAtom(p.value.value);
default:
throw e;
}
}
try {
return walk(p);
} catch (ee) {
if (ee == e) return null;
throw ee;
}
} }
export function rec(label: AnyValue, ... fields: P.Pattern[]): P.Pattern { export function rec(label: AnyValue, ... fields: P.Pattern[]): P.Pattern {
return P.Pattern.DCompound(P.DCompound.rec({ return P.Pattern.DCompound(P.DCompound.rec({ label, fields }));
ctor: P.CRec({
label,
arity: fields.length,
}),
members: indexedMembers(fields),
}));
} }
export function arr(... patterns: P.Pattern[]): P.Pattern { export function arr(... patterns: P.Pattern[]): P.Pattern {
return P.Pattern.DCompound(P.DCompound.arr({ return P.Pattern.DCompound(P.DCompound.arr(patterns));
ctor: P.CArr(patterns.length),
members: indexedMembers(patterns),
}));
} }
export function dict(... entries: [AnyValue, P.Pattern][]): P.Pattern { export function dict(... entries: [AnyValue, P.Pattern][]): P.Pattern {
const members = new KeyedDictionary<AnyValue, P.Pattern, Ref>(); return P.Pattern.DCompound(P.DCompound.dict(new Dictionary<Ref, P.Pattern>(entries)));
entries.forEach(([k, p]) => {
if (!is(p, _)) members.set(k, p);
});
return P.Pattern.DCompound(P.DCompound.dict({ ctor: P.CDict(), members }));
} }

View File

@ -2,7 +2,7 @@
/// SPDX-FileCopyrightText: Copyright © 2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { AnyValue, Ref } from './actor.js'; import { AnyValue, Ref } from './actor.js';
import { Pattern } from '../gen/dataspacePatterns.js'; import { Pattern, toPattern } from '../gen/dataspacePatterns.js';
import * as P from './pattern.js'; import * as P from './pattern.js';
import { Value, RecordConstructorInfo, is, Record } from '@preserves/core'; import { Value, RecordConstructorInfo, is, Record } from '@preserves/core';
import { Meta, Type, GenType } from '@preserves/schema'; import { Meta, Type, GenType } from '@preserves/schema';
@ -49,6 +49,16 @@ export function lit(value: AnyValue): QuasiValue {
lit.quasiValue = (q: QuasiValue) => rec(Symbol.for('lit'), q); lit.quasiValue = (q: QuasiValue) => rec(Symbol.for('lit'), q);
export function drop_lit(patValue: AnyValue): AnyValue | null;
export function drop_lit<R>(patValue: AnyValue, parser: (v: AnyValue) => R): R | null;
export function drop_lit<R>(patValue: AnyValue, parser?: (v: AnyValue) => R): any {
const pat = toPattern(patValue);
if (pat === void 0) return null;
const val = P.drop_lit(pat);
if (val === null) return null;
return parser === void 0 ? val : parser(val);
}
export function rec(label: AnyValue, ... items: QuasiValue[]): QuasiValue { export function rec(label: AnyValue, ... items: QuasiValue[]): QuasiValue {
const literals = items.flatMap(i => i.type === 'lit' ? [i.value] : []); const literals = items.flatMap(i => i.type === 'lit' ? [i.value] : []);
if (literals.length === items.length) { if (literals.length === items.length) {
@ -76,18 +86,12 @@ export function quote(quoted: QuasiValue): QuasiValue {
case 'bind': return quote(bind.quasiValue(quoted.inner)); case 'bind': return quote(bind.quasiValue(quoted.inner));
case 'discard': return quote(discard.quasiValue()); case 'discard': return quote(discard.quasiValue());
case 'lit': return rec(Symbol.for('lit'), lit(quoted.value)); case 'lit': return rec(Symbol.for('lit'), lit(quoted.value));
case 'arr': return rec( case 'arr': return rec(Symbol.for('arr'), arr(
Symbol.for('compound'), ... quoted.items.map(quote)));
rec(Symbol.for('arr'), lit(quoted.items.length)), case 'rec': return rec(Symbol.for('rec'), lit(quoted.label), arr(
dict(... quoted.items.map((qq, i) => [i, quote(qq)] as [AnyValue, QuasiValue]))); ... quoted.items.map(quote)));
case 'rec': return rec( case 'dict': return rec(Symbol.for('dict'), dict(
Symbol.for('compound'), ... quoted.entries.map(([k, qq]) => [k, quote(qq)] as [AnyValue, QuasiValue])));
rec(Symbol.for('rec'), lit(quoted.label), lit(quoted.items.length)),
dict(... quoted.items.map((qq, i) => [i, quote(qq)] as [AnyValue, QuasiValue])));
case 'dict': return rec(
Symbol.for('compound'),
rec(Symbol.for('dict')),
dict(... quoted.entries.map(([k, qq]) => [k, quote(qq)] as [AnyValue, QuasiValue])));
case 'unquote': return quoted.unquoted; case 'unquote': return quoted.unquoted;
} }
} }

View File

@ -9,17 +9,12 @@ import { SturdyValue } from "../transport/sturdy.js";
import { import {
Alts, Alts,
Attenuation, Attenuation,
CArr,
CDict,
CRec,
Caveat, Caveat,
ConstructorSpec,
Lit, Lit,
PAnd, PAnd,
PAtom, PAtom,
PBind, PBind,
PCompound, PCompound,
PCompoundMembers,
PDiscard, PDiscard,
PNot, PNot,
PEmbedded, PEmbedded,
@ -72,36 +67,35 @@ export function match(p: Pattern, v: Assertion): Bindings | null {
} }
case 'Lit': case 'Lit':
return is(p.value.value, v); return is(p.value.value, v);
case 'PCompound': { case 'PCompound': switch (p.value._variant) {
const ctor = p.value.ctor; case 'rec': {
const members = p.value.members; if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false;
switch (ctor._variant) { if (!is(p.value.label, v.label)) return false;
case 'CRec': if (p.value.fields.length !== v.length) return false;
if (!Record.isRecord<Assertion, Tuple<Assertion>, Ref>(v)) return false; let index = 0;
if (!is(ctor.value.label, v.label)) return false; for (const pp of p.value.fields) {
if (ctor.value.arity !== v.length) return false; if (!walk(pp, v[index++])) return false;
for (const [key, pp] of members) { }
if (typeof key !== 'number') return false; return true;
if (!walk(pp, v[key])) return false; }
} case 'arr': {
return true; if (!Array.isArray(v)) return false;
case 'CArr': if ('label' in v) return false;
if (!Array.isArray(v)) return false; if (p.value.items.length !== v.length) return false;
if ('label' in v) return false; let index = 0;
if (ctor.value.arity !== v.length) return false; for (const pp of p.value.items) {
for (const [key, pp] of members) { if (!walk(pp, v[index++])) return false;
if (typeof key !== 'number') return false; }
if (!walk(pp, v[key])) return false; return true;
} }
return true; case 'dict':{
case 'CDict': if (!Dictionary.isDictionary<Ref, Assertion>(v)) return false;
if (!Dictionary.isDictionary<Ref, Assertion>(v)) return false; for (const [key, pp] of p.value.entries.entries()) {
for (const [key, pp] of members) { const vv = v.get(key);
const vv = v.get(key); if (vv === void 0) return false;
if (vv === void 0) return false; if (!walk(pp, vv)) return false;
if (!walk(pp, vv)) return false; }
} return true;
return true;
} }
} }
default: default:
@ -132,36 +126,22 @@ export function instantiate(t: Template, b: Bindings): Assertion {
} }
case 'Lit': case 'Lit':
return t.value.value; return t.value.value;
case 'TCompound': { case 'TCompound':
const ctor = t.value.ctor; switch (t.value._variant) {
const members = t.value.members; case 'rec': {
switch (ctor._variant) {
case 'CRec': {
const v = [] as unknown as Record<Assertion, Assertion[], Ref>; const v = [] as unknown as Record<Assertion, Assertion[], Ref>;
v.length = ctor.value.arity; v.label = t.value.label;
v.label = ctor.value.label; t.value.fields.forEach(tt => v.push(walk(tt)));
for (const [key, tt] of members) {
v[key as number] = walk(tt);
}
return v; return v;
} }
case 'CArr': { case 'arr':
const v = []; return t.value.items.map(walk);
v.length = ctor.value.arity; case 'dict': {
for (const [key, tt] of members) {
v[key as number] = walk(tt);
}
return v;
}
case 'CDict': {
const v = new Dictionary<Ref, Assertion>(); const v = new Dictionary<Ref, Assertion>();
for (const [key, tt] of members) { t.value.entries.forEach((tt, key) => v.set(key, walk(tt)));
v.set(key, walk(tt));
}
return v; return v;
} }
} }
}
} }
} }
@ -241,24 +221,16 @@ export function forwarder(ref: Ref): { proxy: Ref, revoker: Ref } {
return { proxy, revoker }; return { proxy, revoker };
} }
export function pRec(label: SturdyValue, ... members: Array<Pattern>): Pattern { export function pRec(label: SturdyValue, ... fields: Array<Pattern>): Pattern {
return Pattern.PCompound(PCompound({ return Pattern.PCompound(PCompound.rec({ label, fields }));
ctor: ConstructorSpec.CRec(CRec({ label: label, arity: members.length })),
members: PCompoundMembers(new Dictionary<_embedded, Pattern>(
members.map((p, i) => [i, p] as const).filter(e => e[1]._variant !== 'PDiscard')))}));
} }
export function pArr(... members: Array<Pattern>): Pattern { export function pArr(... items: Array<Pattern>): Pattern {
return Pattern.PCompound(PCompound({ return Pattern.PCompound(PCompound.arr(items));
ctor: ConstructorSpec.CArr(CArr(members.length)),
members: PCompoundMembers(new Dictionary<_embedded, Pattern>(
members.map((p, i) => [i, p] as const).filter(e => e[1]._variant !== 'PDiscard')))}));
} }
export function pDict(... entries: [SturdyValue, Pattern][]): Pattern { export function pDict(... entries: [SturdyValue, Pattern][]): Pattern {
return Pattern.PCompound(PCompound({ return Pattern.PCompound(PCompound.dict(new Dictionary<_embedded, Pattern>(entries)));
ctor: ConstructorSpec.CDict(CDict()),
members: PCompoundMembers(new Dictionary<_embedded, Pattern>(entries))}));
} }
export function pLit(value: SturdyValue): Pattern { export function pLit(value: SturdyValue): Pattern {

View File

@ -187,11 +187,16 @@ class Node {
table[cls] = nextNode; table[cls] = nextNode;
} }
popCount = 0; popCount = 0;
p.value.members.forEach((member, stepIndex) => { function walkKey(pp: P.Pattern, stepIndex: AnyValue) {
path.push(stepIndex); path.push(stepIndex);
[popCount, nextNode] = walkNode(nextNode, popCount, stepIndex, member); [popCount, nextNode] = walkNode(nextNode, popCount, stepIndex, pp);
path.pop(); path.pop();
}); }
switch (p.value._variant) {
case 'rec': p.value.fields.forEach(walkKey); break;
case 'arr': p.value.items.forEach(walkKey); break;
case 'dict': p.value.entries.forEach(walkKey); break;
}
return [popCount + 1, nextNode]; return [popCount + 1, nextNode];
} }
} }

View File

@ -22,7 +22,7 @@ export function boot(ds: Ref) {
at ds { at ds {
during Observe({ during Observe({
"pattern": :pattern PeriodicTick(\Q.lit($intervalMS)) "pattern": :pattern PeriodicTick(\Q.lit($intervalMS))
}) => spawn linked named (preserves`PeriodicTick(${intervalMS})`) { }) => spawn named (preserves`PeriodicTick(${intervalMS})`) {
const thisFacet = Turn.activeFacet; const thisFacet = Turn.activeFacet;
thisFacet.preventInertCheck(); thisFacet.preventInertCheck();
const handle = setInterval(() => thisFacet.turn(() => { const handle = setInterval(() => thisFacet.turn(() => {
@ -37,7 +37,7 @@ export function boot(ds: Ref) {
at ds { at ds {
during Observe({ during Observe({
"pattern": :pattern TimeLaterThan(\Q.lit($deadlineMS)) "pattern": :pattern TimeLaterThan(\Q.lit($deadlineMS))
}) => spawn linked named (preserves`TimeLaterThan(${deadlineMS})`) { }) => spawn named (preserves`TimeLaterThan(${deadlineMS})`) {
const thisFacet = Turn.activeFacet; const thisFacet = Turn.activeFacet;
thisFacet.preventInertCheck(); thisFacet.preventInertCheck();
let delta = floatValue(deadlineMS) - (+(new Date())); let delta = floatValue(deadlineMS) - (+(new Date()));

View File

@ -9,12 +9,12 @@ export function boot(ds: Ref, debug: boolean = false) {
spawn named 'wsRelay' { spawn named 'wsRelay' {
at ds { at ds {
during Observe({ "pattern": :pattern G.Resolved({ during Observe({ "pattern": :pattern G.Resolved({
"addr": \Q.lit($addrValue), "addr": \$addrPatValue,
"sturdyref": \Q.lit($sturdyRefValue), "sturdyref": \$sturdyRefPatValue,
"resolved": \Q.bind(), "resolved": \Q.bind(),
}) }) => { }) }) => {
const addr = G.toRelayAddress(addrValue); const addr = Q.drop_lit(addrPatValue, G.toRelayAddress);
const sturdyref = Schemas.sturdy.toSturdyRef(sturdyRefValue); const sturdyref = Q.drop_lit(sturdyRefPatValue, Schemas.sturdy.toSturdyRef);
if (addr && sturdyref) { if (addr && sturdyref) {
assert G.fromViaRelay(G.ViaRelay({ assert G.fromViaRelay(G.ViaRelay({
"addr": addr, "addr": addr,