diff options
Diffstat (limited to 'llparse-frontend/test')
64 files changed, 833 insertions, 0 deletions
diff --git a/llparse-frontend/test/container-test.ts b/llparse-frontend/test/container-test.ts new file mode 100644 index 0000000..28b7f1b --- /dev/null +++ b/llparse-frontend/test/container-test.ts @@ -0,0 +1,46 @@ +import * as assert from 'assert'; + +import { Builder } from 'llparse-builder'; + +import { Container, ContainerWrap, Frontend, node } from '../src/frontend'; +import implementation from './fixtures/a-implementation'; +import { Node } from './fixtures/implementation/node/base'; + +describe('llparse-frontend/Container', () => { + let b: Builder; + beforeEach(() => { + b = new Builder(); + }); + + it('should translate nodes to implementation', () => { + const comb = new Container(); + comb.add('a', implementation); + comb.add('b', implementation); + + const f = new Frontend('llparse', comb.build()); + + const root = b.node('root'); + + root.match('ab', root); + root.match('acd', root); + root.match('efg', root); + root.otherwise(b.error(123, 'hello')); + + const fRoot = f.compile(root, []).root as ContainerWrap<node.Node>; + + const out: string[] = []; + (fRoot.get('a') as Node<node.Node>).build(out); + + assert.deepStrictEqual(out, [ + '<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" ' + + 'otherwise-no_adv=llparse__n_error/>', + ]); + }); +}); diff --git a/llparse-frontend/test/fixtures/a-implementation/code/and.ts b/llparse-frontend/test/fixtures/a-implementation/code/and.ts new file mode 100644 index 0000000..c1df821 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/and.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class And extends Code<code.And> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/base.ts b/llparse-frontend/test/fixtures/a-implementation/code/base.ts new file mode 100644 index 0000000..d9a7ace --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/base.ts @@ -0,0 +1,6 @@ +export abstract class Code<T> { + constructor(public readonly ref: T) { + } + + public abstract build(): string; +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/index.ts b/llparse-frontend/test/fixtures/a-implementation/code/index.ts new file mode 100644 index 0000000..855a5cf --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/index.ts @@ -0,0 +1,15 @@ +import { And } from './and'; +import { IsEqual } from './is-equal'; +import { Load } from './load'; +import { Match } from './match'; +import { MulAdd } from './mul-add'; +import { Or } from './or'; +import { Span } from './span'; +import { Store } from './store'; +import { Test } from './test'; +import { Update } from './update'; +import { Value } from './value'; + +export default { + And, IsEqual, Load, Match, MulAdd, Or, Span, Store, Test, Update, Value, +}; diff --git a/llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts b/llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts new file mode 100644 index 0000000..13a1737 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/is-equal.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class IsEqual extends Code<code.IsEqual> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/load.ts b/llparse-frontend/test/fixtures/a-implementation/code/load.ts new file mode 100644 index 0000000..bc97f27 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/load.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Load extends Code<code.Load> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/match.ts b/llparse-frontend/test/fixtures/a-implementation/code/match.ts new file mode 100644 index 0000000..e933a71 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/match.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Match extends Code<code.Match> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts b/llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts new file mode 100644 index 0000000..e06a217 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/mul-add.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class MulAdd extends Code<code.MulAdd> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/or.ts b/llparse-frontend/test/fixtures/a-implementation/code/or.ts new file mode 100644 index 0000000..a569db4 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/or.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Or extends Code<code.Or> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/span.ts b/llparse-frontend/test/fixtures/a-implementation/code/span.ts new file mode 100644 index 0000000..46fc410 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/span.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Span extends Code<code.Span> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/store.ts b/llparse-frontend/test/fixtures/a-implementation/code/store.ts new file mode 100644 index 0000000..7a1ca9f --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/store.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Store extends Code<code.Store> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/test.ts b/llparse-frontend/test/fixtures/a-implementation/code/test.ts new file mode 100644 index 0000000..4fc8ddb --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/test.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Test extends Code<code.Test> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/update.ts b/llparse-frontend/test/fixtures/a-implementation/code/update.ts new file mode 100644 index 0000000..16b20e2 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/update.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Update extends Code<code.Update> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/code/value.ts b/llparse-frontend/test/fixtures/a-implementation/code/value.ts new file mode 100644 index 0000000..8e76e2a --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/code/value.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Value extends Code<code.Value> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/index.ts b/llparse-frontend/test/fixtures/a-implementation/index.ts new file mode 100644 index 0000000..1d8d29a --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/index.ts @@ -0,0 +1,5 @@ +import code from './code'; +import node from './node'; +import transform from './transform'; + +export default { code, node, transform }; diff --git a/llparse-frontend/test/fixtures/a-implementation/node/base.ts b/llparse-frontend/test/fixtures/a-implementation/node/base.ts new file mode 100644 index 0000000..04c8285 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/base.ts @@ -0,0 +1,38 @@ +import { ContainerWrap, node } from '../../../../src/frontend'; + +export abstract class Node<T extends node.Node> { + private built: boolean = false; + + constructor(public readonly ref: T) { + } + + public build(out: string[]): void { + if (this.built) { + return; + } + + this.built = true; + this.doBuild(out); + + if (this.ref.otherwise !== undefined) { + const cwrap = this.ref.otherwise.node as ContainerWrap<T>; + const otherwise = cwrap.get<Node<T>>('a'); + otherwise.build(out); + } + } + + protected format(value: string): string { + let otherwise: string = ''; + if (this.ref.otherwise !== undefined) { + const otherwiseRef = this.ref.otherwise.node.ref; + otherwise = ' otherwise' + + `${this.ref.otherwise.noAdvance ? '-no_adv' : ''}=` + + `${otherwiseRef.id.name}`; + } + + return `<${this.constructor.name} name=${this.ref.id.name} ` + + `${value}${otherwise}/>`; + } + + protected abstract doBuild(out: string[]): void; +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/consume.ts b/llparse-frontend/test/fixtures/a-implementation/node/consume.ts new file mode 100644 index 0000000..cdc6cef --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/consume.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Consume extends Node<node.Consume> { + protected doBuild(out: string[]): void { + out.push(this.format(`field=${this.ref.field}`)); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/empty.ts b/llparse-frontend/test/fixtures/a-implementation/node/empty.ts new file mode 100644 index 0000000..ef1499b --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/empty.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Empty extends Node<node.Empty> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/error.ts b/llparse-frontend/test/fixtures/a-implementation/node/error.ts new file mode 100644 index 0000000..1a4f31d --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/error.ts @@ -0,0 +1,10 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +class ErrorNode extends Node<node.Error> { + protected doBuild(out: string[]): void { + out.push(this.format(`code=${this.ref.code} reason="${this.ref.reason}"`)); + } +} + +export { ErrorNode as Error }; diff --git a/llparse-frontend/test/fixtures/a-implementation/node/index.ts b/llparse-frontend/test/fixtures/a-implementation/node/index.ts new file mode 100644 index 0000000..31dbc5e --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/index.ts @@ -0,0 +1,15 @@ +import { Consume } from './consume'; +import { Empty } from './empty'; +import { Error } from './error'; +import { Invoke } from './invoke'; +import { Pause } from './pause'; +import { Sequence } from './sequence'; +import { Single } from './single'; +import { SpanEnd } from './span-end'; +import { SpanStart } from './span-start'; +import { TableLookup } from './table-lookup'; + +export default { + Consume, Empty, Error, Invoke, Pause, Sequence, Single, SpanEnd, + SpanStart, TableLookup, +}; diff --git a/llparse-frontend/test/fixtures/a-implementation/node/invoke.ts b/llparse-frontend/test/fixtures/a-implementation/node/invoke.ts new file mode 100644 index 0000000..674be5f --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/invoke.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Invoke extends Node<node.Invoke> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/pause.ts b/llparse-frontend/test/fixtures/a-implementation/node/pause.ts new file mode 100644 index 0000000..94da63c --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/pause.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Pause extends Node<node.Pause> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/sequence.ts b/llparse-frontend/test/fixtures/a-implementation/node/sequence.ts new file mode 100644 index 0000000..13fd336 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/sequence.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Sequence extends Node<node.Sequence> { + protected doBuild(out: string[]): void { + out.push(this.format(`select="${this.ref.select.toString('hex')}"`)); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/single.ts b/llparse-frontend/test/fixtures/a-implementation/node/single.ts new file mode 100644 index 0000000..d7bcc72 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/single.ts @@ -0,0 +1,18 @@ +import { ContainerWrap, node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Single extends Node<node.Single> { + protected doBuild(out: string[]): void { + const edges: string[] = []; + for (const edge of this.ref.edges) { + edges.push(`k${edge.key}${edge.noAdvance ? '-no_adv-' : ''}=` + + `${edge.node.ref.id.name}`); + } + out.push(this.format(edges.join(' '))); + + for (const edge of this.ref.edges) { + const edgeNode = edge.node as ContainerWrap<node.Node>; + edgeNode.get<Node<node.Node>>('a').build(out); + } + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/span-end.ts b/llparse-frontend/test/fixtures/a-implementation/node/span-end.ts new file mode 100644 index 0000000..dc79b81 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/span-end.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class SpanEnd extends Node<node.SpanEnd> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/span-start.ts b/llparse-frontend/test/fixtures/a-implementation/node/span-start.ts new file mode 100644 index 0000000..32e373c --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/span-start.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class SpanStart extends Node<node.SpanStart> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts b/llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts new file mode 100644 index 0000000..e6166d0 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/node/table-lookup.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class TableLookup extends Node<node.TableLookup> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/base.ts b/llparse-frontend/test/fixtures/a-implementation/transform/base.ts new file mode 100644 index 0000000..96dc27d --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/transform/base.ts @@ -0,0 +1,6 @@ +export abstract class Transform<T> { + constructor(public readonly ref: T) { + } + + public abstract build(): string; +} diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/id.ts b/llparse-frontend/test/fixtures/a-implementation/transform/id.ts new file mode 100644 index 0000000..e6c1adc --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/transform/id.ts @@ -0,0 +1,8 @@ +import { transform } from '../../../../src/frontend'; +import { Transform } from './base'; + +export class ID extends Transform<transform.ID> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/index.ts b/llparse-frontend/test/fixtures/a-implementation/transform/index.ts new file mode 100644 index 0000000..bed8bc9 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/transform/index.ts @@ -0,0 +1,5 @@ +import { ID } from './id'; +import { ToLower } from './to-lower'; +import { ToLowerUnsafe } from './to-lower-unsafe'; + +export default { ID, ToLower, ToLowerUnsafe }; diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts new file mode 100644 index 0000000..9d175a9 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower-unsafe.ts @@ -0,0 +1,8 @@ +import { transform } from '../../../../src/frontend'; +import { Transform } from './base'; + +export class ToLowerUnsafe extends Transform<transform.ToLowerUnsafe> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts new file mode 100644 index 0000000..cbe6456 --- /dev/null +++ b/llparse-frontend/test/fixtures/a-implementation/transform/to-lower.ts @@ -0,0 +1,8 @@ +import { transform } from '../../../../src/frontend'; +import { Transform } from './base'; + +export class ToLower extends Transform<transform.ToLower> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/and.ts b/llparse-frontend/test/fixtures/implementation/code/and.ts new file mode 100644 index 0000000..c1df821 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/and.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class And extends Code<code.And> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/base.ts b/llparse-frontend/test/fixtures/implementation/code/base.ts new file mode 100644 index 0000000..d9a7ace --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/base.ts @@ -0,0 +1,6 @@ +export abstract class Code<T> { + constructor(public readonly ref: T) { + } + + public abstract build(): string; +} diff --git a/llparse-frontend/test/fixtures/implementation/code/index.ts b/llparse-frontend/test/fixtures/implementation/code/index.ts new file mode 100644 index 0000000..855a5cf --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/index.ts @@ -0,0 +1,15 @@ +import { And } from './and'; +import { IsEqual } from './is-equal'; +import { Load } from './load'; +import { Match } from './match'; +import { MulAdd } from './mul-add'; +import { Or } from './or'; +import { Span } from './span'; +import { Store } from './store'; +import { Test } from './test'; +import { Update } from './update'; +import { Value } from './value'; + +export default { + And, IsEqual, Load, Match, MulAdd, Or, Span, Store, Test, Update, Value, +}; diff --git a/llparse-frontend/test/fixtures/implementation/code/is-equal.ts b/llparse-frontend/test/fixtures/implementation/code/is-equal.ts new file mode 100644 index 0000000..13a1737 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/is-equal.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class IsEqual extends Code<code.IsEqual> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/load.ts b/llparse-frontend/test/fixtures/implementation/code/load.ts new file mode 100644 index 0000000..bc97f27 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/load.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Load extends Code<code.Load> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/match.ts b/llparse-frontend/test/fixtures/implementation/code/match.ts new file mode 100644 index 0000000..e933a71 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/match.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Match extends Code<code.Match> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/mul-add.ts b/llparse-frontend/test/fixtures/implementation/code/mul-add.ts new file mode 100644 index 0000000..e06a217 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/mul-add.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class MulAdd extends Code<code.MulAdd> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/or.ts b/llparse-frontend/test/fixtures/implementation/code/or.ts new file mode 100644 index 0000000..a569db4 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/or.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Or extends Code<code.Or> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/span.ts b/llparse-frontend/test/fixtures/implementation/code/span.ts new file mode 100644 index 0000000..46fc410 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/span.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Span extends Code<code.Span> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/store.ts b/llparse-frontend/test/fixtures/implementation/code/store.ts new file mode 100644 index 0000000..7a1ca9f --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/store.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Store extends Code<code.Store> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/test.ts b/llparse-frontend/test/fixtures/implementation/code/test.ts new file mode 100644 index 0000000..4fc8ddb --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/test.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Test extends Code<code.Test> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/update.ts b/llparse-frontend/test/fixtures/implementation/code/update.ts new file mode 100644 index 0000000..16b20e2 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/update.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Update extends Code<code.Update> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/code/value.ts b/llparse-frontend/test/fixtures/implementation/code/value.ts new file mode 100644 index 0000000..8e76e2a --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/code/value.ts @@ -0,0 +1,8 @@ +import { code } from '../../../../src/frontend'; +import { Code } from './base'; + +export class Value extends Code<code.Value> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/index.ts b/llparse-frontend/test/fixtures/implementation/index.ts new file mode 100644 index 0000000..1d8d29a --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/index.ts @@ -0,0 +1,5 @@ +import code from './code'; +import node from './node'; +import transform from './transform'; + +export default { code, node, transform }; diff --git a/llparse-frontend/test/fixtures/implementation/node/base.ts b/llparse-frontend/test/fixtures/implementation/node/base.ts new file mode 100644 index 0000000..c9fd589 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/base.ts @@ -0,0 +1,39 @@ +import { node } from '../../../../src/frontend'; + +export abstract class Node<T extends node.Node> { + private built: boolean = false; + + constructor(public readonly ref: T) { + } + + public build(out: string[]): void { + if (this.built) { + return; + } + + this.built = true; + this.doBuild(out); + + if (this.ref.otherwise !== undefined) { + (this.ref.otherwise.node as Node<T>).build(out); + } + } + + protected format(value: string): string { + let otherwise: string = ''; + if (this.ref.otherwise !== undefined) { + const otherwiseRef = this.ref.otherwise.node.ref; + otherwise = ' otherwise' + + `${this.ref.otherwise.noAdvance ? '-no_adv' : ''}=` + + `${otherwiseRef.id.name}`; + if (this.ref.otherwise.value !== undefined) { + otherwise += `:${this.ref.otherwise.value}`; + } + } + + return `<${this.constructor.name} name=${this.ref.id.name} ` + + `${value}${otherwise}/>`; + } + + protected abstract doBuild(out: string[]): void; +} diff --git a/llparse-frontend/test/fixtures/implementation/node/consume.ts b/llparse-frontend/test/fixtures/implementation/node/consume.ts new file mode 100644 index 0000000..cdc6cef --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/consume.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Consume extends Node<node.Consume> { + protected doBuild(out: string[]): void { + out.push(this.format(`field=${this.ref.field}`)); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/empty.ts b/llparse-frontend/test/fixtures/implementation/node/empty.ts new file mode 100644 index 0000000..ef1499b --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/empty.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Empty extends Node<node.Empty> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/error.ts b/llparse-frontend/test/fixtures/implementation/node/error.ts new file mode 100644 index 0000000..1a4f31d --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/error.ts @@ -0,0 +1,10 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +class ErrorNode extends Node<node.Error> { + protected doBuild(out: string[]): void { + out.push(this.format(`code=${this.ref.code} reason="${this.ref.reason}"`)); + } +} + +export { ErrorNode as Error }; diff --git a/llparse-frontend/test/fixtures/implementation/node/index.ts b/llparse-frontend/test/fixtures/implementation/node/index.ts new file mode 100644 index 0000000..31dbc5e --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/index.ts @@ -0,0 +1,15 @@ +import { Consume } from './consume'; +import { Empty } from './empty'; +import { Error } from './error'; +import { Invoke } from './invoke'; +import { Pause } from './pause'; +import { Sequence } from './sequence'; +import { Single } from './single'; +import { SpanEnd } from './span-end'; +import { SpanStart } from './span-start'; +import { TableLookup } from './table-lookup'; + +export default { + Consume, Empty, Error, Invoke, Pause, Sequence, Single, SpanEnd, + SpanStart, TableLookup, +}; diff --git a/llparse-frontend/test/fixtures/implementation/node/invoke.ts b/llparse-frontend/test/fixtures/implementation/node/invoke.ts new file mode 100644 index 0000000..674be5f --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/invoke.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Invoke extends Node<node.Invoke> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/pause.ts b/llparse-frontend/test/fixtures/implementation/node/pause.ts new file mode 100644 index 0000000..94da63c --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/pause.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Pause extends Node<node.Pause> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/sequence.ts b/llparse-frontend/test/fixtures/implementation/node/sequence.ts new file mode 100644 index 0000000..bb745f5 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/sequence.ts @@ -0,0 +1,15 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Sequence extends Node<node.Sequence> { + protected doBuild(out: string[]): void { + let str = `select="${this.ref.select.toString('hex')}" ` + + `edge="${this.ref.edge!.node.ref.id.name}"`; + if (this.ref.edge!.value !== undefined) { + str += `:${this.ref.edge!.value}`; + } + out.push(this.format(str)); + const edgeNode = this.ref.edge!.node as Node<node.Node>; + edgeNode.build(out); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/single.ts b/llparse-frontend/test/fixtures/implementation/node/single.ts new file mode 100644 index 0000000..b24ef93 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/single.ts @@ -0,0 +1,22 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class Single extends Node<node.Single> { + protected doBuild(out: string[]): void { + const edges: string[] = []; + for (const edge of this.ref.edges) { + let str = `k${edge.key}${edge.noAdvance ? '-no_adv-' : ''}=` + + `${edge.node.ref.id.name}`; + if (edge.value !== undefined) { + str += `:${edge.value}`; + } + edges.push(str); + } + out.push(this.format(edges.join(' '))); + + for (const edge of this.ref.edges) { + const edgeNode = edge.node as Node<node.Node>; + edgeNode.build(out); + } + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/span-end.ts b/llparse-frontend/test/fixtures/implementation/node/span-end.ts new file mode 100644 index 0000000..dc79b81 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/span-end.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class SpanEnd extends Node<node.SpanEnd> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/span-start.ts b/llparse-frontend/test/fixtures/implementation/node/span-start.ts new file mode 100644 index 0000000..32e373c --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/span-start.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class SpanStart extends Node<node.SpanStart> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/node/table-lookup.ts b/llparse-frontend/test/fixtures/implementation/node/table-lookup.ts new file mode 100644 index 0000000..e6166d0 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/node/table-lookup.ts @@ -0,0 +1,8 @@ +import { node } from '../../../../src/frontend'; +import { Node } from './base'; + +export class TableLookup extends Node<node.TableLookup> { + protected doBuild(out: string[]): void { + out.push(this.format('')); + } +} diff --git a/llparse-frontend/test/fixtures/implementation/transform/base.ts b/llparse-frontend/test/fixtures/implementation/transform/base.ts new file mode 100644 index 0000000..96dc27d --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/transform/base.ts @@ -0,0 +1,6 @@ +export abstract class Transform<T> { + constructor(public readonly ref: T) { + } + + public abstract build(): string; +} diff --git a/llparse-frontend/test/fixtures/implementation/transform/id.ts b/llparse-frontend/test/fixtures/implementation/transform/id.ts new file mode 100644 index 0000000..e6c1adc --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/transform/id.ts @@ -0,0 +1,8 @@ +import { transform } from '../../../../src/frontend'; +import { Transform } from './base'; + +export class ID extends Transform<transform.ID> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/transform/index.ts b/llparse-frontend/test/fixtures/implementation/transform/index.ts new file mode 100644 index 0000000..bed8bc9 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/transform/index.ts @@ -0,0 +1,5 @@ +import { ID } from './id'; +import { ToLower } from './to-lower'; +import { ToLowerUnsafe } from './to-lower-unsafe'; + +export default { ID, ToLower, ToLowerUnsafe }; diff --git a/llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts b/llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts new file mode 100644 index 0000000..9d175a9 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/transform/to-lower-unsafe.ts @@ -0,0 +1,8 @@ +import { transform } from '../../../../src/frontend'; +import { Transform } from './base'; + +export class ToLowerUnsafe extends Transform<transform.ToLowerUnsafe> { + public build(): string { + return ''; + } +} diff --git a/llparse-frontend/test/fixtures/implementation/transform/to-lower.ts b/llparse-frontend/test/fixtures/implementation/transform/to-lower.ts new file mode 100644 index 0000000..cbe6456 --- /dev/null +++ b/llparse-frontend/test/fixtures/implementation/transform/to-lower.ts @@ -0,0 +1,8 @@ +import { transform } from '../../../../src/frontend'; +import { Transform } from './base'; + +export class ToLower extends Transform<transform.ToLower> { + public build(): string { + return ''; + } +} 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', + ]); + }); +}); |