Skip to content

Commit d7ade4a

Browse files
committed
frontend: export resumptionTargets
1 parent 8a67670 commit d7ade4a

File tree

3 files changed

+117
-17
lines changed

3 files changed

+117
-17
lines changed

src/frontend.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export class Frontend {
7070
private readonly map: Map<source.node.Node, WrappedNode> = new Map();
7171
private readonly spanMap: Map<source.Span, SpanField> = new Map();
7272
private readonly codeCache: Map<string, WrappedCode> = new Map();
73+
private readonly resumptionTargets: Set<WrappedNode> = new Set();
7374

7475
constructor(private readonly prefix: string,
7576
private readonly implementation: IImplementation,
@@ -112,15 +113,28 @@ export class Frontend {
112113

113114
debug('enumerating');
114115
const enumerator = new Enumerator();
115-
const nodes = enumerator.getAllNodes(out);
116+
let nodes = enumerator.getAllNodes(out);
116117

117118
debug('peephole optimization');
118119
const peephole = new Peephole();
119120
out = peephole.optimize(out, nodes);
120121

122+
debug('re-enumerating');
123+
nodes = enumerator.getAllNodes(out);
124+
125+
debug('registering resumption targets');
126+
this.resumptionTargets.add(out);
127+
for (const node of nodes) {
128+
this.registerNode(node);
129+
}
130+
121131
return { prefix: this.prefix, properties, spans, root: out };
122132
}
123133

134+
public getResumptionTargets(): ReadonlySet<WrappedNode> {
135+
return this.resumptionTargets;
136+
}
137+
124138
private translate(node: source.node.Node): WrappedNode {
125139
if (this.map.has(node)) {
126140
return this.map.get(node)!;
@@ -188,7 +202,7 @@ export class Frontend {
188202
assert(result.length >= 1);
189203
return result[0];
190204
} else {
191-
const single = result as WrappedNode;
205+
const single: WrappedNode = result as WrappedNode;
192206
assert(single.ref instanceof frontend.node.Node);
193207

194208
// Break loops
@@ -215,6 +229,25 @@ export class Frontend {
215229
}
216230
}
217231

232+
private registerNode(node: any): void {
233+
const nodeImpl = this.implementation.node;
234+
235+
// Nodes with prologue check (start_pos != end_pos)
236+
if (node instanceof nodeImpl.Consume ||
237+
node instanceof nodeImpl.Empty ||
238+
node instanceof nodeImpl.Sequence ||
239+
node instanceof nodeImpl.Single ||
240+
node instanceof nodeImpl.SpanStart ||
241+
node instanceof nodeImpl.TableLookup) {
242+
this.resumptionTargets.add(node);
243+
244+
// Nodes that can interrupt the execution to be resumed at different node
245+
} else if (node instanceof nodeImpl.Pause ||
246+
node instanceof nodeImpl.SpanEnd) {
247+
this.resumptionTargets.add(node.ref.otherwise!.node);
248+
}
249+
}
250+
218251
private translateMatch(node: source.node.Match): MatchResult {
219252
const trie = new Trie(node.name);
220253

test/fixtures/implementation/node/sequence.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { Node } from './base';
33

44
export class Sequence extends Node<node.Sequence> {
55
protected doBuild(out: string[]): void {
6-
out.push(this.format(`select="${this.ref.select.toString('hex')}"`));
6+
out.push(this.format(`select="${this.ref.select.toString('hex')}" ` +
7+
`edge="${this.ref.edge!.node.ref.id.name}"`));
8+
const edgeNode = this.ref.edge!.node as Node<node.Node>;
9+
edgeNode.build(out);
710
}
811
}

test/frontend-test.ts

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,34 @@
11
import * as assert from 'assert';
22

3-
import { Builder } from 'llparse-builder';
3+
import * as source from 'llparse-builder';
44

55
import { Frontend, node } from '../src/frontend';
66
import implementation from './fixtures/implementation';
77
import { Node } from './fixtures/implementation/node/base';
88

9+
function checkNodes(f: Frontend, root: source.node.Node,
10+
expected: ReadonlyArray<string>) {
11+
const fRoot = f.compile(root, []).root as Node<node.Node>;
12+
13+
const out: string[] = [];
14+
fRoot.build(out);
15+
16+
assert.deepStrictEqual(out, expected);
17+
}
18+
19+
function checkResumptionTargets(f: Frontend, expected: ReadonlyArray<string>) {
20+
const targets = Array.from(f.getResumptionTargets()).map((t) => {
21+
return t.ref.id.name;
22+
});
23+
24+
assert.deepStrictEqual(targets, expected);
25+
}
26+
927
describe('llparse-frontend', () => {
10-
let b: Builder;
28+
let b: source.Builder;
1129
let f: Frontend;
1230
beforeEach(() => {
13-
b = new Builder();
31+
b = new source.Builder();
1432
f = new Frontend('llparse', implementation);
1533
});
1634

@@ -22,12 +40,7 @@ describe('llparse-frontend', () => {
2240
root.match('efg', root);
2341
root.otherwise(b.error(123, 'hello'));
2442

25-
const fRoot = f.compile(root, []).root as Node<node.Node>;
26-
27-
const out: string[] = [];
28-
fRoot.build(out);
29-
30-
assert.deepStrictEqual(out, [
43+
checkNodes(f, root, [
3144
'<Single name=llparse__n_root k97=llparse__n_root_1 ' +
3245
'k101=llparse__n_root_3 otherwise-no_adv=llparse__n_error/>',
3346
'<Single name=llparse__n_root_1 k98=llparse__n_root ' +
@@ -36,8 +49,16 @@ describe('llparse-frontend', () => {
3649
'otherwise-no_adv=llparse__n_error/>',
3750
'<ErrorNode name=llparse__n_error code=123 reason="hello"/>',
3851
'<Sequence name=llparse__n_root_3 select="6667" ' +
52+
'edge=\"llparse__n_root\" ' +
3953
'otherwise-no_adv=llparse__n_error/>',
4054
]);
55+
56+
checkResumptionTargets(f, [
57+
'llparse__n_root',
58+
'llparse__n_root_1',
59+
'llparse__n_root_3',
60+
'llparse__n_root_2',
61+
]);
4162
});
4263

4364
it('should do peephole optimization', () => {
@@ -53,13 +74,56 @@ describe('llparse-frontend', () => {
5374
node1.otherwise(node2);
5475
node2.otherwise(root);
5576

56-
const fRoot = f.compile(root, []).root as Node<node.Node>;
77+
checkNodes(f, root, [
78+
'<Empty name=llparse__n_b otherwise=llparse__n_b/>',
79+
]);
5780

58-
const out: string[] = [];
59-
fRoot.build(out);
81+
checkResumptionTargets(f, [
82+
'llparse__n_b',
83+
]);
84+
});
6085

61-
assert.deepStrictEqual(out, [
62-
'<Empty name=llparse__n_b otherwise=llparse__n_b/>',
86+
it('should generate proper resumption targets', () => {
87+
b.property('i64', 'counter');
88+
89+
const root = b.node('root');
90+
const end = b.node('end');
91+
const store = b.invoke(b.code.store('counter'));
92+
93+
root.select({ a: 1, b: 2 }, store);
94+
root.otherwise(b.error(1, 'okay'));
95+
96+
store.otherwise(end);
97+
98+
end.match('ohai', root);
99+
end.match('paus', b.pause(1, 'paused').otherwise(
100+
b.pause(2, 'paused').otherwise(root)));
101+
end.otherwise(b.error(2, 'ohai'));
102+
103+
checkNodes(f, root, [
104+
'<Single name=llparse__n_root k97=llparse__n_invoke_store_counter ' +
105+
'k98=llparse__n_invoke_store_counter ' +
106+
'otherwise-no_adv=llparse__n_error_1/>',
107+
'<Invoke name=llparse__n_invoke_store_counter ' +
108+
'otherwise-no_adv=llparse__n_end/>',
109+
'<Single name=llparse__n_end k111=llparse__n_end_1 ' +
110+
'k112=llparse__n_end_2 otherwise-no_adv=llparse__n_error/>',
111+
'<Sequence name=llparse__n_end_1 select="686169" ' +
112+
'edge="llparse__n_root" otherwise-no_adv=llparse__n_error/>',
113+
'<ErrorNode name=llparse__n_error code=2 reason="ohai"/>',
114+
'<Sequence name=llparse__n_end_2 select="617573" ' +
115+
'edge="llparse__n_pause" otherwise-no_adv=llparse__n_error/>',
116+
'<Pause name=llparse__n_pause otherwise-no_adv=llparse__n_pause_1/>',
117+
'<Pause name=llparse__n_pause_1 otherwise-no_adv=llparse__n_root/>',
118+
'<ErrorNode name=llparse__n_error_1 code=1 reason="okay"/>',
119+
]);
120+
121+
checkResumptionTargets(f, [
122+
'llparse__n_root',
123+
'llparse__n_end',
124+
'llparse__n_end_1',
125+
'llparse__n_end_2',
126+
'llparse__n_pause_1',
63127
]);
64128
});
65129
});

0 commit comments

Comments
 (0)