Change `stop` to refer to a lexical facet, not a dynamic facet.
The previous behaviour of `stop` was inappropriate: it always stopped `Syndicate.Turn.activeFacet`, which is an instance of dynamic binding. Now, it instead stops the unique lexically-innermost lexically-apparent facet - the special name `currentSyndicateFacet` - by default. It is an error if no such facet is lexically apparent (if `currentSyndicateFacet` is unbound). This makes it similar to `break` and `continue` in structured programming. In addition, an expression denoting a facet can now be used with `stop` to override this default - again, like `break` and `continue` in some languages. Finally, `react` can now be preceded by a label, which binds the label as a variable denoting the newly-created facet (inside the facet's scope).
This commit is contained in:
parent
644891ce76
commit
b4d728ca7a
|
@ -17,6 +17,7 @@ import {
|
||||||
|
|
||||||
compilePattern,
|
compilePattern,
|
||||||
SpawnStatement,
|
SpawnStatement,
|
||||||
|
FacetToStop,
|
||||||
} from './grammar';
|
} from './grammar';
|
||||||
|
|
||||||
export function stripShebang(items: Items): Items {
|
export function stripShebang(items: Items): Items {
|
||||||
|
@ -116,16 +117,39 @@ function binderTypeGuard(t: TemplateFunction): (binder: Binder, index: number) =
|
||||||
export function expand(tree: Items, ctx: ExpansionContext): Items {
|
export function expand(tree: Items, ctx: ExpansionContext): Items {
|
||||||
const macro = new Templates(undefined, { extraDelimiters: ':' });
|
const macro = new Templates(undefined, { extraDelimiters: ':' });
|
||||||
|
|
||||||
function terminalWrap(t: TemplateFunction, isTerminal: boolean, body: Statement): Statement {
|
function terminalWrap(
|
||||||
if (isTerminal) {
|
t: TemplateFunction,
|
||||||
return t`__SYNDICATE__.Turn.active._stop(__SYNDICATE__.Turn.activeFacet, () => {${body}})`
|
facetToStop: FacetToStop | 'none' | 'once-wrapper',
|
||||||
|
body: Statement,
|
||||||
|
): Statement {
|
||||||
|
if (facetToStop === 'none') {
|
||||||
|
return walk(body);
|
||||||
} else {
|
} else {
|
||||||
return body;
|
const toStop =
|
||||||
|
facetToStop === 'default' ? 'currentSyndicateFacet' :
|
||||||
|
facetToStop === 'once-wrapper' ? '__once_facet' :
|
||||||
|
walk(facetToStop);
|
||||||
|
const resetCurrentSyndicateFacet =
|
||||||
|
facetToStop === 'once-wrapper' ? [] :
|
||||||
|
t`const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;`;
|
||||||
|
return t`__SYNDICATE__.Turn.active._stop(${toStop}, () => {${resetCurrentSyndicateFacet}${walk(body)}})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function facetWrap(t: TemplateFunction, items: Items): Items {
|
function facetWrap(
|
||||||
return t`__SYNDICATE__.Turn.active.facet(() => {${items}})`;
|
t: TemplateFunction,
|
||||||
|
facetName: Identifier | 'default' | 'once-wrapper',
|
||||||
|
items: Items,
|
||||||
|
): Items {
|
||||||
|
if (facetName === 'once-wrapper') {
|
||||||
|
return t`__SYNDICATE__.Turn.active.facet(() => {const __once_facet = __SYNDICATE__.Turn.activeFacet; ${items}});`;
|
||||||
|
} else {
|
||||||
|
const defaultLabel = t`const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; `;
|
||||||
|
const customLabel = facetName === 'default'
|
||||||
|
? []
|
||||||
|
: t`const ${facetName.text} = currentSyndicateFacet; `;
|
||||||
|
return t`__SYNDICATE__.Turn.active.facet(() => {${defaultLabel}${customLabel}${items}});`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function x<T>(p: Pattern<T>, f: (v: T, t: TemplateFunction) => Items) {
|
function x<T>(p: Pattern<T>, f: (v: T, t: TemplateFunction) => Items) {
|
||||||
|
@ -155,7 +179,7 @@ export function expand(tree: Items, ctx: ExpansionContext): Items {
|
||||||
|
|
||||||
let body = (spawn === null)
|
let body = (spawn === null)
|
||||||
? walk(s.body)
|
? walk(s.body)
|
||||||
: expandSpawn(spawn, t, t`__SYNDICATE__.Turn.activeFacet.preventInertCheck();`);
|
: expandSpawn(spawn, t, t` __SYNDICATE__.Turn.activeFacet.preventInertCheck();`);
|
||||||
|
|
||||||
const sa = compilePattern(s.pattern);
|
const sa = compilePattern(s.pattern);
|
||||||
const assertion = t`__SYNDICATE__.Observe({
|
const assertion = t`__SYNDICATE__.Observe({
|
||||||
|
@ -185,8 +209,9 @@ ${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
|
||||||
? t`, new __SYNDICATE__.Set([${commaJoin(s.initialAssertions.map(walk))}])`
|
? t`, new __SYNDICATE__.Set([${commaJoin(s.initialAssertions.map(walk))}])`
|
||||||
: ``;
|
: ``;
|
||||||
*/
|
*/
|
||||||
const n = spawn.name === void 0 ? '' : t` __SYNDICATE__.Turn.activeFacet.actor.name = ${walk(spawn.name)};`;
|
const f = t` const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;`;
|
||||||
return t`__SYNDICATE__.Dataspace._spawn${spawn.linkedToken ? 'Link': ''}(() => {${n} ${inject} ${walk(spawn.body)} });`;
|
const n = spawn.name === void 0 ? '' : t` currentSyndicateFacet.actor.name = ${walk(spawn.name)};`;
|
||||||
|
return t`__SYNDICATE__.Dataspace._spawn${spawn.linkedToken ? 'Link': ''}(() => {${f}${n}${inject}${walk(spawn.body)}});`;
|
||||||
}
|
}
|
||||||
|
|
||||||
x(ctx.parser.spawn, expandSpawn);
|
x(ctx.parser.spawn, expandSpawn);
|
||||||
|
@ -226,10 +251,10 @@ ${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
|
||||||
t`_dataflow(() => {${walk(s.body)}});`);
|
t`_dataflow(() => {${walk(s.body)}});`);
|
||||||
|
|
||||||
x(ctx.parser.eventHandlerEndpointStatement, (s, t) => {
|
x(ctx.parser.eventHandlerEndpointStatement, (s, t) => {
|
||||||
const wrap = s.once ? (i: Items) => facetWrap(t, i) : (i: Items) => i;
|
const wrap = s.once ? (i: Items) => facetWrap(t, 'once-wrapper', i) : (i: Items) => i;
|
||||||
|
|
||||||
if (s.triggerType === 'dataflow') {
|
if (s.triggerType === 'dataflow') {
|
||||||
return wrap(t`__SYNDICATE__.Turn.active._dataflow(() => { if (${walk(s.predicate)}) { ${terminalWrap(t, s.terminal, walk(s.body))} } });`);
|
return wrap(t`__SYNDICATE__.Turn.active._dataflow(() => { if (${walk(s.predicate)}) { ${terminalWrap(t, s.facetToStop, s.body)} } });`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.triggerType === 'stop') {
|
if (s.triggerType === 'stop') {
|
||||||
|
@ -247,19 +272,19 @@ ${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
|
||||||
case 'asserted':
|
case 'asserted':
|
||||||
entity = t`{
|
entity = t`{
|
||||||
assert: (${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}, ${ctx.argDecl(t, '__handle', '__SYNDICATE__.Handle')}) => {
|
assert: (${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}, ${ctx.argDecl(t, '__handle', '__SYNDICATE__.Handle')}) => {
|
||||||
${guardBody(terminalWrap(t, s.terminal, walk(s.body)))}
|
${guardBody(terminalWrap(t, s.facetToStop, s.body))}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
break;
|
break;
|
||||||
case 'retracted':
|
case 'retracted':
|
||||||
entity = t`__SYNDICATE__.assertionObserver((${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) => {
|
entity = t`__SYNDICATE__.assertionObserver((${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) => {
|
||||||
${guardBody(t`return () => { ${terminalWrap(t, s.terminal, walk(s.body))} };`)}
|
${guardBody(t`return () => { ${terminalWrap(t, s.facetToStop, s.body)} };`)}
|
||||||
})`;
|
})`;
|
||||||
break;
|
break;
|
||||||
case 'message':
|
case 'message':
|
||||||
entity = t`{
|
entity = t`{
|
||||||
message: (${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) => {
|
message: (${ctx.argDecl(t, '__vs', '__SYNDICATE__.AnyValue')}) => {
|
||||||
${guardBody(terminalWrap(t, s.terminal, walk(s.body)))}
|
${guardBody(terminalWrap(t, s.facetToStop, s.body))}
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
break;
|
break;
|
||||||
|
@ -294,10 +319,9 @@ ${joinItems(sa.captureBinders.map(binderTypeGuard(t)), '\n')}
|
||||||
|
|
||||||
xf(ctx.parser.messageSendStatement, (s, t) => t`message(currentSyndicateTarget, ${walk(s.expr)});`);
|
xf(ctx.parser.messageSendStatement, (s, t) => t`message(currentSyndicateTarget, ${walk(s.expr)});`);
|
||||||
|
|
||||||
x(ctx.parser.reactStatement, (s, t) => facetWrap(t, s.body));
|
x(ctx.parser.reactStatement, (s, t) => facetWrap(t, s.label ?? 'default', s.body));
|
||||||
|
|
||||||
x(ctx.parser.stopStatement, (s, t) =>
|
x(ctx.parser.stopStatement, (s, t) => t`${terminalWrap(t, s.facetToStop, s.body)};`);
|
||||||
t`__SYNDICATE__.Turn.active._stop(__SYNDICATE__.Turn.activeFacet, () => {${walk(s.body)}});`)
|
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
Pattern,
|
Pattern,
|
||||||
foldItems, match, anonymousTemplate as template, commaJoin,
|
foldItems, match, anonymousTemplate as template, commaJoin,
|
||||||
|
|
||||||
scope, bind, seq, alt, upTo, atom, atomString, group,
|
scope, bind, seq, seqTuple, alt, upTo, atom, atomString, group,
|
||||||
repeat, option, withoutSpace, map, mapm, rest, discard,
|
repeat, option, withoutSpace, map, mapm, rest, discard,
|
||||||
value, succeed, fail, separatedOrTerminatedBy, not,
|
value, succeed, fail, separatedOrTerminatedBy, not,
|
||||||
} from '../syntax/index';
|
} from '../syntax/index';
|
||||||
|
@ -50,8 +50,14 @@ export interface StatementTurnAction extends TurnAction {
|
||||||
body: Statement;
|
body: Statement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FacetToStop = 'default' | Expr;
|
||||||
|
|
||||||
|
export interface StopStatement extends StatementTurnAction {
|
||||||
|
facetToStop: FacetToStop;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GenericEventEndpointStatement extends StatementTurnAction {
|
export interface GenericEventEndpointStatement extends StatementTurnAction {
|
||||||
terminal: boolean;
|
facetToStop: FacetToStop | 'none' | 'once-wrapper';
|
||||||
once: boolean;
|
once: boolean;
|
||||||
isDynamic: boolean;
|
isDynamic: boolean;
|
||||||
}
|
}
|
||||||
|
@ -90,6 +96,7 @@ export interface DuringStatement extends FacetSetupAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReactStatement extends FacetSetupAction {
|
export interface ReactStatement extends FacetSetupAction {
|
||||||
|
label: Identifier | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AtStatement {
|
export interface AtStatement {
|
||||||
|
@ -184,6 +191,10 @@ export class SyndicateParser {
|
||||||
return withoutSpace(upTo(alt(this.exprBoundary, ... extraStops)));
|
return withoutSpace(upTo(alt(this.exprBoundary, ... extraStops)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr1(... extraStops: Pattern<any>[]): Pattern<Expr> {
|
||||||
|
return mapm(this.expr(... extraStops), e => e.length ? succeed(e) : fail);
|
||||||
|
}
|
||||||
|
|
||||||
propertyNameExpr(): Pattern<Expr> {
|
propertyNameExpr(): Pattern<Expr> {
|
||||||
const dq = template`"`;
|
const dq = template`"`;
|
||||||
return alt<Expr>(
|
return alt<Expr>(
|
||||||
|
@ -271,22 +282,25 @@ export class SyndicateParser {
|
||||||
|
|
||||||
mandatoryIfNotTerminal(o: GenericEventEndpointStatement, p: Pattern<any>): Pattern<any> {
|
mandatoryIfNotTerminal(o: GenericEventEndpointStatement, p: Pattern<any>): Pattern<any> {
|
||||||
return i => {
|
return i => {
|
||||||
return (o.terminal) ? option(p)(i) : p(i);
|
return (o.facetToStop !== 'none') ? option(p)(i) : p(i);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Principal: Turn
|
// Principal: Turn
|
||||||
readonly eventHandlerEndpointStatement: Pattern<EventHandlerEndpointStatement> =
|
readonly eventHandlerEndpointStatement: Pattern<EventHandlerEndpointStatement> =
|
||||||
this.turnAction(o => {
|
this.turnAction(o => {
|
||||||
o.terminal = false;
|
o.facetToStop = 'none';
|
||||||
o.once = false;
|
o.once = false;
|
||||||
o.isDynamic = true;
|
o.isDynamic = true;
|
||||||
o.body = [];
|
o.body = [];
|
||||||
return seq(alt(seq(option(map(atom('stop'), _ => o.terminal = true)),
|
return seq(alt(seq(option(seq(atom('stop'),
|
||||||
|
map(option(this.expr1(atom('on'))), es => {
|
||||||
|
o.facetToStop = es.length ? es[0] : 'default';
|
||||||
|
}))),
|
||||||
atom('on')),
|
atom('on')),
|
||||||
map(atom('once'), _ => {
|
map(atom('once'), _ => {
|
||||||
o.terminal = true;
|
|
||||||
o.once = true;
|
o.once = true;
|
||||||
|
o.facetToStop = 'once-wrapper';
|
||||||
})),
|
})),
|
||||||
alt<any>(seq(map(group('(', bind(o as DataflowEndpointStatement, 'predicate',
|
alt<any>(seq(map(group('(', bind(o as DataflowEndpointStatement, 'predicate',
|
||||||
this.expr())),
|
this.expr())),
|
||||||
|
@ -294,7 +308,7 @@ export class SyndicateParser {
|
||||||
this.mandatoryIfNotTerminal(o, this.statement(o.body))),
|
this.mandatoryIfNotTerminal(o, this.statement(o.body))),
|
||||||
mapm(seq(bind(o, 'triggerType', atomString('stop')),
|
mapm(seq(bind(o, 'triggerType', atomString('stop')),
|
||||||
option(this.statement(o.body))),
|
option(this.statement(o.body))),
|
||||||
v => o.terminal ? fail : succeed(v)),
|
v => ((o.facetToStop !== 'none') || o.once) ? fail : succeed(v)),
|
||||||
seq(bind(o, 'triggerType',
|
seq(bind(o, 'triggerType',
|
||||||
alt(atomString('asserted'),
|
alt(atomString('asserted'),
|
||||||
atomString('retracted'),
|
atomString('retracted'),
|
||||||
|
@ -338,12 +352,23 @@ export class SyndicateParser {
|
||||||
// Principal: Turn
|
// Principal: Turn
|
||||||
readonly reactStatement: Pattern<ReactStatement> =
|
readonly reactStatement: Pattern<ReactStatement> =
|
||||||
this.turnAction(o => {
|
this.turnAction(o => {
|
||||||
|
o.label = null;
|
||||||
o.body = [];
|
o.body = [];
|
||||||
return seq(atom('react'), this.block(o.body));
|
return seq(option(map(seqTuple(this.identifier, atom(':')),
|
||||||
|
([i, _colon]) => o.label = i)),
|
||||||
|
atom('react'),
|
||||||
|
this.block(o.body));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Principal: Turn
|
// Principal: Turn
|
||||||
readonly stopStatement = this.blockTurnAction(atom('stop'));
|
readonly stopStatement: Pattern<StopStatement> =
|
||||||
|
this.turnAction(o => {
|
||||||
|
o.facetToStop = 'default';
|
||||||
|
o.body = [];
|
||||||
|
return seq(atom('stop'),
|
||||||
|
option(map(this.expr1(), e => o.facetToStop = e)),
|
||||||
|
alt(this.block(o.body), this.statementBoundary));
|
||||||
|
});
|
||||||
|
|
||||||
// Principal: none
|
// Principal: none
|
||||||
readonly atStatement: Pattern<AtStatement> =
|
readonly atStatement: Pattern<AtStatement> =
|
||||||
|
|
|
@ -14,6 +14,8 @@ import { List, ArrayList, atEnd, notAtEnd } from './list';
|
||||||
export type PatternResult<T> = [T, List<Item>] | null;
|
export type PatternResult<T> = [T, List<Item>] | null;
|
||||||
export type Pattern<T> = (i: List<Item>) => PatternResult<T>;
|
export type Pattern<T> = (i: List<Item>) => PatternResult<T>;
|
||||||
|
|
||||||
|
export type PatternTypeArg<P> = P extends Pattern<infer T> ? T : never;
|
||||||
|
|
||||||
export function match<T,F>(p: Pattern<T>, items: Items, failure: F): T | F {
|
export function match<T,F>(p: Pattern<T>, items: Items, failure: F): T | F {
|
||||||
const r = p(new ArrayList(items));
|
const r = p(new ArrayList(items));
|
||||||
if (r === null) return failure;
|
if (r === null) return failure;
|
||||||
|
@ -78,6 +80,22 @@ export function seq(... patterns: Pattern<any>[]): Pattern<any> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function seqTuple<Patterns extends [...Pattern<any>[]]>(
|
||||||
|
... patterns: Patterns
|
||||||
|
): Pattern<{ [I in keyof Patterns]: PatternTypeArg<Patterns[I]> } & { length: Patterns['length'] }>
|
||||||
|
{
|
||||||
|
return i => {
|
||||||
|
const rs = [];
|
||||||
|
for (const p of patterns) {
|
||||||
|
const r = p(i);
|
||||||
|
if (r === null) return null;
|
||||||
|
rs.push(r[0]);
|
||||||
|
i = r[1];
|
||||||
|
}
|
||||||
|
return [rs as unknown as PatternTypeArg<ReturnType<typeof seqTuple<Patterns>>>, i];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function alt<T>(... alts: Pattern<T>[]): Pattern<T> {
|
export function alt<T>(... alts: Pattern<T>[]): Pattern<T> {
|
||||||
return i => {
|
return i => {
|
||||||
for (const a of alts) {
|
for (const a of alts) {
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
/// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/// SPDX-FileCopyrightText: Copyright © 2024 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
|
||||||
|
import { compile, CompileOptions, Syntax } from '../src/index';
|
||||||
|
import Pos = Syntax.Pos;
|
||||||
|
import './test-utils';
|
||||||
|
|
||||||
|
type Error = { message: string, start: Pos | undefined, end: Pos | undefined };
|
||||||
|
|
||||||
|
function translate(source: string, options: Partial<CompileOptions> = {}): { code: string, errors: Error[] } {
|
||||||
|
const errors: Error[] = [];
|
||||||
|
const result = compile({
|
||||||
|
... options,
|
||||||
|
module: 'none',
|
||||||
|
source,
|
||||||
|
emitError: (message, start, end) => errors.push({ message, start, end }),
|
||||||
|
});
|
||||||
|
return { code: result.text, errors };
|
||||||
|
}
|
||||||
|
|
||||||
|
function translateNoErrors(source: string): string {
|
||||||
|
const o = translate(source);
|
||||||
|
expect(o.errors.length).toBe(0);
|
||||||
|
return o.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('react', () => {
|
||||||
|
it('without label', () => {
|
||||||
|
expect(translateNoErrors(`react { a; b; c; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active.facet(() => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; a; b; c; });`);
|
||||||
|
});
|
||||||
|
it('with label', () => {
|
||||||
|
expect(translateNoErrors(`someLabel: react { a; b; c; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active.facet(() => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; const someLabel = currentSyndicateFacet; a; b; c; });`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('spawn', () => {
|
||||||
|
it('without name', () => {
|
||||||
|
expect(translateNoErrors(`spawn { a; b; c; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Dataspace._spawn(() => { const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; a; b; c; });`);
|
||||||
|
});
|
||||||
|
it('with name', () => {
|
||||||
|
expect(translateNoErrors(`spawn named 'foo' { a; b; c; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Dataspace._spawn(() => { const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; currentSyndicateFacet.actor.name = 'foo'; a; b; c; });`);
|
||||||
|
});
|
||||||
|
it('with missing name (known incorrect parsing and codegen)', () => {
|
||||||
|
// At present, the expr() parser accepts *empty input*. TODO: something better.
|
||||||
|
expect(translateNoErrors(`spawn named { a; b; c; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Dataspace._spawn(() => { const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; currentSyndicateFacet.actor.name = ; a; b; c; });`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('stop', () => {
|
||||||
|
it('non-statement', () => {
|
||||||
|
expect(translateNoErrors(`stop`)).toBe(
|
||||||
|
`stop`);
|
||||||
|
});
|
||||||
|
it('without facet, without body', () => {
|
||||||
|
expect(translateNoErrors(`stop;`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;});`);
|
||||||
|
});
|
||||||
|
it('without facet, empty body', () => {
|
||||||
|
expect(translateNoErrors(`stop {}`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;});`);
|
||||||
|
});
|
||||||
|
it('without facet, non-empty body', () => {
|
||||||
|
expect(translateNoErrors(`stop { a; b; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; a; b; });`);
|
||||||
|
});
|
||||||
|
it('with facet, without body', () => {
|
||||||
|
expect(translateNoErrors(`stop x.y;`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(x.y, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;});`);
|
||||||
|
});
|
||||||
|
it('with facet, empty body', () => {
|
||||||
|
expect(translateNoErrors(`stop x.y {}`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(x.y, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;});`);
|
||||||
|
});
|
||||||
|
it('with facet, non-empty body', () => {
|
||||||
|
expect(translateNoErrors(`stop x.y { a; b; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(x.y, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; a; b; });`);
|
||||||
|
});
|
||||||
|
it('nested stop, no labels', () => {
|
||||||
|
expect(translateNoErrors(`stop { stop; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet; __SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;}); });`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('during', () => {
|
||||||
|
it('stop in body', () => {
|
||||||
|
expect(translateNoErrors(`during P => { a; stop; b; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active.assertDataflow(() => ({ target: currentSyndicateTarget, assertion: __SYNDICATE__.Observe({
|
||||||
|
pattern: __SYNDICATE__.QuasiValue.finish((__SYNDICATE__.QuasiValue.lit(__SYNDICATE__.fromJS(P)))),
|
||||||
|
observer: __SYNDICATE__.Turn.ref(__SYNDICATE__.assertionFacetObserver(
|
||||||
|
(__vs) => {
|
||||||
|
if (Array.isArray(__vs)) {
|
||||||
|
|
||||||
|
a; __SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;}); b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}) }));`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('once', () => {
|
||||||
|
it('basics with block', () => {
|
||||||
|
expect(translateNoErrors(`once asserted P => { a; b; }`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active.facet(() => {const __once_facet = __SYNDICATE__.Turn.activeFacet; __SYNDICATE__.Turn.active.assertDataflow(() => ({
|
||||||
|
target: currentSyndicateTarget,
|
||||||
|
assertion: __SYNDICATE__.Observe({
|
||||||
|
pattern: __SYNDICATE__.QuasiValue.finish((__SYNDICATE__.QuasiValue.lit(__SYNDICATE__.fromJS(P)))),
|
||||||
|
observer: __SYNDICATE__.Turn.ref({
|
||||||
|
assert: (__vs, __handle) => {
|
||||||
|
if (Array.isArray(__vs)) {
|
||||||
|
|
||||||
|
__SYNDICATE__.Turn.active._stop(__once_facet, () => { a; b; })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}));});`);
|
||||||
|
});
|
||||||
|
it('basics with statement', () => {
|
||||||
|
expect(translateNoErrors(`once asserted P => x;`)).toBe(
|
||||||
|
`__SYNDICATE__.Turn.active.facet(() => {const __once_facet = __SYNDICATE__.Turn.activeFacet; __SYNDICATE__.Turn.active.assertDataflow(() => ({
|
||||||
|
target: currentSyndicateTarget,
|
||||||
|
assertion: __SYNDICATE__.Observe({
|
||||||
|
pattern: __SYNDICATE__.QuasiValue.finish((__SYNDICATE__.QuasiValue.lit(__SYNDICATE__.fromJS(P)))),
|
||||||
|
observer: __SYNDICATE__.Turn.ref({
|
||||||
|
assert: (__vs, __handle) => {
|
||||||
|
if (Array.isArray(__vs)) {
|
||||||
|
|
||||||
|
__SYNDICATE__.Turn.active._stop(__once_facet, () => {x;})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
}));});`);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue