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-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 * as P from '../gen/dataspacePatterns.js';
@ -24,9 +24,9 @@ export function classOfValue(v: any): Shape | null {
export function classOfCtor(v: P.DCompound): Shape {
switch (v._variant) {
case 'rec':
return canonicalString(v.ctor.label) + '/' + v.ctor.arity;
return canonicalString(v.label) + '/' + v.fields.length;
case 'arr':
return '' + v.ctor.arity;
return '' + v.items.length;
case 'dict':
return '{}';
}
@ -59,14 +59,20 @@ export function analysePattern(p: P.Pattern): PatternAnalysis {
};
const path: Path = [];
function walkKey(p: P.Pattern, key: AnyValue) {
path.push(key);
walk(p);
path.pop();
}
function walk(p: P.Pattern) {
switch (p._variant) {
case 'DCompound':
p.value.members.forEach((v, k) => {
path.push(k);
walk(v);
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;
}
break;
case 'DBind':
result.capturePaths.push(path.slice());
@ -76,7 +82,7 @@ export function analysePattern(p: P.Pattern): PatternAnalysis {
break;
case 'DLit':
result.constPaths.push(path.slice());
result.constValues.push(p.value.value);
result.constValues.push(P.fromAnyAtom(p.value.value));
break;
}
}
@ -102,10 +108,21 @@ export function match(p: P.Pattern, v: AnyValue): Array<AnyValue> | false {
const pcls = classOfCtor(p.value);
const vcls = classOfValue(v);
if (pcls !== vcls) return false;
for (const [stepIndex, pp] of p.value.members.entries()) {
const vv = step(v, stepIndex);
if (vv === void 0) return false;
if (!walk(pp, vv)) return false;
let items: Array<P.Pattern>;
switch (p.value._variant) {
case 'dict':
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;
}
@ -121,11 +138,16 @@ export function isCompletelyConcrete(p: P.Pattern): boolean {
case 'DBind': return false;
case 'DDiscard': return false;
case 'DLit': return true;
case 'DCompound':
for (const pp of p.value.members.values()) {
if (!walk(pp)) return false;
case 'DCompound': switch (p.value._variant) {
case 'rec': return p.value.fields.every(isCompletelyConcrete);
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);
@ -137,11 +159,19 @@ export function withoutCaptures(p: P.Pattern): P.Pattern {
case 'DBind': return walk(p.value.pattern);
case 'DDiscard': return p;
case 'DLit': return p;
case 'DCompound': return P.Pattern.DCompound({
_variant: p.value._variant,
ctor: p.value.ctor,
members: p.value.members.mapEntries(e => [e[0], walk(e[1])]),
} as P.DCompound);
case 'DCompound':
switch (p.value._variant) {
case 'rec':
return P.Pattern.DCompound(P.DCompound.rec({
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);
@ -161,38 +191,67 @@ export function discard(): P.Pattern {
export const _ = discard();
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> {
const members = new KeyedDictionary<number, P.Pattern, Ref>();
items.forEach((p, i) => {
if (!is(p, _)) members.set(i, p);
});
return members;
export function drop_lit(p: P.Pattern): AnyValue | null {
const e = new Error();
function walk(p: P.Pattern): AnyValue {
switch (p._variant) {
case 'DCompound':
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 {
return P.Pattern.DCompound(P.DCompound.rec({
ctor: P.CRec({
label,
arity: fields.length,
}),
members: indexedMembers(fields),
}));
return P.Pattern.DCompound(P.DCompound.rec({ label, fields }));
}
export function arr(... patterns: P.Pattern[]): P.Pattern {
return P.Pattern.DCompound(P.DCompound.arr({
ctor: P.CArr(patterns.length),
members: indexedMembers(patterns),
}));
return P.Pattern.DCompound(P.DCompound.arr(patterns));
}
export function dict(... entries: [AnyValue, P.Pattern][]): P.Pattern {
const members = new KeyedDictionary<AnyValue, P.Pattern, Ref>();
entries.forEach(([k, p]) => {
if (!is(p, _)) members.set(k, p);
});
return P.Pattern.DCompound(P.DCompound.dict({ ctor: P.CDict(), members }));
return P.Pattern.DCompound(P.DCompound.dict(new Dictionary<Ref, P.Pattern>(entries)));
}

View File

@ -2,7 +2,7 @@
/// SPDX-FileCopyrightText: Copyright © 2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
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 { Value, RecordConstructorInfo, is, Record } from '@preserves/core';
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);
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 {
const literals = items.flatMap(i => i.type === 'lit' ? [i.value] : []);
if (literals.length === items.length) {
@ -76,18 +86,12 @@ export function quote(quoted: QuasiValue): QuasiValue {
case 'bind': return quote(bind.quasiValue(quoted.inner));
case 'discard': return quote(discard.quasiValue());
case 'lit': return rec(Symbol.for('lit'), lit(quoted.value));
case 'arr': return rec(
Symbol.for('compound'),
rec(Symbol.for('arr'), lit(quoted.items.length)),
dict(... quoted.items.map((qq, i) => [i, quote(qq)] as [AnyValue, QuasiValue])));
case 'rec': return rec(
Symbol.for('compound'),
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 'arr': return rec(Symbol.for('arr'), arr(
... quoted.items.map(quote)));
case 'rec': return rec(Symbol.for('rec'), lit(quoted.label), arr(
... quoted.items.map(quote)));
case 'dict': return rec(Symbol.for('dict'), dict(
... quoted.entries.map(([k, qq]) => [k, quote(qq)] as [AnyValue, QuasiValue])));
case 'unquote': return quoted.unquoted;
}
}

View File

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

View File

@ -187,11 +187,16 @@ class Node {
table[cls] = nextNode;
}
popCount = 0;
p.value.members.forEach((member, stepIndex) => {
function walkKey(pp: P.Pattern, stepIndex: AnyValue) {
path.push(stepIndex);
[popCount, nextNode] = walkNode(nextNode, popCount, stepIndex, member);
[popCount, nextNode] = walkNode(nextNode, popCount, stepIndex, pp);
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];
}
}

View File

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

View File

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