syndicate-js/packages/core/src/compiler/grammar.ts

216 lines
7.6 KiB
TypeScript

import {
Token, Items,
Pattern,
scope, bind, seq, alt, upTo, atom, group, exec,
repeat, option, withoutSpace, map, rest, discard,
value,
} from '../syntax/index.js';
import * as Matcher from '../syntax/matcher.js';
export type Expr = Items;
export type Statement = Items;
export type Identifier = Token;
export const block = (acc?: Items) =>
(acc === void 0)
? group('{', discard)
: group('{', map(rest, items => acc.push(... items)));
export const statementBoundary = alt(atom(';'), Matcher.newline, Matcher.end);
export const exprBoundary = alt(atom(';'), atom(','), group('{', discard), Matcher.end);
export const identifier: Pattern<Identifier> = atom();
export function expr(... extraStops: Pattern<any>[]): Pattern<Expr> {
return withoutSpace(upTo(alt(exprBoundary, ... extraStops)));
}
export function statement(acc: Items): Pattern<void> {
return alt(group('{', map(rest, items => acc.push(... items))),
withoutSpace(seq(map(upTo(statementBoundary), items => acc.push(... items)),
map(statementBoundary, i => i ? acc.push(i) : void 0))));
}
export interface FacetAction {
implicitFacet: boolean;
}
export function facetAction<I extends FacetAction, T extends I>(
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));
const r = p(i);
if (r === null) return null;
return [scope, r[1]];
};
}
export interface SpawnStatement extends FacetAction {
isDataspace: boolean;
name?: Expr;
initialAssertions: Expr[];
parentIds: Identifier[];
parentInits: Expr[];
bootProcBody: Statement;
}
export const spawn: Pattern<SpawnStatement> & { headerExpr: Pattern<Expr> } =
Object.assign(facetAction((o: SpawnStatement) => {
o.isDataspace = false;
o.initialAssertions = [];
o.parentIds = [];
o.parentInits = [];
o.bootProcBody = [];
return seq(atom('spawn'),
option(seq(atom('dataspace'), exec(() => o.isDataspace = true))),
option(seq(atom('named'),
bind(o, 'name', spawn.headerExpr))),
repeat(alt(seq(atom(':asserting'),
map(spawn.headerExpr, e => o.initialAssertions.push(e))),
map(scope((l: { id: Identifier, init: Expr }) =>
seq(atom(':let'),
bind(l, 'id', identifier),
atom('='),
bind(l, 'init', spawn.headerExpr))),
l => {
o.parentIds.push(l.id);
o.parentInits.push(l.init);
}))),
statement(o.bootProcBody));
}), {
headerExpr: expr(atom(':asserting'), atom(':let')),
});
export interface FieldDeclarationStatement extends FacetAction {
member: Expr;
expr?: Expr;
}
// Principal: Dataspace, but only for implementation reasons, so really Facet
export const fieldDeclarationStatement: Pattern<FieldDeclarationStatement> =
facetAction(o => seq(atom('field'),
bind(o, 'member', expr(atom('='))),
option(seq(atom('='), bind(o, 'expr', expr()))),
statementBoundary));
export interface AssertionEndpointStatement extends FacetAction {
isDynamic: boolean,
template: Expr,
test?: Expr,
}
// Principal: Facet
export const assertionEndpointStatement: Pattern<AssertionEndpointStatement> =
facetAction(o => {
o.isDynamic = true;
return seq(atom('assert'),
option(map(atom(':snapshot'), _ => o.isDynamic = false)),
bind(o, 'template', expr(seq(atom('when'), group('(', discard)))),
option(seq(atom('when'), group('(', bind(o, 'test', expr())))),
statementBoundary);
});
export interface StatementFacetAction extends FacetAction {
body: Statement;
}
export function statementFacetAction(kw: Pattern<any>): Pattern<StatementFacetAction> {
return facetAction(o => {
o.body = [];
return seq(kw, statement(o.body));
});
}
// Principal: Facet
export const dataflowStatement = statementFacetAction(atom('dataflow'));
export interface EventHandlerEndpointStatement extends FacetAction {
terminal: boolean;
triggerType: 'dataflow' | 'start' | 'stop' | 'asserted' | 'retracted' | 'message';
isDynamic: boolean;
pattern?: Expr;
body: Statement;
}
// Principal: Facet
export const eventHandlerEndpointStatement: Pattern<EventHandlerEndpointStatement> =
facetAction(o => {
o.terminal = false;
o.isDynamic = true;
o.body = [];
return seq(option(map(atom('stop'), _ => o.terminal = true)),
atom('on'),
alt(map(group('(', bind(o, 'pattern', expr())), _ => o.triggerType = 'dataflow'),
seq(bind(o, 'triggerType',
map(alt(atom('start'), atom('stop')), e => e.text)),
option(statement(o.body))),
seq(bind(o, 'triggerType',
map(alt(atom('asserted'),
atom('retracted'),
atom('message')),
e => e.text)),
option(map(atom(':snapshot'), _ => o.isDynamic = false)),
bind(o, 'pattern', expr(atom('=>'))),
alt(seq(atom('=>'), statement(o.body)),
statementBoundary))));
});
export interface TypeDefinitionStatement {
expectedUse: 'message' | 'assertion';
label: Identifier;
fields: Identifier[];
wireName?: Expr;
}
// Principal: none
export const typeDefinitionStatement: Pattern<TypeDefinitionStatement> =
scope(o => seq(bind(o, 'expectedUse', map(alt(atom('message'), atom('assertion')), e => e.text)),
atom('type'),
bind(o, 'label', identifier),
group('(', bind(o, 'fields', repeat(identifier, { separator: atom(',') }))),
option(seq(atom('='),
bind(o, 'wireName', withoutSpace(upTo(statementBoundary))))),
statementBoundary));
export interface MessageSendStatement extends FacetAction {
expr: Expr;
}
// Principal: Facet
export const messageSendStatement: Pattern<MessageSendStatement> =
facetAction(o => seq(atom('send'),
bind(o, 'expr', withoutSpace(upTo(statementBoundary))),
statementBoundary));
export interface DuringStatement extends FacetAction {
pattern: Expr;
body: Statement;
}
// Principal: Facet
export const duringStatement: Pattern<DuringStatement> =
facetAction(o => {
o.body = [];
return seq(atom('during'),
bind(o, 'pattern', expr()),
statement(o.body));
});
// Principal: Facet
export const reactStatement = statementFacetAction(atom('react'));
// Principal: none
export const bootStatement: Pattern<Statement> =
value(o => {
o.value = [];
return seq(atom('boot'), statement(o.value));
});
// Principal: Facet
export const stopStatement = statementFacetAction(atom('stop'));