diff --git a/Makefile b/Makefile index 5f3a537..f000942 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +__ignored__ := $(shell ./setup.sh) + LERNA=./node_modules/.bin/lerna bootstrap: node_modules/lerna diff --git a/packages/compiler/src/compiler/codegen.ts b/packages/compiler/src/compiler/codegen.ts index 7c09a6e..4b20258 100644 --- a/packages/compiler/src/compiler/codegen.ts +++ b/packages/compiler/src/compiler/codegen.ts @@ -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; } -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 = []; - 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(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(p: Pattern, f: (v: T, t: TemplateFunction) => Items) { + function xf(p: Pattern, 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>(); - 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[]')} = - [${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>')}) => { - 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>` + ? 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 { diff --git a/packages/compiler/src/compiler/grammar.ts b/packages/compiler/src/compiler/grammar.ts index f3ce606..3cc55cb 100644 --- a/packages/compiler/src/compiler/grammar.ts +++ b/packages/compiler/src/compiler/grammar.ts @@ -2,17 +2,15 @@ /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones 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(pattern: (scope: T) => Pattern): Pattern { + turnAction(pattern: (scope: T) => Pattern): Pattern { 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 = - 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 = - 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 = - 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): Pattern { - return this.facetAction(o => { + blockTurnAction(kw: Pattern): Pattern { + 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): Pattern { return i => { @@ -256,9 +258,9 @@ export class SyndicateParser { }; } - // Principal: Facet + // Principal: Turn readonly eventHandlerEndpointStatement: Pattern = - 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 = - 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 = - 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 = - this.facetAction(o => { + this.turnAction(o => { o.body = []; - o.facetFields = []; return seq(atom('react'), this.block(o.body)); }); // Principal: none - readonly bootStatement: Pattern = - value(o => { - o.value = []; - return seq(atom('boot'), this.block(o.value)); + readonly bootStatement: Pattern = + 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 = - scope(o => seq(bind(o, 'activationKeyword', atom('activate')), - follows(alt(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 = + scope(o => { + o.body = []; + return seq(atom('at'), + bind(o, 'target', this.expr()), + this.statement(o.body)); + }); + + // Principal: none + readonly createExpression: Pattern = + 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 = 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 = + 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 = + 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[]): Pattern { 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( 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] { + 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 = { - 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 = { - 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 { - if (skel === null) { - return template`null`; - } else { - return template`({shape:${skel.shape}, members: [${commaJoin(skel.members.map(renderSkeleton))}]})`; - } -} diff --git a/packages/compiler/src/compiler/index.ts b/packages/compiler/src/compiler/index.ts index 99dac1b..2c678bd 100644 --- a/packages/compiler/src/compiler/index.ts +++ b/packages/compiler/src/compiler/index.ts @@ -2,6 +2,5 @@ /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones export * as Grammar from './grammar.js'; -export * as Internals from './internals.js'; export * as Codegen from './codegen.js'; export { compile, CompileOptions } from './codegen.js'; diff --git a/packages/compiler/src/compiler/internals.ts b/packages/compiler/src/compiler/internals.ts deleted file mode 100644 index bd4314c..0000000 --- a/packages/compiler/src/compiler/internals.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// SPDX-License-Identifier: GPL-3.0-or-later -/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones - -export const BootProc = '__SYNDICATE__bootProc'; - -// Keep these definitions in sync with api.ts from the core package -// -export type NonEmptySkeleton = { shape: Shape, members: Skeleton[] }; -export type Skeleton = null | NonEmptySkeleton; diff --git a/packages/core/examples/box-and-client.js b/packages/core/examples/box-and-client.js index cc94426..3e35ac7 100644 --- a/packages/core/examples/box-and-client.js +++ b/packages/core/examples/box-and-client.js @@ -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()); + } + }); }); }); diff --git a/packages/core/examples/box-and-client.ts b/packages/core/examples/box-and-client.ts index c58c304..ba0a07c 100644 --- a/packages/core/examples/box-and-client.ts +++ b/packages/core/examples/box-and-client.ts @@ -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()); + } + }); }); }); diff --git a/packages/core/src/runtime/actor.ts b/packages/core/src/runtime/actor.ts index 709fd1d..aba1732 100644 --- a/packages/core/src/runtime/actor.ts +++ b/packages/core/src/runtime/actor.ts @@ -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()): void { - this._spawn(bootProc, initialAssertions); + // Alias for syndicatec code generator to use + _spawn(bootProc: LocalAction, initialAssertions = new IdentitySet()): void { + this.spawn(bootProc, initialAssertions); } - _spawn(bootProc: LocalAction, initialAssertions = new IdentitySet()): Actor { + spawn(bootProc: LocalAction, initialAssertions = new IdentitySet()): void { + this.__spawn(bootProc, initialAssertions); + } + + __spawn(bootProc: LocalAction, initialAssertions = new IdentitySet()): 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()): void { + this.spawnLink(bootProc, initialAssertions); + } + spawnLink(bootProc: LocalAction, initialAssertions = new IdentitySet()): 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 { diff --git a/packages/core/src/runtime/dataflow.ts b/packages/core/src/runtime/dataflow.ts index 5613075..6db99a1 100644 --- a/packages/core/src/runtime/dataflow.ts +++ b/packages/core/src/runtime/dataflow.ts @@ -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 { diff --git a/packages/core/src/runtime/dataspace.ts b/packages/core/src/runtime/dataspace.ts index 83d5707..fb95ddf 100644 --- a/packages/core/src/runtime/dataspace.ts +++ b/packages/core/src/runtime/dataspace.ts @@ -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 { readonly index = new Index(); @@ -33,3 +34,40 @@ export class Dataspace implements Partial { this.index.deliverMessage(turn, v); } } + +export function assertionObserver(f: (t: Turn, a: Assertion) => LocalAction | undefined): Partial { + const assertionMap = new IdentityMap(); + 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 { + const facetMap = new IdentityMap(); + 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): Handle { + return t.assert(ds, fromObserve(Observe({ pattern, observer: t.ref(e) }))); +} diff --git a/packages/syndicatec/examples/javascript/index.html b/packages/syndicatec/examples/javascript/index.html index 13b7645..d26fc89 100644 --- a/packages/syndicatec/examples/javascript/index.html +++ b/packages/syndicatec/examples/javascript/index.html @@ -8,6 +8,6 @@
diff --git a/packages/syndicatec/examples/javascript/package.json b/packages/syndicatec/examples/javascript/package.json index a87313d..9f872f3 100644 --- a/packages/syndicatec/examples/javascript/package.json +++ b/packages/syndicatec/examples/javascript/package.json @@ -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" } } diff --git a/packages/syndicatec/examples/javascript/src/box.js b/packages/syndicatec/examples/javascript/src/box.js index 388f204..8e09165 100644 --- a/packages/syndicatec/examples/javascript/src/box.js +++ b/packages/syndicatec/examples/javascript/src/box.js @@ -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; + } } } diff --git a/packages/syndicatec/examples/javascript/src/client.js b/packages/syndicatec/examples/javascript/src/client.js index c7dac47..c0868ba 100644 --- a/packages/syndicatec/examples/javascript/src/client.js +++ b/packages/syndicatec/examples/javascript/src/client.js @@ -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(); + } + } } } diff --git a/packages/syndicatec/examples/javascript/src/index.js b/packages/syndicatec/examples/javascript/src/index.js index a8acd7a..14451a5 100644 --- a/packages/syndicatec/examples/javascript/src/index.js +++ b/packages/syndicatec/examples/javascript/src/index.js @@ -2,11 +2,14 @@ /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones 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())); } diff --git a/packages/syndicatec/examples/typescript/index.html b/packages/syndicatec/examples/typescript/index.html index 13b7645..64cb9e9 100644 --- a/packages/syndicatec/examples/typescript/index.html +++ b/packages/syndicatec/examples/typescript/index.html @@ -8,6 +8,6 @@
diff --git a/packages/syndicatec/examples/typescript/package.json b/packages/syndicatec/examples/typescript/package.json index 3771fbc..2af659e 100644 --- a/packages/syndicatec/examples/typescript/package.json +++ b/packages/syndicatec/examples/typescript/package.json @@ -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" } } diff --git a/packages/syndicatec/examples/typescript/src/box.ts b/packages/syndicatec/examples/typescript/src/box.ts index 0df308e..023386f 100644 --- a/packages/syndicatec/examples/typescript/src/box.ts +++ b/packages/syndicatec/examples/typescript/src/box.ts @@ -2,13 +2,17 @@ /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones 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; + } } } diff --git a/packages/syndicatec/examples/typescript/src/client.ts b/packages/syndicatec/examples/typescript/src/client.ts index b1765d1..791953f 100644 --- a/packages/syndicatec/examples/typescript/src/client.ts +++ b/packages/syndicatec/examples/typescript/src/client.ts @@ -2,10 +2,16 @@ /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones 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(); + } + } } } diff --git a/packages/syndicatec/examples/typescript/src/index.ts b/packages/syndicatec/examples/typescript/src/index.ts index ae242bd..b313f1e 100644 --- a/packages/syndicatec/examples/typescript/src/index.ts +++ b/packages/syndicatec/examples/typescript/src/index.ts @@ -2,11 +2,14 @@ /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones 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())); } diff --git a/packages/syndicatec/examples/typescript/src/protocol.ts b/packages/syndicatec/examples/typescript/src/protocol.ts index 551aeba..4e4ed3b 100644 --- a/packages/syndicatec/examples/typescript/src/protocol.ts +++ b/packages/syndicatec/examples/typescript/src/protocol.ts @@ -1,7 +1,7 @@ /// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones -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;