Update compiler

This commit is contained in:
Tony Garnock-Jones 2021-12-03 00:55:42 +01:00
parent f9d1e694e0
commit dd14c8471d
21 changed files with 448 additions and 515 deletions

View File

@ -1,3 +1,5 @@
__ignored__ := $(shell ./setup.sh)
LERNA=./node_modules/.bin/lerna
bootstrap: node_modules/lerna

View File

@ -3,7 +3,7 @@
import {
isToken, isTokenType, replace, commaJoin, startPos, fixPos, joinItems,
laxRead, itemText, match,
laxRead, itemText,
Items, Pattern, Templates, Substitution, TokenType,
SourceMap, CodeWriter, TemplateFunction, Token, SpanIndex,
@ -11,19 +11,12 @@ import {
import {
SyndicateParser, SyndicateTypedParser,
Identifier,
FacetAction,
TurnAction,
Statement,
ActivationImport,
FacetFields,
Binder,
compilePattern,
patternText,
instantiatePatternToPattern,
} from './grammar.js';
import {
BootProc,
} from './internals.js';
export function stripShebang(items: Items): Items {
if ((items.length > 0) &&
@ -52,22 +45,15 @@ export interface CompilerOutput {
sourceToTargetMap: SpanIndex<number>;
}
function receiverFor(s: FacetAction): Substitution {
return (s.implicitFacet) ? 'thisFacet.' : '.';
}
export interface ActivationRecord {
activation: ActivationImport;
activationScriptId: Identifier;
function receiverFor(s: TurnAction): Substitution {
return (s.implicitTurn) ? 'thisTurn.' : '.';
}
export class ExpansionContext {
readonly parser: SyndicateParser;
readonly moduleType: ModuleType;
readonly activationRecords: Array<ActivationRecord> = [];
hasBootProc: boolean = false;
hasBootableBootProc: boolean = false;
readonly typescript: boolean;
_collectedFields: FacetFields | null = null;
nextIdNumber = 0;
constructor(moduleType: ModuleType,
@ -82,28 +68,12 @@ export class ExpansionContext {
return '__SYNDICATE__id_' + (this.nextIdNumber++);
}
get collectedFields(): FacetFields {
// Allocates a transient array for collected fields in
// contexts lacking a surrounding collector - that is, for errors.
return this._collectedFields ?? [];
argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Items {
return (this.typescript) ? t`${name}: ${type}` : t`name`;
}
collectField(f: Binder) {
this.collectedFields.push(f);
}
withCollectedFields<T>(fs: FacetFields, f: () => T): T {
const oldCollectedFields = this._collectedFields;
try {
this._collectedFields = fs;
return f();
} finally {
this._collectedFields = oldCollectedFields;
}
}
argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Substitution {
return (this.typescript) ? t`${name}: ${type}` : name;
turnDecl(t: TemplateFunction): Items {
return this.argDecl(t, 'thisTurn', '__SYNDICATE__.Turn');
}
}
@ -111,18 +81,6 @@ function stringifyId(i: Identifier): Items {
return [ { ... i, type: TokenType.STRING, text: JSON.stringify(i.text) } ];
}
function facetFieldObjectType(
t: TemplateFunction,
fs: FacetFields,
defaultType?: Substitution): Substitution
{
function formatBinder(binder: Binder) {
const hasType = ((binder.type ?? defaultType) !== void 0);
return t`${[binder.id]}${hasType ? ': ': ''}${binder.type ?? defaultType ?? ''}`;
}
return t`{${commaJoin(fs.map(formatBinder))}}`;
}
function binderTypeGuard(t: TemplateFunction): (binder: Binder, index: number) => Items {
return (binder, index) => {
if (binder.id.text[0] === '_') {
@ -156,7 +114,7 @@ export function expand(tree: Items, ctx: ExpansionContext): Items {
function terminalWrap(t: TemplateFunction, isTerminal: boolean, body: Statement): Statement {
if (isTerminal) {
return t`thisFacet._stop(function (thisFacet) {${body}})`
return t`thisTurn._stop(thisTurn.activeFacet, (${ctx.turnDecl(t)}) => {${body}})`
} else {
return body;
}
@ -166,7 +124,7 @@ export function expand(tree: Items, ctx: ExpansionContext): Items {
tree = replace(tree, p, (v, start) => f(v, macro.template(fixPos(start))));
}
function xf<T extends FacetAction>(p: Pattern<T>, f: (v: T, t: TemplateFunction) => Items) {
function xf<T extends TurnAction>(p: Pattern<T>, f: (v: T, t: TemplateFunction) => Items) {
x(p, (v, t) => t`${receiverFor(v)}${f(v, t)}`);
}
@ -174,175 +132,156 @@ export function expand(tree: Items, ctx: ExpansionContext): Items {
const maybeWalk = (tree?: Items) : Items | undefined => (tree === void 0) ? tree : walk(tree);
xf(ctx.parser.duringStatement, (s, t) => {
let spawn0 = match(ctx.parser.spawn, s.body, null);
if (spawn0 !== null) {
const spawn = spawn0;
const id = ctx.quasiRandomId();
const instantiated = patternText(instantiatePatternToPattern(s.pattern));
return t`on asserted ${patternText(s.pattern)} => {
const ${id} = __SYNDICATE__.genUuid();
const ${id}_inst = __SYNDICATE__.Instance(${id});
react {
stop on asserted ${id}_inst => react {
stop on retracted ${id}_inst;
stop on retracted :snapshot ${instantiated};
}
stop on retracted :snapshot ${instantiated} => react {
stop on asserted ${id}_inst;
}
}
spawn
${spawn.isDataspace ? 'dataspace' : []}
${spawn.name === void 0 ? [] : t`named ${spawn.name}`}
:asserting ${id}_inst
${joinItems(spawn.initialAssertions.map(e => t`:asserting ${e}`), ' ')}
${joinItems(spawn.parentBinders.map((b, i) => {
const init = spawn.parentInits[i];
return t`:let ${[b.id]}${b.type === void 0 ? [] : t`: ${b.type}`} = ${init}`;
}), ' ')}
{
assert ${id}_inst;
stop on retracted __SYNDICATE__.Observe(${id}_inst);
${spawn.body}
}
}`;
} else {
// TODO: untyped template
const sa = compilePattern(s.pattern);
return t`withSelfDo(function (thisFacet) {
const _Facets = new __SYNDICATE__.Dictionary<any, __SYNDICATE__.Facet<any>>();
on asserted ${patternText(s.pattern)} => react {
_Facets.set([${commaJoin(sa.captureBinders.map(t=>[t.id]))}], thisFacet);
dataflow { } // TODO: horrible hack to keep the facet alive if no other endpoints
${s.body}
}
on retracted ${patternText(s.pattern)} => {
const ${ctx.argDecl(t, '_Key', '__SYNDICATE__.Value<any>[]')} =
[${commaJoin(sa.captureBinders.map(t=>[t.id]))}];
_Facets.get(_Key)?._stop();
_Facets.delete(_Key);
}
});`;
}
// TODO: untyped template
const sa = compilePattern(s.pattern);
return t`assertDataflow((${ctx.turnDecl(t)}) => ({
target: currentSyndicateTarget,
assertion: __SYNDICATE__.fromObserve(__SYNDICATE__.Observe({
pattern: ${sa.skeleton},
observer: thisTurn.ref(__SYNDICATE__.assertionFacetObserver(
(${ctx.turnDecl(t)}, ${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) => {
if (Array.isArray(__vs)) {
${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
${walk(s.body)}
}
}
))
})),
}));`;
});
xf(ctx.parser.spawn, (s, t) => {
// TODO: parentBinders, parentInits
let body = ctx.withCollectedFields(s.facetFields, () => walk(s.body));
let proc = t`function (thisFacet) {${body}}`;
if (s.isDataspace) proc = t`__SYNDICATE__.inNestedDataspace(${proc})`;
let body = walk(s.body);
/*
let assertions = (s.initialAssertions.length > 0)
? t`, new __SYNDICATE__.Set([${commaJoin(s.initialAssertions.map(walk))}])`
: ``;
let fieldTypeParam = ctx.typescript ? t`<${facetFieldObjectType(t, s.facetFields)}>` : '';
return t`_spawn${fieldTypeParam}(${maybeWalk(s.name) ?? 'null'}, ${proc}${assertions});`;
*/
const n = s.name === void 0 ? '' : t` thisTurn.activeFacet.actor.name = ${walk(s.name)};`;
return t`_spawn${s.isLink ? 'Link': ''}((${ctx.turnDecl(t)}) => {${n} ${body} });`;
});
xf(ctx.parser.fieldDeclarationStatement, (s, t) => {
ctx.collectField(s.property);
return t`declareField(this, ${stringifyId(s.property.id)}, ${maybeWalk(s.init) ?? 'void 0'});`;
x(ctx.parser.fieldDeclarationStatement, (s, t) => {
const ft = ctx.typescript ? t`<${s.field.type ?? '__SYNDICATE__.AnyValue'}>` : '';
return t`const ${[s.field.id]} = thisTurn.field${ft}(${maybeWalk(s.init) ?? 'void 0'}, ${stringifyId(s.field.id)});`;
});
x(ctx.parser.atStatement, (s, t) => {
return t`(((${ctx.argDecl(t, 'currentSyndicateTarget', '__SYNDICATE__.Ref')}) => {${walk(s.body)}})(${walk(s.target)}));`;
});
x(ctx.parser.createExpression, (s, t) => {
return t`thisTurn.ref(${walk(s.entity)})`;
});
xf(ctx.parser.assertionEndpointStatement, (s, t) => {
if (s.test == void 0) {
return t`addEndpoint(thisFacet => ({ assertion: ${walk(s.template)}, analysis: null }));`;
if (s.isDynamic) {
if (s.test == void 0) {
return t`assertDataflow((${ctx.turnDecl(t)}) => ({ target: currentSyndicateTarget, assertion: ${walk(s.template)} }));`;
} else {
return t`assertDataflow((${ctx.turnDecl(t)}) => (${walk(s.test)})
? ({ target: currentSyndicateTarget, assertion: ${walk(s.template)} })
: ({ target: void 0, assertion: void 0 }));`;
}
} else {
return t`addEndpoint(thisFacet => (${walk(s.test)})
? ({ assertion: ${walk(s.template)}, analysis: null })
: ({ assertion: void 0, analysis: null }), ${''+s.isDynamic});`;
if (s.test == void 0) {
return t`assert(currentSyndicateTarget, ${walk(s.template)});`;
} else {
return t`replace(currentSyndicateTarget, void 0, (${walk(s.test)}) ? (${walk(s.template)}) : void 0);`;
}
}
});
xf(ctx.parser.dataflowStatement, (s, t) =>
t`addDataflow(function (thisFacet) {${walk(s.body)}});`);
t`_dataflow((${ctx.turnDecl(t)}) => {${walk(s.body)}});`);
xf(ctx.parser.eventHandlerEndpointStatement, (s, t) => {
switch (s.triggerType) {
case 'dataflow':
return t`withSelfDo(function (thisFacet) { dataflow { if (${walk(s.predicate)}) { ${terminalWrap(t, s.terminal, walk(s.body))} } } });`;
if (s.triggerType === 'dataflow') {
return t`withSelfDo((${ctx.turnDecl(t)}) => { dataflow { if (${walk(s.predicate)}) { ${terminalWrap(t, s.terminal, walk(s.body))} } } });`;
}
case 'start':
case 'stop': {
const m = s.triggerType === 'start' ? 'addStartScript' : 'addStopScript';
return t`${m}(function (thisFacet) {${walk(s.body)}});`;
}
if (s.triggerType === 'stop') {
return t`activeFacet.onStop((${ctx.turnDecl(t)}) => {${walk(s.body)}});`;
}
case 'asserted':
case 'retracted':
case 'message': {
const sa = compilePattern(s.pattern);
const expectedEvt = ({
'asserted': 'ADDED',
'retracted': 'REMOVED',
'message': 'MESSAGE',
})[s.triggerType];
return t`addEndpoint(thisFacet => ({
assertion: __SYNDICATE__.Observe(${walk(sa.assertion)}),
analysis: {
skeleton: ${walk(sa.skeleton)},
constPaths: ${JSON.stringify(sa.constPaths)},
constVals: [${commaJoin(sa.constVals.map(walk))}],
capturePaths: ${JSON.stringify(sa.capturePaths)},
callback: thisFacet.wrap((thisFacet, __Evt, ${ctx.argDecl(t, '__vs', 'Array<__SYNDICATE__.Value<any>>')}) => {
if (__Evt === __SYNDICATE__.Skeleton.EventType.${expectedEvt}) {
const sa = compilePattern(s.pattern);
const guardBody = (body: Statement) => t`if (Array.isArray(__vs)) {
${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
thisFacet.scheduleScript(() => {${terminalWrap(t, s.terminal, walk(s.body))}});
}
})
}
}), ${'' + s.isDynamic});`;
}
${body}
}`;
let entity: Items;
switch (s.triggerType) {
case 'asserted':
entity = t`{
assert(${ctx.turnDecl(t)}, ${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}, __handle: __SYNDICATE__.Handle) {
${guardBody(terminalWrap(t, s.terminal, walk(s.body)))}
}
}`;
break;
case 'retracted':
entity = t`__SYNDICATE__.assertionObserver((${ctx.turnDecl(t)}, ${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) => {
${guardBody(t`return (${ctx.turnDecl(t)}) => { ${terminalWrap(t, s.terminal, walk(s.body))} };`)}
})`;
break;
case 'message':
entity = t`{
message(${ctx.turnDecl(t)}, ${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) {
${guardBody(terminalWrap(t, s.terminal, walk(s.body)))}
}
}`;
break;
}
const assertion = t`__SYNDICATE__.fromObserve(__SYNDICATE__.Observe({
pattern: ${sa.skeleton},
observer: thisTurn.ref(${entity}),
}))`;
if (s.isDynamic) {
return t`assertDataflow((${ctx.turnDecl(t)}) => ({
target: currentSyndicateTarget,
assertion: ${assertion},
}));`;
} else {
return t`replace(currentSyndicateTarget, void 0, ${assertion});`;
}
});
x(ctx.parser.typeDefinitionStatement, (s, t) => {
const l = `Symbol.for(${JSON.stringify(s.label.text)})`;
const fns = JSON.stringify(s.fields.map(f => f.id.text));
const formatBinder = (b: Binder) => t`${[b.id]}: ${b.type ?? '__SYNDICATE__.AnyValue'}`;
const fs = ctx.typescript
? t`<${facetFieldObjectType(t, s.fields, t`__SYNDICATE__.Value<any>`)}, any>`
? t`<{${commaJoin(s.fields.map(formatBinder))}}, __SYNDICATE__.Ref>`
: '';
return t`const ${[s.label]} = __SYNDICATE__.Record.makeConstructor${fs}()(${maybeWalk(s.wireName) ?? l}, ${fns});`;
});
xf(ctx.parser.messageSendStatement, (s, t) => t`_send(${walk(s.expr)});`);
xf(ctx.parser.messageSendStatement, (s, t) => t`message(currentSyndicateTarget, ${walk(s.expr)});`);
xf(ctx.parser.reactStatement, (s, t) => {
const body = ctx.withCollectedFields(s.facetFields, () => walk(s.body));
const fieldTypeParam = ctx.typescript
? t`<${facetFieldObjectType(t, s.facetFields)}>`
: '';
return t`addChildFacet${fieldTypeParam}(function (thisFacet) {${body}});`;
return t`facet((${ctx.turnDecl(t)}) => {${s.body}});`;
});
x(ctx.parser.activationImport, (s) => {
const activationScriptId: Token = {
start: s.activationKeyword.start,
end: s.activationKeyword.end,
text: `__SYNDICATE__activationScript${'' + ctx.activationRecords.length}`,
type: TokenType.ATOM
};
ctx.activationRecords.push({ activation: s, activationScriptId });
return [];
}),
x(ctx.parser.bootStatement, (s, t) => {
ctx.hasBootProc = true;
const activationStatements = ctx.activationRecords.map(({ activationScriptId: id }) =>
t`thisFacet.activate(${[id]}); `);
const body = t`${joinItems(activationStatements)}${walk(s)}`;
const facetDecl = ctx.typescript ? 'thisFacet: __SYNDICATE__.Facet<{}>' : 'thisFacet';
ctx.hasBootableBootProc = s.formals.length == 0;
const body = t`{${walk(s.body)}}`;
const bootFormals = commaJoin([ctx.turnDecl(t), ... s.formals.map(
b => ctx.argDecl(t, [b.id], b.type ?? '__SYNDICATE__.AnyValue'))]);
switch (ctx.moduleType) {
case 'es6':
return t`export function ${BootProc}(${facetDecl}) {${body}}`;
return t`export function boot(${bootFormals}) ${body}`;
case 'require':
return t`module.exports.${BootProc} = function (${facetDecl}) {${body}};`;
return t`module.exports.boot = (${bootFormals}) => ${body};`;
case 'global':
return t`function ${BootProc}(${facetDecl}) {${body}}`;
return t`function boot(${bootFormals}) ${body}`;
}
});
xf(ctx.parser.stopStatement, (s, t) =>
t`_stop(function (thisFacet) {${walk(s.body)}});`)
t`withSelfDo((${ctx.turnDecl(t)}) => thisTurn._stop(thisTurn.activeFacet, (${ctx.turnDecl(t)}) => {${walk(s.body)}});`)
return tree;
}
@ -369,32 +308,20 @@ export function compile(options: CompileOptions): CompilerOutput {
const ts = macro.template(fixPos(start));
const te = macro.template(fixPos(end));
if (ctx.hasBootProc) {
if (ctx.hasBootableBootProc) {
let bp;
switch (moduleType) {
case 'es6':
case 'global':
bp = BootProc;
bp = te`boot`;
break;
case 'require':
bp = te`module.exports.${BootProc}`;
bp = te`module.exports.boot`;
break;
}
tree = te`${tree}\nif (typeof module !== 'undefined' && ((typeof require === 'undefined' ? {main: void 0} : require).main === module)) __SYNDICATE__.bootModule(${bp});`;
tree = te`${tree}\nif (typeof module !== 'undefined' && ((typeof require === 'undefined' ? {main: void 0} : require).main === module)) __SYNDICATE__.Actor.boot(${bp});`;
}
const activationImports = ctx.activationRecords.map(r => {
const a = r.activation;
const t = macro.template(a.activationKeyword.start);
switch (a.target.type) {
case 'import':
return t`import { ${BootProc} as ${[r.activationScriptId]} } from ${[a.target.moduleName]};\n`;
case 'expr':
return t`const ${[r.activationScriptId]} = (${a.target.moduleExpr}).${BootProc};\n`;
}
});
tree = ts`${joinItems(activationImports)}${tree}`;
{
const runtime = options.runtime ?? '@syndicate-lang/core';
switch (moduleType) {
@ -413,7 +340,6 @@ export function compile(options: CompileOptions): CompilerOutput {
const cw = new CodeWriter(inputFilename);
cw.emit(tree);
const text = cw.text;
return {

View File

@ -2,17 +2,15 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import {
TokenType, Token, Items,
Token, Items,
Pattern,
foldItems, match, anonymousTemplate as template, commaJoin,
startPos,
scope, bind, seq, alt, upTo, atom, atomString, group, exec,
scope, bind, seq, alt, upTo, atom, atomString, group,
repeat, option, withoutSpace, map, mapm, rest, discard,
value, succeed, fail, separatedBy, anything, not, follows,
value, succeed, fail, separatedBy, anything, not
} from '../syntax/index.js';
import * as Matcher from '../syntax/matcher.js';
import { Path, Skeleton } from './internals.js';
//---------------------------------------------------------------------------
// AST types
@ -23,41 +21,37 @@ export type Identifier = Token;
export type Type = Items;
export type Binder = { id: Identifier, type?: Type };
export interface FacetAction {
implicitFacet: boolean;
export interface TurnAction {
implicitTurn: boolean;
}
export type FacetFields = Binder[];
export interface FacetProducingAction extends FacetAction {
export interface FacetSetupAction extends TurnAction {
body: Statement;
facetFields: FacetFields;
}
export interface SpawnStatement extends FacetProducingAction {
isDataspace: boolean;
export interface SpawnStatement extends FacetSetupAction {
name?: Expr;
initialAssertions: Expr[];
isLink: boolean;
parentBinders: Binder[];
parentInits: Expr[];
}
export interface FieldDeclarationStatement extends FacetAction {
property: Binder;
export interface FieldDeclarationStatement extends TurnAction {
field: Binder;
init?: Expr;
}
export interface AssertionEndpointStatement extends FacetAction {
export interface AssertionEndpointStatement extends TurnAction {
isDynamic: boolean,
template: Expr,
test?: Expr,
}
export interface StatementFacetAction extends FacetAction {
export interface StatementTurnAction extends TurnAction {
body: Statement;
}
export interface GenericEventEndpointStatement extends StatementFacetAction {
export interface GenericEventEndpointStatement extends StatementTurnAction {
terminal: boolean;
isDynamic: boolean;
}
@ -68,7 +62,7 @@ export interface DataflowEndpointStatement extends GenericEventEndpointStatement
}
export interface PseudoEventEndpointStatement extends GenericEventEndpointStatement {
triggerType: 'start' | 'stop';
triggerType: 'stop';
}
export interface AssertionEventEndpointStatement extends GenericEventEndpointStatement {
@ -86,20 +80,29 @@ export interface TypeDefinitionStatement {
wireName?: Expr;
}
export interface MessageSendStatement extends FacetAction {
export interface MessageSendStatement extends TurnAction {
expr: Expr;
}
export interface DuringStatement extends FacetProducingAction {
export interface DuringStatement extends FacetSetupAction {
pattern: ValuePattern;
}
export interface ReactStatement extends FacetProducingAction {
export interface ReactStatement extends FacetSetupAction {
}
export interface ActivationImport {
activationKeyword: Identifier;
target: { type: 'import', moduleName: Token } | { type: 'expr', moduleExpr: Expr };
export interface BootStatement {
formals: Binder[];
body: Statement;
}
export interface AtStatement {
target: Expr;
body: Statement;
}
export interface CreateExpression {
entity: Expr;
}
//---------------------------------------------------------------------------
@ -115,23 +118,28 @@ export interface PDiscard {
type: 'PDiscard',
}
export interface PConstructor {
type: 'PConstructor',
ctor: Expr,
arguments: ValuePattern[],
}
export interface PConstant {
type: 'PConstant',
value: Expr,
}
export interface PRecord {
type: 'PRecord',
ctor: Expr,
arguments: ValuePattern[],
}
export interface PArray {
type: 'PArray',
elements: ValuePattern[],
}
export type ValuePattern = PCapture | PDiscard | PConstructor | PConstant | PArray;
export interface PDict {
type: 'PDict',
elements: [Expr, ValuePattern][],
}
export type ValuePattern = PCapture | PDiscard | PRecord | PConstant | PArray | PDict;
interface RawCall {
items: Items;
@ -140,12 +148,8 @@ interface RawCall {
}
export interface StaticAnalysis {
skeleton: Expr;
constPaths: Path[];
constVals: Expr[];
capturePaths: Path[];
skeleton: Expr; // constructs a P.Pattern
captureBinders: Binder[];
assertion: Expr;
}
//---------------------------------------------------------------------------
@ -180,11 +184,11 @@ export class SyndicateParser {
i => i ? acc.push(i) : void 0))));
}
facetAction<T extends FacetAction>(pattern: (scope: T) => Pattern<any>): Pattern<T> {
turnAction<T extends TurnAction>(pattern: (scope: T) => Pattern<any>): Pattern<T> {
return i => {
const scope = Object.create(null);
scope.implicitFacet = true;
const p = seq(option(map(atom('.'), _ => scope.implicitFacet = false)), pattern(scope));
scope.implicitTurn = true;
const p = seq(option(map(atom('.'), _ => scope.implicitTurn = false)), pattern(scope));
const r = p(i);
if (r === null) return null;
return [scope, r[1]];
@ -193,45 +197,43 @@ export class SyndicateParser {
readonly headerExpr = this.expr(kw('asserting'), kw('let'));
// Principal: Facet
// Principal: Turn
readonly spawn: Pattern<SpawnStatement> =
this.facetAction(o => {
o.isDataspace = false;
o.initialAssertions = [];
this.turnAction(o => {
o.isLink = false;
o.parentBinders = [];
o.parentInits = [];
o.body = [];
o.facetFields = [];
return seq(atom('spawn'),
option(seq(atom('dataspace'), exec(() => o.isDataspace = true))),
option(seq(atom('named'),
bind(o, 'name', this.headerExpr))),
repeat(alt(seq(kw('asserting'),
map(this.headerExpr, e => o.initialAssertions.push(e))),
map(scope((l: { b: Binder, init: Expr }) =>
seq(kw('let'),
bind(l, 'b', this.binder),
atom('='),
bind(l, 'init', this.headerExpr))),
l => {
o.parentBinders.push(l.b);
o.parentInits.push(l.init);
}))),
option(map(atom('linked'), _ => o.isLink = true)),
option(seq(atom('named'), bind(o, 'name', this.headerExpr))),
repeat(alt(
/* seq(kw('asserting'), map(this.headerExpr, e => o.initialAssertions.push(e))), */
map(scope(
(l: { b: Binder, init: Expr }) =>
seq(kw('let'),
bind(l, 'b', this.binder),
atom('='),
bind(l, 'init', this.headerExpr))),
l => {
o.parentBinders.push(l.b);
o.parentInits.push(l.init);
}))),
this.block(o.body));
});
// Principal: Dataspace, but only for implementation reasons, so really Facet
// Principal: Turn
readonly fieldDeclarationStatement: Pattern<FieldDeclarationStatement> =
this.facetAction(o => {
this.turnAction(o => {
return seq(atom('field'),
bind(o, 'property', this.binder),
bind(o, 'field', this.binder),
option(seq(atom('='), bind(o, 'init', this.expr()))),
this.statementBoundary);
});
// Principal: Facet
// Principal: Turn
readonly assertionEndpointStatement: Pattern<AssertionEndpointStatement> =
this.facetAction(o => {
this.turnAction(o => {
o.isDynamic = true;
return seq(atom('assert'),
option(map(kw('snapshot'), _ => o.isDynamic = false)),
@ -240,15 +242,15 @@ export class SyndicateParser {
this.statementBoundary);
});
blockFacetAction(kw: Pattern<any>): Pattern<StatementFacetAction> {
return this.facetAction(o => {
blockTurnAction(kw: Pattern<any>): Pattern<StatementTurnAction> {
return this.turnAction(o => {
o.body = [];
return seq(kw, this.block(o.body));
});
}
// Principal: Facet
readonly dataflowStatement = this.blockFacetAction(atom('dataflow'));
// Principal: Turn
readonly dataflowStatement = this.blockTurnAction(atom('dataflow'));
mandatoryIfNotTerminal(o: GenericEventEndpointStatement, p: Pattern<any>): Pattern<any> {
return i => {
@ -256,9 +258,9 @@ export class SyndicateParser {
};
}
// Principal: Facet
// Principal: Turn
readonly eventHandlerEndpointStatement: Pattern<EventHandlerEndpointStatement> =
this.facetAction(o => {
this.turnAction(o => {
o.terminal = false;
o.isDynamic = true;
o.body = [];
@ -268,8 +270,7 @@ export class SyndicateParser {
this.expr())),
_ => o.triggerType = 'dataflow'),
this.mandatoryIfNotTerminal(o, this.statement(o.body))),
mapm(seq(bind(o, 'triggerType',
alt(atomString('start'), atomString('stop'))),
mapm(seq(bind(o, 'triggerType', atomString('stop')),
option(this.statement(o.body))),
v => o.terminal ? fail : succeed(v)),
seq(bind(o, 'triggerType',
@ -288,67 +289,77 @@ export class SyndicateParser {
scope(o => seq(bind(o, 'expectedUse', alt(atomString('message'), atomString('assertion'))),
atom('type'),
bind(o, 'label', this.identifier),
group('(', bind(o, 'fields',
repeat(this.binder, { separator: atom(',') }))),
group('(', bind(o, 'fields', repeat(this.binder, { separator: atom(',') }))),
option(seq(atom('='),
bind(o, 'wireName', withoutSpace(upTo(this.statementBoundary))))),
this.statementBoundary));
// Principal: Facet
// Principal: Turn
readonly messageSendStatement: Pattern<MessageSendStatement> =
this.facetAction(o => seq(atom('send'),
this.turnAction(o => seq(atom('send'),
atom('message'),
not(this.statementBoundary),
bind(o, 'expr', withoutSpace(upTo(this.statementBoundary))),
this.statementBoundary));
// Principal: Facet
// Principal: Turn
readonly duringStatement: Pattern<DuringStatement> =
this.facetAction(o => {
this.turnAction(o => {
o.body = [];
o.facetFields = [];
return seq(atom('during'),
bind(o, 'pattern', this.valuePattern(atom('=>'))),
seq(atom('=>'), this.statement(o.body)));
});
// Principal: Facet
// Principal: Turn
readonly reactStatement: Pattern<ReactStatement> =
this.facetAction(o => {
this.turnAction(o => {
o.body = [];
o.facetFields = [];
return seq(atom('react'), this.block(o.body));
});
// Principal: none
readonly bootStatement: Pattern<Statement> =
value(o => {
o.value = [];
return seq(atom('boot'), this.block(o.value));
readonly bootStatement: Pattern<BootStatement> =
scope(o => {
o.body = [];
return seq(
atom('boot'),
group('(', bind(o, 'formals', repeat(this.binder, { separator: atom(',') }))),
this.block(o.body));
});
// Principal: Facet
readonly stopStatement = this.blockFacetAction(atom('stop'));
// Principal: Turn
readonly stopStatement = this.blockTurnAction(atom('stop'));
// Principal: none
readonly activationImport: Pattern<ActivationImport> =
scope(o => seq(bind(o, 'activationKeyword', atom('activate')),
follows(alt<any>(seq(atom('import'),
upTo(seq(
map(atom(void 0, { tokenType: TokenType.STRING }),
n => o.target = {
type: 'import',
moduleName: n
}),
this.statementBoundary))),
map(this.expr(), e => o.target = {
type: 'expr',
moduleExpr: e
})))));
readonly atStatement: Pattern<AtStatement> =
scope(o => {
o.body = [];
return seq(atom('at'),
bind(o, 'target', this.expr()),
this.statement(o.body));
});
// Principal: none
readonly createExpression: Pattern<CreateExpression> =
scope(o => seq(atom('create'), bind(o, 'entity', this.expr())));
//---------------------------------------------------------------------------
// Syntax of patterns over Value, used in endpoints
// $id - capture of discard
// _ - discard
//
// expr(pat, ...) - record ctor
// $id(pat) - nested capture
// [pat, ...] - array pat
// {expr: pat, ...} - dict pat
//
// expr(expr, ...) - constant
// [expr, ...] - constant
// {expr: expr, ...} - constant
// other - constant
readonly pCaptureBinder: Pattern<Binder> =
mapm(this.binder, i => {
return i.id.text.startsWith('$')
@ -366,16 +377,28 @@ export class SyndicateParser {
bs => bs.some(b => b));
}
// $id - capture of discard
// _ - discard
//
// expr(pat, ...) - record ctor
// $id(pat) - nested capture
// [pat, ...] - array pat
//
// expr(expr, ...) - constant
// [expr, ...] - constant
// other - constant
pArray: Pattern<PArray> =
scope(o => {
o.type = 'PArray';
return group(
'[', mapm(bind(o, 'elements', separatedBy(this.valuePattern(), atom(','))),
v => (o.elements.every(p => p.type === 'PConstant') ? fail : succeed(v))));
});
pDict: Pattern<PDict> =
scope(o => {
o.type = 'PDict';
return group(
'{', mapm(bind(o,
'elements',
separatedBy(
scope<[Expr, ValuePattern]>(e =>
seq(bind(e, '0', this.expr(atom(':'))),
atom(':'),
bind(e, '1', this.valuePattern()))),
atom(','))),
v => (o.elements.every(e => e[1].type === 'PConstant') ? fail : succeed(v))));
});
pRawCall(... extraStops: Pattern<any>[]): Pattern<RawCall> {
return scope((o: RawCall) =>
@ -401,6 +424,8 @@ export class SyndicateParser {
return bind(o, 'binder', this.pCaptureBinder);
}),
scope(o => map(this.pDiscard, _ => o.type = 'PDiscard')),
this.pArray,
this.pDict,
mapm<RawCall, ValuePattern>(
this.pRawCall(... extraStops),
o => {
@ -424,7 +449,7 @@ export class SyndicateParser {
const argPats = o.arguments.map(a => match(this.valuePattern(), a, null));
if (argPats.some(p => p === null)) return fail;
return succeed({
type: 'PConstructor',
type: 'PRecord',
ctor: o.callee,
arguments: argPats as ValuePattern[]
});
@ -446,125 +471,43 @@ export class SyndicateTypedParser extends SyndicateParser {
//---------------------------------------------------------------------------
// Value pattern utilities
export function patternText(p: ValuePattern): Items {
switch (p.type) {
case 'PDiscard': return template`_`;
case 'PConstant': return p.value;
case 'PCapture':
{
const binderId = { ... p.binder.id, text: '$' + p.binder.id.text };
const affix =
(p.inner.type === 'PDiscard') ? [] : template`(${patternText(p.inner)})`;
if (p.binder.type !== void 0) {
return template`${[binderId]}:${p.binder.type}${affix}`;
} else {
return template`${[binderId]}${affix}`;
}
}
case 'PArray': return template`[${commaJoin(p.elements.map(patternText))}]`;
case 'PConstructor': return template`${p.ctor}(${commaJoin(p.arguments.map(patternText))})`;
}
}
export function instantiatePatternToPattern(p: ValuePattern): ValuePattern {
switch (p.type) {
case 'PDiscard': return p;
case 'PConstant': return p;
case 'PCapture': return { type: 'PConstant', value: [p.binder.id] };
case 'PArray':
return {
type: 'PArray',
elements: p.elements.map(instantiatePatternToPattern),
};
case 'PConstructor':
return {
type: 'PConstructor',
ctor: p.ctor,
arguments: p.arguments.map(instantiatePatternToPattern),
};
}
}
const eDiscard: Expr = template`(__SYNDICATE__.Discard._instance)`;
const eCapture = (e: Expr): Expr => template`(__SYNDICATE__.Capture(${e}))`;
const eDiscard: Expr = template`(__SYNDICATE__.Pattern._)`;
const eBind = (e: Expr): Expr => template`(__SYNDICATE__.Pattern.bind(${e}))`;
const eLit = (e : Expr): Expr => template`(__SYNDICATE__.Pattern.lit(${e}))`;
export function compilePattern(pattern: ValuePattern): StaticAnalysis {
const constPaths: Path[] = [];
const constVals: Expr[] = [];
const capturePaths: Path[] = [];
const captureBinders: Binder[] = [];
const currentPath: Path = [];
function walk(pattern: ValuePattern): [Skeleton<Expr>, Expr] {
function walk(pattern: ValuePattern): Expr {
switch (pattern.type) {
case 'PDiscard':
return [null, eDiscard];
return eDiscard;
case 'PCapture': {
capturePaths.push(currentPath.slice());
captureBinders.push(pattern.binder);
const [s, a] = walk(pattern.inner);
return [s, eCapture(a)];
return eBind(walk(pattern.inner));
}
case 'PConstant':
constPaths.push(currentPath.slice());
constVals.push(pattern.value);
return [null, pattern.value];
case 'PConstructor': {
const skel: Skeleton<Expr> = {
shape: template`__SYNDICATE__.Skeleton.constructorInfoSignature((${pattern.ctor}).constructorInfo)`,
members: [],
};
const assertionArgs: Expr[] = [];
pattern.arguments.forEach((argPat, i) => {
currentPath.push(i);
const [s, a] = walk(argPat);
skel.members.push(s);
assertionArgs.push(a);
currentPath.pop();
});
return [skel, template`(${pattern.ctor}(${commaJoin(assertionArgs)}))`];
return eLit(pattern.value);
case 'PRecord': {
const pieces = [template`(${pattern.ctor}).constructorInfo.label`,
... pattern.arguments.map(walk)];
return template`(__SYNDICATE__.Pattern.rec(${commaJoin(pieces)}))`;
}
case 'PArray': {
const skel: Skeleton<Expr> = {
shape: [ {
start: startPos(null),
end: startPos(null),
type: TokenType.STRING,
text: JSON.stringify(pattern.elements.length.toString()),
} ],
members: []
};
const elements: Expr[] = [];
pattern.elements.forEach((elemPat, i) => {
currentPath.push(i);
const [s, a] = walk(elemPat);
skel.members.push(s);
elements.push(a);
currentPath.pop();
});
return [skel, template`[${commaJoin(elements)}]`];
const pieces = pattern.elements.map(walk);
return template`(__SYNDICATE__.Pattern.arr(${commaJoin(pieces)}))`;
}
case 'PDict': {
const pieces = pattern.elements.map(([k, v]) => template`[${k}, ${walk(v)}]`);
return template`(__SYNDICATE__.Pattern.dict(${commaJoin(pieces)}))`;
}
}
}
const [skeletonStructure, assertion] = walk(pattern);
const skeleton = renderSkeleton(skeletonStructure);
const skeleton = walk(pattern);
return {
skeleton,
constPaths,
constVals,
capturePaths,
captureBinders,
assertion,
};
}
function renderSkeleton(skel: Skeleton<Expr>): Expr {
if (skel === null) {
return template`null`;
} else {
return template`({shape:${skel.shape}, members: [${commaJoin(skel.members.map(renderSkeleton))}]})`;
}
}

View File

@ -2,6 +2,5 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
export * as Grammar from './grammar.js';
export * as Internals from './internals.js';
export * as Codegen from './codegen.js';
export { compile, CompileOptions } from './codegen.js';

View File

@ -1,9 +0,0 @@
/// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
export const BootProc = '__SYNDICATE__bootProc';
// Keep these definitions in sync with api.ts from the core package
//
export type NonEmptySkeleton<Shape> = { shape: Shape, members: Skeleton<Shape>[] };
export type Skeleton<Shape> = null | NonEmptySkeleton<Shape>;

View File

@ -4,7 +4,7 @@
import {
Pattern as P,
Observe, fromObserve,
assertObserve,
Record,
Actor, Dataspace,
} from '..';
@ -41,38 +41,29 @@ Actor.boot(t => {
}
});
t.assert(ds, fromObserve(Observe({
pattern: P.rec(SetBox.constructorInfo.label, P.bind()),
observer: t.ref({
message(_t, [v]) {
boxValue.value = v;
// console.log('box updated value', v);
}
})
})));
assertObserve(t, ds, P.rec(SetBox.constructorInfo.label, P.bind()), {
message(_t, [v]) {
boxValue.value = v;
// console.log('box updated value', v);
}
});
});
t.spawn(t => {
t.activeFacet.actor.name = 'client';
t.assert(ds, fromObserve(Observe({
pattern: P.rec(BoxState.constructorInfo.label, P.bind()),
observer: t.ref({
assert(t, [v], _handle) {
// console.log('client sending SetBox', v + 1);
t.message(ds, SetBox(v + 1));
}
})
})));
assertObserve(t, ds, P.rec(BoxState.constructorInfo.label, P.bind()), {
assert(t, [v], _handle) {
// console.log('client sending SetBox', v + 1);
t.message(ds, SetBox(v + 1));
}
});
t.assert(ds, fromObserve(Observe({
pattern: P.rec(BoxState.constructorInfo.label, P._),
observer: t.ref({
retract(_t) {
console.log('box gone');
console.timeEnd('box-and-client-' + N.toString());
}
})
})));
assertObserve(t, ds, P.rec(BoxState.constructorInfo.label, P._), {
retract(_t) {
console.log('box gone');
console.timeEnd('box-and-client-' + N.toString());
}
});
});
});

View File

@ -4,7 +4,7 @@
import {
Pattern as P,
Observe, fromObserve,
assertObserve,
Record,
Actor, Dataspace,
} from '..';
@ -41,38 +41,29 @@ Actor.boot(t => {
}
});
t.assert(ds, fromObserve(Observe({
pattern: P.rec(SetBox.constructorInfo.label, P.bind()),
observer: t.ref({
message(_t, [v]: [number]) {
boxValue.value = v;
// console.log('box updated value', v);
}
})
})));
assertObserve(t, ds, P.rec(SetBox.constructorInfo.label, P.bind()), {
message(_t, [v]: [number]) {
boxValue.value = v;
// console.log('box updated value', v);
}
});
});
t.spawn(t => {
t.activeFacet.actor.name = 'client';
t.assert(ds, fromObserve(Observe({
pattern: P.rec(BoxState.constructorInfo.label, P.bind()),
observer: t.ref({
assert(t, [v]: [number], _handle) {
// console.log('client sending SetBox', v + 1);
t.message(ds, SetBox(v + 1));
}
})
})));
assertObserve(t, ds, P.rec(BoxState.constructorInfo.label, P.bind()), {
assert(t, [v]: [number], _handle) {
// console.log('client sending SetBox', v + 1);
t.message(ds, SetBox(v + 1));
}
});
t.assert(ds, fromObserve(Observe({
pattern: P.rec(BoxState.constructorInfo.label, P._),
observer: t.ref({
retract(_t) {
console.log('box gone');
console.timeEnd('box-and-client-' + N.toString());
}
})
})));
assertObserve(t, ds, P.rec(BoxState.constructorInfo.label, P._), {
retract(_t) {
console.log('box gone');
console.timeEnd('box-and-client-' + N.toString());
}
});
});
});

View File

@ -129,6 +129,10 @@ export class Facet {
this.outbound = initialAssertions;
}
turn(a: LocalAction) {
Turn.for(this, a);
}
onStop(a: LocalAction): void {
this.shutdownActions.push(a);
}
@ -251,6 +255,11 @@ export class Turn {
return newFacet;
}
// Alias for syndicatec code generator to use
_stop(facet: Facet = this.activeFacet, continuation?: LocalAction) {
this.stop(facet, continuation);
}
stop(facet: Facet = this.activeFacet, continuation?: LocalAction) {
this.enqueue(facet.parent!, t => {
facet._terminate(t, true);
@ -258,11 +267,16 @@ export class Turn {
});
}
spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
this._spawn(bootProc, initialAssertions);
// Alias for syndicatec code generator to use
_spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
this.spawn(bootProc, initialAssertions);
}
_spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): Actor {
spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
this.__spawn(bootProc, initialAssertions);
}
__spawn(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): Actor {
const newOutbound: OutboundMap = new Map();
initialAssertions.forEach(key => newOutbound.set(key, this.activeFacet.outbound.get(key)!));
// ^ we trust initialAssertions, so can use `!` safely
@ -275,9 +289,14 @@ export class Turn {
return newActor;
}
// Alias for syndicatec code generator to use
_spawnLink(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
this.spawnLink(bootProc, initialAssertions);
}
spawnLink(bootProc: LocalAction, initialAssertions = new IdentitySet<Handle>()): void {
if (!this.activeFacet.isLive) return;
const newActor = this._spawn(bootProc, initialAssertions);
const newActor = this.__spawn(bootProc, initialAssertions);
this.activeFacet._halfLink(newActor.root);
newActor.root._halfLink(this.activeFacet);
}
@ -294,6 +313,11 @@ export class Turn {
return new Field(this.activeFacet.actor.dataflowGraph, initial, name);
}
// Alias for syndicatec code generator to use
_dataflow(a: LocalAction) {
this.dataflow(a);
}
dataflow(a: LocalAction) {
const f = this.activeFacet;
const b = (t: Turn) => f.isLive && t._inFacet(f, a);
@ -385,6 +409,10 @@ export class Turn {
queueTask(() => Turn.for(actor.root, t => q.forEach(f => f(t)))));
this.queues = null;
}
withSelfDo(a: LocalAction) {
a(this);
}
}
function stopIfInertAfter(a: LocalAction): LocalAction {

View File

@ -4,7 +4,7 @@
// Property-based "dataflow"
import { FlexSet, FlexMap, Canonicalizer, Value, is } from '@preserves/core';
import { Ref } from 'index.js';
import { Ref } from './actor.js';
import * as MapSet from './mapset.js';
export interface ObservingGraph<ObjectId> {

View File

@ -3,8 +3,9 @@
import { IdentityMap } from '@preserves/core';
import { Index } from './skeleton.js';
import { Assertion, Entity, Handle, Turn } from './actor.js';
import { Observe, toObserve } from '../gen/dataspace.js';
import { Assertion, Entity, Facet, Handle, LocalAction, Ref, Turn } from './actor.js';
import { fromObserve, Observe, toObserve } from '../gen/dataspace.js';
import * as P from '../gen/dataspacePatterns.js';
export class Dataspace implements Partial<Entity> {
readonly index = new Index();
@ -33,3 +34,40 @@ export class Dataspace implements Partial<Entity> {
this.index.deliverMessage(turn, v);
}
}
export function assertionObserver(f: (t: Turn, a: Assertion) => LocalAction | undefined): Partial<Entity> {
const assertionMap = new IdentityMap<Handle, LocalAction>();
return {
assert(t: Turn, a: Assertion, h: Handle): void {
const g = f(t, a) ?? null;
if (g !== null) {
assertionMap.set(h, g);
}
},
retract(t: Turn, h: Handle): void {
assertionMap.get(h)?.(t);
assertionMap.delete(h);
},
};
}
export function assertionFacetObserver(f: (t: Turn, a: Assertion) => void, inertOk: boolean = true): Partial<Entity> {
const facetMap = new IdentityMap<Handle, Facet>();
return {
assert(t: Turn, a: Assertion, h: Handle): void {
facetMap.set(h, t.facet(t => {
if (inertOk) t.activeFacet.preventInertCheck();
f(t, a);
}));
},
retract(t: Turn, h: Handle): void {
const facet = facetMap.get(h);
if (facet) t.stop(facet);
facetMap.delete(h);
},
};
}
export function assertObserve(t: Turn, ds: Ref, pattern: P.Pattern, e: Partial<Entity>): Handle {
return t.assert(ds, fromObserve(Observe({ pattern, observer: t.ref(e) })));
}

View File

@ -8,6 +8,6 @@
<main id="main">
</main>
<script>
Syndicate.bootModule(Main.__SYNDICATE__bootProc);
Main.boot();
</script>
</html>

View File

@ -16,7 +16,7 @@
},
"devDependencies": {
"@syndicate-lang/syndicatec": "^0.2.0",
"rollup": "^2.37.0",
"rollup-plugin-sourcemaps": "^0.6.3"
"rollup": "^2.60",
"rollup-plugin-sourcemaps": "^0.6"
}
}

View File

@ -3,12 +3,15 @@
import { BoxState, SetBox, N } from './protocol.js';
boot {
boot(ds) {
spawn named 'box' {
field value = 0;
assert BoxState(this.value);
stop on (this.value === N)
field boxValue = 0;
at ds {
assert BoxState(boxValue.value);
on message SetBox($v) => boxValue.value = v;
}
stop on (boxValue.value === N) {
console.log('terminated box root facet');
on message SetBox($v) => this.value = v;
}
}
}

View File

@ -3,9 +3,14 @@
import { BoxState, SetBox } from './protocol.js';
boot {
boot(ds, doneCallback) {
spawn named 'client' {
on asserted BoxState($v) => send message SetBox(v + 1);
on retracted BoxState(_) => console.log('box gone');
at ds {
on asserted BoxState($v) => send message SetBox(v + 1);
on retracted BoxState(_) => {
console.log('box gone');
doneCallback();
}
}
}
}

View File

@ -2,11 +2,14 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { N } from './protocol.js';
activate import './box.js';
activate import './client.js';
import * as Box from './box.js';
import * as Client from './client.js';
import { Dataspace } from '@syndicate-lang/core';
console.time('box-and-client-' + N.toString());
boot {
thisFacet.actor.dataspace.ground().addStopHandler(() =>
console.timeEnd('box-and-client-' + N.toString()));
boot() {
thisTurn.activeFacet.preventInertCheck();
const ds = create new Dataspace();
Box.boot(ds);
Client.boot(ds, () => console.timeEnd('box-and-client-' + N.toString()));
}

View File

@ -8,6 +8,6 @@
<main id="main">
</main>
<script>
Syndicate.bootModule(Main.__SYNDICATE__bootProc);
Syndicate.Actor.boot(Main.boot);
</script>
</html>

View File

@ -16,8 +16,8 @@
},
"devDependencies": {
"@syndicate-lang/syndicatec": "^0.2.0",
"rollup": "^2.37.0",
"rollup-plugin-sourcemaps": "^0.6.3",
"typescript": "^4.1.3"
"rollup": "^2.60",
"rollup-plugin-sourcemaps": "^0.6",
"typescript": "^4.5"
}
}

View File

@ -2,13 +2,17 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { BoxState, SetBox, N } from './protocol.js';
import { Ref } from '@syndicate-lang/core';
boot {
boot(ds: Ref) {
spawn named 'box' {
field value: number = 0;
assert BoxState(this.value);
stop on (this.value === N)
field boxValue: number = 0;
at ds {
assert BoxState(boxValue.value);
on message SetBox($v: number) => boxValue.value = v;
}
stop on (boxValue.value === N) {
console.log('terminated box root facet');
on message SetBox($v: number) => this.value = v;
}
}
}

View File

@ -2,10 +2,16 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { BoxState, SetBox } from './protocol.js';
import { Ref } from '@syndicate-lang/core';
boot {
boot(ds: Ref, doneCallback: () => void) {
spawn named 'client' {
on asserted BoxState($v: number) => send message SetBox(v + 1);
on retracted BoxState(_) => console.log('box gone');
at ds {
on asserted BoxState($v: number) => send message SetBox(v + 1);
on retracted BoxState(_) => {
console.log('box gone');
doneCallback();
}
}
}
}

View File

@ -2,11 +2,14 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { N } from './protocol.js';
activate import './box.js';
activate import './client.js';
import * as Box from './box.js';
import * as Client from './client.js';
import { Dataspace } from '@syndicate-lang/core';
console.time('box-and-client-' + N.toString());
boot {
thisFacet.actor.dataspace.ground().addStopHandler(() =>
console.timeEnd('box-and-client-' + N.toString()));
boot() {
thisTurn.activeFacet.preventInertCheck();
const ds = create new Dataspace();
Box.boot(thisTurn, ds);
Client.boot(thisTurn, ds, () => console.timeEnd('box-and-client-' + N.toString()));
}

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>
export assertion type BoxState(value);
export message type SetBox(newValue);
export assertion type BoxState(value: number);
export message type SetBox(newValue: number);
export const N = 100000;