summaryrefslogtreecommitdiffstats
path: root/llparse-frontend/test/frontend-test.ts
blob: 69e075c04db7d2b136e0ef26ed2745233ec6ac4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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',
    ]);
  });
});