214 lines
7.4 KiB
TypeScript
214 lines
7.4 KiB
TypeScript
/// 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';
|
|
import { js as format } from 'js-beautify';
|
|
|
|
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, options?: Partial<CompileOptions>): string {
|
|
const o = translate(source, options);
|
|
expect(o.errors.length).toBe(0);
|
|
return o.code;
|
|
}
|
|
|
|
function expectCodeEqual(input: string, output: string, options?: Partial<CompileOptions>) {
|
|
expect(format(translateNoErrors(input, options))).toBe(format(output));
|
|
}
|
|
|
|
describe('react', () => {
|
|
|
|
it('without label', () => expectCodeEqual(`react { a; b; c; }`, `
|
|
__SYNDICATE__.Turn.active.facet(() => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
a; b; c;
|
|
});`));
|
|
|
|
it('with label', () => expectCodeEqual(`someLabel: react { a; b; c; }`, `
|
|
__SYNDICATE__.Turn.active.facet(() => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
const someLabel = currentSyndicateFacet;
|
|
a; b; c;
|
|
});`));
|
|
|
|
});
|
|
|
|
describe('spawn', () => {
|
|
|
|
it('without name', () => expectCodeEqual(`spawn { a; b; c; }`, `
|
|
__SYNDICATE__.Dataspace._spawn(() => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
a; b; c;
|
|
});`));
|
|
|
|
it('with name', () => expectCodeEqual(`spawn named 'foo' { a; b; c; }`, `
|
|
__SYNDICATE__.Dataspace._spawn(() => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
currentSyndicateFacet.actor.name = 'foo';
|
|
a; b; c;
|
|
});`));
|
|
|
|
// At present, the expr() parser accepts *empty input*. TODO: something better.
|
|
it('with missing name (known incorrect parsing and codegen)', () =>
|
|
expectCodeEqual(`spawn named { a; b; c; }`, `
|
|
__SYNDICATE__.Dataspace._spawn(() => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
currentSyndicateFacet.actor.name = ;
|
|
a; b; c;
|
|
});`));
|
|
|
|
});
|
|
|
|
describe('stop', () => {
|
|
|
|
it('non-statement', () => expectCodeEqual(`(stop)`, `(stop)`));
|
|
it('toplevel end-delimited statement', () => expectCodeEqual(`stop`, `
|
|
__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
});`));
|
|
it('nested end-delimited statement', () => expectCodeEqual(`{ stop }`, `
|
|
{
|
|
__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
});
|
|
}`));
|
|
|
|
it('without facet, without body', () => expectCodeEqual(`stop;`, `
|
|
__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
});`));
|
|
|
|
it('without facet, empty body', () => expectCodeEqual(`stop {}`, `
|
|
__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
});`));
|
|
|
|
it('without facet, non-empty body', () => expectCodeEqual(`stop { a; b; }`, `
|
|
__SYNDICATE__.Turn.active._stop(currentSyndicateFacet, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
a; b;
|
|
});`));
|
|
|
|
it('with facet, without body', () => expectCodeEqual(`stop x.y;`, `
|
|
__SYNDICATE__.Turn.active._stop(x.y, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
});`));
|
|
|
|
it('with facet, empty body', () => expectCodeEqual(`stop x.y {}`, `
|
|
__SYNDICATE__.Turn.active._stop(x.y, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
});`));
|
|
|
|
it('with facet, non-empty body', () => expectCodeEqual(`stop x.y { a; b; }`, `
|
|
__SYNDICATE__.Turn.active._stop(x.y, () => {
|
|
const currentSyndicateFacet = __SYNDICATE__.Turn.activeFacet;
|
|
a; b;
|
|
});`));
|
|
|
|
it('nested stop, no labels', () => expectCodeEqual(`stop { stop; }`, `
|
|
__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', () => expectCodeEqual(`during P => { a; stop; b; }`, `
|
|
__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;
|
|
}
|
|
}
|
|
))
|
|
})
|
|
}));`));
|
|
|
|
it('capture with type at top', () => expectCodeEqual(`during $v: T => { ok() }`, `
|
|
__SYNDICATE__.Turn.active.assertDataflow(() => ({
|
|
target: currentSyndicateTarget,
|
|
assertion: __SYNDICATE__.Observe({
|
|
pattern: __SYNDICATE__.QuasiValue.finish((__SYNDICATE__.QuasiValue.bind((__SYNDICATE__.QuasiValue._)))),
|
|
observer: __SYNDICATE__.Turn.ref(__SYNDICATE__.assertionFacetObserver(
|
|
(__vs: __SYNDICATE__.AnyValue) => {
|
|
if (Array.isArray(__vs)) {
|
|
const __v_0 = T.__from_preserve__(__vs[0]);
|
|
if (__v_0 === void 0) return;
|
|
const v = __v_0;
|
|
ok()
|
|
}
|
|
}
|
|
))
|
|
})
|
|
}));
|
|
`, { typescript: true }));
|
|
|
|
});
|
|
|
|
describe('once', () => {
|
|
it('basics with block', () => expectCodeEqual(`once asserted P => { a; b; }`, `
|
|
__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', () => expectCodeEqual(`once asserted P => x;`, `
|
|
__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;})
|
|
}
|
|
}
|
|
}),
|
|
}),
|
|
}));
|
|
});`));
|
|
|
|
});
|