summaryrefslogtreecommitdiffstats
path: root/llparse-frontend/test/frontend-test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'llparse-frontend/test/frontend-test.ts')
-rw-r--r--llparse-frontend/test/frontend-test.ts187
1 files changed, 187 insertions, 0 deletions
diff --git a/llparse-frontend/test/frontend-test.ts b/llparse-frontend/test/frontend-test.ts
new file mode 100644
index 0000000..69e075c
--- /dev/null
+++ b/llparse-frontend/test/frontend-test.ts
@@ -0,0 +1,187 @@
+import * as assert from 'assert';
+
+import * as source from 'llparse-builder';
+
+import { Frontend, node } from '../src/frontend';
+import implementation from './fixtures/implementation';
+import { Node } from './fixtures/implementation/node/base';
+
+function checkNodes(f: Frontend, root: source.node.Node,
+ expected: ReadonlyArray<string>) {
+ const fRoot = f.compile(root, []).root as Node<node.Node>;
+
+ const out: string[] = [];
+ fRoot.build(out);
+
+ assert.deepStrictEqual(out, expected);
+
+ return fRoot;
+}
+
+function checkResumptionTargets(f: Frontend, expected: ReadonlyArray<string>) {
+ const targets = Array.from(f.getResumptionTargets()).map((t) => {
+ return t.ref.id.name;
+ });
+
+ assert.deepStrictEqual(targets, expected);
+}
+
+describe('llparse-frontend', () => {
+ let b: source.Builder;
+ let f: Frontend;
+ beforeEach(() => {
+ b = new source.Builder();
+ f = new Frontend('llparse', implementation);
+ });
+
+ it('should translate nodes to implementation', () => {
+ const root = b.node('root');
+
+ root.match('ab', root);
+ root.match('acd', root);
+ root.match('efg', root);
+ root.otherwise(b.error(123, 'hello'));
+
+ checkNodes(f, root, [
+ '<Single name=llparse__n_root k97=llparse__n_root_1 ' +
+ 'k101=llparse__n_root_3 otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k98=llparse__n_root ' +
+ 'k99=llparse__n_root_2 otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_2 k100=llparse__n_root ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ '<Sequence name=llparse__n_root_3 select="6667" ' +
+ 'edge=\"llparse__n_root\" ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_root_1',
+ 'llparse__n_root_3',
+ 'llparse__n_root_2',
+ ]);
+ });
+
+ it('should do peephole optimization', () => {
+ const root = b.node('root');
+ const root1 = b.node('a');
+ const root2 = b.node('b');
+ const node1 = b.node('c');
+ const node2 = b.node('d');
+
+ root.otherwise(root1);
+ root1.otherwise(root2);
+ root2.skipTo(node1);
+ node1.otherwise(node2);
+ node2.otherwise(root);
+
+ checkNodes(f, root, [
+ '<Empty name=llparse__n_b otherwise=llparse__n_b/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_b',
+ ]);
+ });
+
+ it('should generate proper resumption targets', () => {
+ b.property('i64', 'counter');
+
+ const root = b.node('root');
+ const end = b.node('end');
+ const store = b.invoke(b.code.store('counter'));
+
+ root.select({ a: 1, b: 2 }, store);
+ root.otherwise(b.error(1, 'okay'));
+
+ store.otherwise(end);
+
+ end.match('ohai', root);
+ end.match('paus', b.pause(1, 'paused').otherwise(
+ b.pause(2, 'paused').otherwise(root)));
+ end.otherwise(b.error(2, 'ohai'));
+
+ checkNodes(f, root, [
+ '<Single name=llparse__n_root k97=llparse__n_invoke_store_counter:1 ' +
+ 'k98=llparse__n_invoke_store_counter:2 ' +
+ 'otherwise-no_adv=llparse__n_error_1/>',
+ '<Invoke name=llparse__n_invoke_store_counter ' +
+ 'otherwise-no_adv=llparse__n_end/>',
+ '<Single name=llparse__n_end k111=llparse__n_end_1 ' +
+ 'k112=llparse__n_end_2 otherwise-no_adv=llparse__n_error/>',
+ '<Sequence name=llparse__n_end_1 select="686169" ' +
+ 'edge="llparse__n_root" otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=2 reason="ohai"/>',
+ '<Sequence name=llparse__n_end_2 select="617573" ' +
+ 'edge="llparse__n_pause" otherwise-no_adv=llparse__n_error/>',
+ '<Pause name=llparse__n_pause otherwise-no_adv=llparse__n_pause_1/>',
+ '<Pause name=llparse__n_pause_1 otherwise-no_adv=llparse__n_root/>',
+ '<ErrorNode name=llparse__n_error_1 code=1 reason="okay"/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_end',
+ 'llparse__n_end_1',
+ 'llparse__n_end_2',
+ 'llparse__n_pause_1',
+ ]);
+ });
+
+ it('should translate Span code into Span', () => {
+ const root = b.invoke(b.code.span('my_span'));
+ root.otherwise(b.error(1, 'okay'));
+
+ const fRoot = checkNodes(f, root, [
+ '<Invoke name=llparse__n_invoke_my_span ' +
+ 'otherwise-no_adv=llparse__n_error/>',
+ '<ErrorNode name=llparse__n_error code=1 reason="okay"/>',
+ ]);
+
+ assert((fRoot.ref as any).code instanceof implementation.code.Span);
+ });
+
+ it('should translate overlapping matches', () => {
+ const root = b.node('root');
+
+ root.match('ab', root);
+ root.match('abc', root);
+ root.otherwise(b.error(123, 'hello'));
+
+ checkNodes(f, root, [
+ '<Sequence name=llparse__n_root select="6162" edge="llparse__n_root_1" otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k99=llparse__n_root otherwise-no_adv=llparse__n_root/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_root_1',
+ ]);
+ });
+
+ it('should translate overlapping matches with values', () => {
+ const root = b.node('root');
+ const store = b.invoke(b.code.store('counter'));
+
+ root.select({
+ ab: 1,
+ abc: 2,
+ }, store);
+ store.otherwise(root);
+ root.otherwise(b.error(123, 'hello'));
+
+ checkNodes(f, root, [
+ '<Sequence name=llparse__n_root select="6162" edge="llparse__n_root_1" otherwise-no_adv=llparse__n_error/>',
+ '<Single name=llparse__n_root_1 k99=llparse__n_invoke_store_counter:2 otherwise-no_adv=llparse__n_invoke_store_counter:1/>',
+ '<Invoke name=llparse__n_invoke_store_counter otherwise-no_adv=llparse__n_root/>',
+ '<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
+ ]);
+
+ checkResumptionTargets(f, [
+ 'llparse__n_root',
+ 'llparse__n_root_1',
+ ]);
+ });
+});