@ohm-js/wasm 0.6.8 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/src/AstBuilder.d.ts +12 -10
- package/dist/src/AstBuilder.js +28 -31
- package/dist/src/compat.d.ts +1 -0
- package/dist/src/compat.js +1 -0
- package/dist/src/createToAst.d.ts +22 -0
- package/dist/src/createToAst.js +140 -0
- package/dist/src/miniohm.d.ts +160 -30
- package/dist/src/miniohm.js +279 -96
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/src/AstBuilder.d.ts
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import type { CstNode, MatchResult } from './miniohm.ts';
|
|
2
|
-
export type AstNodeTemplate<
|
|
3
|
-
[property: string]: number | string | boolean | object | null | ((this: AstBuilder, children:
|
|
1
|
+
import type { CstNode, CstNodeChildren, MatchResult, NonterminalNode, TerminalNode } from './miniohm.ts';
|
|
2
|
+
export type AstNodeTemplate<R> = {
|
|
3
|
+
[property: string]: number | string | boolean | object | null | ((this: AstBuilder, children: CstNodeChildren) => R);
|
|
4
4
|
};
|
|
5
|
-
export type AstMapping<
|
|
6
|
-
export declare
|
|
5
|
+
export type AstMapping<R> = Record<string, AstNodeTemplate<R> | number | ((this: AstBuilder, ...children: CstNodeChildren) => R)>;
|
|
6
|
+
export declare function createToAst<TNode = any>(mapping: AstMapping<TNode>, opts?: {
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
}): (nodeOrResult: MatchResult | CstNode) => TNode;
|
|
9
|
+
export declare class AstBuilder<TNode = any> {
|
|
7
10
|
currNode?: CstNode;
|
|
8
11
|
private _mapping;
|
|
9
12
|
private _depth;
|
|
10
13
|
private _debug;
|
|
11
|
-
constructor(mapping: AstMapping<
|
|
14
|
+
constructor(mapping: AstMapping<TNode>, opts?: {
|
|
12
15
|
debug?: boolean;
|
|
13
16
|
});
|
|
14
17
|
private _debugLog;
|
|
15
|
-
_visitTerminal(node:
|
|
16
|
-
_visitNonterminal(node:
|
|
17
|
-
|
|
18
|
-
toAst(nodeOrResult: MatchResult | CstNode): T;
|
|
18
|
+
_visitTerminal(node: TerminalNode): string;
|
|
19
|
+
_visitNonterminal(node: NonterminalNode): unknown;
|
|
20
|
+
toAst(nodeOrResult: MatchResult | CstNode): TNode;
|
|
19
21
|
}
|
|
20
22
|
//# sourceMappingURL=AstBuilder.d.ts.map
|
package/dist/src/AstBuilder.js
CHANGED
|
@@ -4,7 +4,11 @@ function childAt(children, idx, ruleName, propName = '') {
|
|
|
4
4
|
const path = propName ? `${ruleName}.${propName}` : ruleName;
|
|
5
5
|
throw new Error(`${path}: Child index ${idx} out of range`);
|
|
6
6
|
}
|
|
7
|
-
return children[idx];
|
|
7
|
+
return checkNotNull(children[idx]);
|
|
8
|
+
}
|
|
9
|
+
export function createToAst(mapping, opts = {}) {
|
|
10
|
+
const builder = new AstBuilder(mapping, opts);
|
|
11
|
+
return (nodeOrResult) => builder.toAst(nodeOrResult);
|
|
8
12
|
}
|
|
9
13
|
export class AstBuilder {
|
|
10
14
|
currNode;
|
|
@@ -14,10 +18,10 @@ export class AstBuilder {
|
|
|
14
18
|
constructor(mapping, opts = {}) {
|
|
15
19
|
const handleListOf = (child) => this.toAst(child);
|
|
16
20
|
const handleEmptyListOf = () => [];
|
|
17
|
-
const handleNonemptyListOf = (first,
|
|
18
|
-
|
|
19
|
-
...
|
|
20
|
-
|
|
21
|
+
const handleNonemptyListOf = (first, sepAndElemList) => {
|
|
22
|
+
assert(!!sepAndElemList?.isList(), 'Expected a ListNode');
|
|
23
|
+
return [this.toAst(first), ...sepAndElemList.collect((_, elem) => this.toAst(elem))];
|
|
24
|
+
};
|
|
21
25
|
this._mapping = {
|
|
22
26
|
listOf: handleListOf,
|
|
23
27
|
ListOf: handleListOf,
|
|
@@ -37,15 +41,15 @@ export class AstBuilder {
|
|
|
37
41
|
return node.sourceString;
|
|
38
42
|
}
|
|
39
43
|
_visitNonterminal(node) {
|
|
40
|
-
const { children,
|
|
44
|
+
const { children, ctorName } = node;
|
|
41
45
|
const mapping = this._mapping;
|
|
42
|
-
this._debugLog(`> ${
|
|
46
|
+
this._debugLog(`> ${ctorName}`);
|
|
43
47
|
const dbgReturn = (val) => {
|
|
44
|
-
this._debugLog(`| ${
|
|
48
|
+
this._debugLog(`| ${ctorName} DONE`);
|
|
45
49
|
return val;
|
|
46
50
|
};
|
|
47
51
|
// without customization
|
|
48
|
-
if (!Object.hasOwn(mapping,
|
|
52
|
+
if (!Object.hasOwn(mapping, ctorName)) {
|
|
49
53
|
// lexical rule
|
|
50
54
|
if (node.isLexical()) {
|
|
51
55
|
return node.sourceString;
|
|
@@ -58,22 +62,22 @@ export class AstBuilder {
|
|
|
58
62
|
// rest: terms with multiple children
|
|
59
63
|
}
|
|
60
64
|
// direct forward
|
|
61
|
-
if (typeof mapping[
|
|
62
|
-
const idx = mapping[
|
|
63
|
-
return dbgReturn(this.toAst(childAt(children, idx,
|
|
65
|
+
if (typeof mapping[ctorName] === 'number') {
|
|
66
|
+
const idx = mapping[ctorName];
|
|
67
|
+
return dbgReturn(this.toAst(childAt(children, idx, ctorName)));
|
|
64
68
|
}
|
|
65
|
-
assert(typeof mapping[
|
|
69
|
+
assert(typeof mapping[ctorName] !== 'function', "shouldn't be possible");
|
|
66
70
|
// named/mapped children or unnamed children ('0', '1', '2', ...)
|
|
67
|
-
const propMap = mapping[
|
|
71
|
+
const propMap = mapping[ctorName] || children;
|
|
68
72
|
const ans = {
|
|
69
|
-
type:
|
|
73
|
+
type: ctorName,
|
|
70
74
|
};
|
|
71
75
|
// eslint-disable-next-line guard-for-in
|
|
72
76
|
for (const prop in propMap) {
|
|
73
|
-
const mappedProp = mapping[
|
|
77
|
+
const mappedProp = mapping[ctorName] && mapping[ctorName][prop];
|
|
74
78
|
if (typeof mappedProp === 'number') {
|
|
75
79
|
// direct forward
|
|
76
|
-
ans[prop] = this.toAst(childAt(children, mappedProp,
|
|
80
|
+
ans[prop] = this.toAst(childAt(children, mappedProp, ctorName, prop));
|
|
77
81
|
}
|
|
78
82
|
else if (typeof mappedProp === 'string' ||
|
|
79
83
|
typeof mappedProp === 'boolean' ||
|
|
@@ -102,33 +106,26 @@ export class AstBuilder {
|
|
|
102
106
|
}
|
|
103
107
|
return dbgReturn(ans);
|
|
104
108
|
}
|
|
105
|
-
_visitIter(node) {
|
|
106
|
-
const { children } = node;
|
|
107
|
-
if (node.isOptional()) {
|
|
108
|
-
if (children.length === 0) {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
return this.toAst(children[0]);
|
|
112
|
-
}
|
|
113
|
-
return children.map(c => this.toAst(c));
|
|
114
|
-
}
|
|
115
109
|
toAst(nodeOrResult) {
|
|
116
110
|
let node = nodeOrResult;
|
|
117
111
|
if (typeof nodeOrResult.succeeded === 'function') {
|
|
118
112
|
const matchResult = nodeOrResult;
|
|
119
113
|
assert(matchResult.succeeded(), 'Cannot convert failed match result to AST');
|
|
120
|
-
node =
|
|
114
|
+
node = matchResult.grammar.getCstRoot();
|
|
121
115
|
}
|
|
122
116
|
let ans;
|
|
123
117
|
this._depth++;
|
|
124
118
|
if (node.isTerminal()) {
|
|
125
119
|
ans = this._visitTerminal(node);
|
|
126
120
|
}
|
|
127
|
-
else if (node.
|
|
128
|
-
ans = this.
|
|
121
|
+
else if (node.isOptional()) {
|
|
122
|
+
ans = node.ifPresent((child) => this.toAst(child), () => null);
|
|
123
|
+
}
|
|
124
|
+
else if (node.isList() || node.isSeq()) {
|
|
125
|
+
ans = node.children.map(c => this.toAst(c));
|
|
129
126
|
}
|
|
130
127
|
else {
|
|
131
|
-
assert(node.isNonterminal(), `Unknown node type: ${node.
|
|
128
|
+
assert(node.isNonterminal(), `Unknown node type: ${node.type}`);
|
|
132
129
|
this.currNode = node;
|
|
133
130
|
ans =
|
|
134
131
|
typeof this._mapping[node.ctorName] === 'function'
|
package/dist/src/compat.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ export interface Grammar extends WasmGrammar {
|
|
|
5
5
|
}
|
|
6
6
|
export declare function grammar(source: string): Grammar;
|
|
7
7
|
export declare function grammars(source: string): Record<string, Grammar>;
|
|
8
|
+
export { createToAst } from './createToAst.ts';
|
|
8
9
|
//# sourceMappingURL=compat.d.ts.map
|
package/dist/src/compat.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { CstNode, CstNodeChildren, MatchResult, NonterminalNode, TerminalNode } from './miniohm.ts';
|
|
2
|
+
export type AstNodeTemplate<R> = {
|
|
3
|
+
[property: string]: number | string | boolean | object | null | ((children: CstNodeChildren) => R);
|
|
4
|
+
};
|
|
5
|
+
export type AstMapping<R> = Record<string, AstNodeTemplate<R> | number | ((...children: CstNodeChildren) => R)>;
|
|
6
|
+
export declare function createToAst<TNode = any>(mapping: AstMapping<TNode>, opts?: {
|
|
7
|
+
debug?: boolean;
|
|
8
|
+
}): (nodeOrResult: MatchResult | CstNode) => TNode;
|
|
9
|
+
export declare class AstBuilder<TNode = any> {
|
|
10
|
+
currNode?: CstNode;
|
|
11
|
+
private _mapping;
|
|
12
|
+
private _depth;
|
|
13
|
+
private _debug;
|
|
14
|
+
constructor(mapping: AstMapping<TNode>, opts?: {
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
});
|
|
17
|
+
private _debugLog;
|
|
18
|
+
_visitTerminal(node: TerminalNode): string;
|
|
19
|
+
_visitNonterminal(node: NonterminalNode): unknown;
|
|
20
|
+
toAst(nodeOrResult: MatchResult | CstNode): TNode;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=createToAst.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { assert, checkNotNull } from "./assert.js";
|
|
2
|
+
function childAt(children, idx, ruleName, propName = '') {
|
|
3
|
+
if (idx > children.length) {
|
|
4
|
+
const path = propName ? `${ruleName}.${propName}` : ruleName;
|
|
5
|
+
throw new Error(`${path}: Child index ${idx} out of range`);
|
|
6
|
+
}
|
|
7
|
+
return checkNotNull(children[idx]);
|
|
8
|
+
}
|
|
9
|
+
export function createToAst(mapping, opts = {}) {
|
|
10
|
+
const builder = new AstBuilder(mapping, opts);
|
|
11
|
+
return (nodeOrResult) => builder.toAst(nodeOrResult);
|
|
12
|
+
}
|
|
13
|
+
export class AstBuilder {
|
|
14
|
+
currNode;
|
|
15
|
+
_mapping;
|
|
16
|
+
_depth = -1;
|
|
17
|
+
_debug = false;
|
|
18
|
+
constructor(mapping, opts = {}) {
|
|
19
|
+
const handleListOf = (child) => this.toAst(child);
|
|
20
|
+
const handleEmptyListOf = () => [];
|
|
21
|
+
const handleNonemptyListOf = (first, sepAndElemList) => {
|
|
22
|
+
assert(!!sepAndElemList?.isList(), 'Expected a ListNode');
|
|
23
|
+
return [this.toAst(first), ...sepAndElemList.collect((_, elem) => this.toAst(elem))];
|
|
24
|
+
};
|
|
25
|
+
this._mapping = {
|
|
26
|
+
listOf: handleListOf,
|
|
27
|
+
ListOf: handleListOf,
|
|
28
|
+
emptyListOf: handleEmptyListOf,
|
|
29
|
+
EmptyListOf: handleEmptyListOf,
|
|
30
|
+
nonemptyListOf: handleNonemptyListOf,
|
|
31
|
+
NonemptyListOf: handleNonemptyListOf,
|
|
32
|
+
...mapping,
|
|
33
|
+
};
|
|
34
|
+
this._debug = opts.debug ?? false;
|
|
35
|
+
}
|
|
36
|
+
_debugLog(...data) {
|
|
37
|
+
if (this._debug)
|
|
38
|
+
console.log(' '.repeat(this._depth), ...data);
|
|
39
|
+
}
|
|
40
|
+
_visitTerminal(node) {
|
|
41
|
+
return node.sourceString;
|
|
42
|
+
}
|
|
43
|
+
_visitNonterminal(node) {
|
|
44
|
+
const { children, ctorName } = node;
|
|
45
|
+
const mapping = this._mapping;
|
|
46
|
+
this._debugLog(`> ${ctorName}`);
|
|
47
|
+
const dbgReturn = (val) => {
|
|
48
|
+
this._debugLog(`| ${ctorName} DONE`);
|
|
49
|
+
return val;
|
|
50
|
+
};
|
|
51
|
+
// without customization
|
|
52
|
+
if (!Object.hasOwn(mapping, ctorName)) {
|
|
53
|
+
// lexical rule
|
|
54
|
+
if (node.isLexical()) {
|
|
55
|
+
return node.sourceString;
|
|
56
|
+
}
|
|
57
|
+
// singular node (e.g. only surrounded by literals or lookaheads)
|
|
58
|
+
const realChildren = children.filter(c => !c.isTerminal());
|
|
59
|
+
if (realChildren.length === 1) {
|
|
60
|
+
return dbgReturn(this.toAst(realChildren[0]));
|
|
61
|
+
}
|
|
62
|
+
// rest: terms with multiple children
|
|
63
|
+
}
|
|
64
|
+
// direct forward
|
|
65
|
+
if (typeof mapping[ctorName] === 'number') {
|
|
66
|
+
const idx = mapping[ctorName];
|
|
67
|
+
return dbgReturn(this.toAst(childAt(children, idx, ctorName)));
|
|
68
|
+
}
|
|
69
|
+
assert(typeof mapping[ctorName] !== 'function', "shouldn't be possible");
|
|
70
|
+
// named/mapped children or unnamed children ('0', '1', '2', ...)
|
|
71
|
+
const propMap = mapping[ctorName] || children;
|
|
72
|
+
const ans = {
|
|
73
|
+
type: ctorName,
|
|
74
|
+
};
|
|
75
|
+
// eslint-disable-next-line guard-for-in
|
|
76
|
+
for (const prop in propMap) {
|
|
77
|
+
const mappedProp = mapping[ctorName] && mapping[ctorName][prop];
|
|
78
|
+
if (typeof mappedProp === 'number') {
|
|
79
|
+
// direct forward
|
|
80
|
+
ans[prop] = this.toAst(childAt(children, mappedProp, ctorName, prop));
|
|
81
|
+
}
|
|
82
|
+
else if (typeof mappedProp === 'string' ||
|
|
83
|
+
typeof mappedProp === 'boolean' ||
|
|
84
|
+
mappedProp === null) {
|
|
85
|
+
// primitive value
|
|
86
|
+
ans[prop] = mappedProp;
|
|
87
|
+
}
|
|
88
|
+
else if (typeof mappedProp === 'object' && mappedProp instanceof Number) {
|
|
89
|
+
// primitive number (must be unboxed)
|
|
90
|
+
ans[prop] = Number(mappedProp);
|
|
91
|
+
}
|
|
92
|
+
else if (typeof mappedProp === 'function') {
|
|
93
|
+
// computed value
|
|
94
|
+
ans[prop] = mappedProp.call(this, children);
|
|
95
|
+
}
|
|
96
|
+
else if (mappedProp === undefined) {
|
|
97
|
+
const child = children[Number(prop)];
|
|
98
|
+
if (child && !child.isTerminal()) {
|
|
99
|
+
ans[prop] = this.toAst(child);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// delete predefined 'type' properties, like 'type', if explicitly removed
|
|
103
|
+
delete ans[prop];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return dbgReturn(ans);
|
|
108
|
+
}
|
|
109
|
+
toAst(nodeOrResult) {
|
|
110
|
+
let node = nodeOrResult;
|
|
111
|
+
if (typeof nodeOrResult.succeeded === 'function') {
|
|
112
|
+
const matchResult = nodeOrResult;
|
|
113
|
+
assert(matchResult.succeeded(), 'Cannot convert failed match result to AST');
|
|
114
|
+
node = matchResult.grammar.getCstRoot();
|
|
115
|
+
}
|
|
116
|
+
let ans;
|
|
117
|
+
this._depth++;
|
|
118
|
+
if (node.isTerminal()) {
|
|
119
|
+
ans = this._visitTerminal(node);
|
|
120
|
+
}
|
|
121
|
+
else if (node.isOptional()) {
|
|
122
|
+
ans = node.ifPresent((child) => this.toAst(child), () => null);
|
|
123
|
+
}
|
|
124
|
+
else if (node.isList() || node.isSeq()) {
|
|
125
|
+
ans = node.children.map(c => this.toAst(c));
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
assert(node.isNonterminal(), `Unknown node type: ${node.type}`);
|
|
129
|
+
this.currNode = node;
|
|
130
|
+
ans =
|
|
131
|
+
typeof this._mapping[node.ctorName] === 'function'
|
|
132
|
+
? this._mapping[node.ctorName].apply(this, node.children)
|
|
133
|
+
: this._visitNonterminal(node);
|
|
134
|
+
this.currNode = undefined;
|
|
135
|
+
}
|
|
136
|
+
this._depth--;
|
|
137
|
+
return ans;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=createToAst.js.map
|
package/dist/src/miniohm.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export declare const CstNodeType: {
|
|
2
|
+
readonly NONTERMINAL: 0;
|
|
3
|
+
readonly TERMINAL: 1;
|
|
4
|
+
readonly LIST: 2;
|
|
5
|
+
readonly OPT: 3;
|
|
6
|
+
readonly SEQ: 4;
|
|
7
|
+
};
|
|
8
|
+
export type CstNodeType = (typeof CstNodeType)[keyof typeof CstNodeType];
|
|
1
9
|
export declare class WasmGrammar {
|
|
2
10
|
name: string;
|
|
3
11
|
private _instance?;
|
|
@@ -30,62 +38,184 @@ export declare class WasmGrammar {
|
|
|
30
38
|
private _fillInputBuffer;
|
|
31
39
|
getRightmostFailurePosition(): number;
|
|
32
40
|
}
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
export interface MatchContext {
|
|
42
|
+
ruleNames: string[];
|
|
43
|
+
view: DataView;
|
|
44
|
+
input: string;
|
|
45
|
+
}
|
|
46
|
+
export type CstNode = NonterminalNode | TerminalNode | ListNode | OptNode | SeqNode;
|
|
47
|
+
export type CstNodeChildren = readonly CstNode[];
|
|
48
|
+
export interface CstNodeBase {
|
|
49
|
+
ctorName: string;
|
|
50
|
+
source: {
|
|
51
|
+
startIdx: number;
|
|
52
|
+
endIdx: number;
|
|
53
|
+
};
|
|
54
|
+
sourceString: string;
|
|
55
|
+
matchLength: number;
|
|
56
|
+
isNonterminal(): this is NonterminalNode;
|
|
57
|
+
isTerminal(): this is TerminalNode;
|
|
58
|
+
isOptional(): this is OptNode;
|
|
59
|
+
isSeq(): this is SeqNode;
|
|
60
|
+
isList(): this is ListNode;
|
|
61
|
+
}
|
|
62
|
+
export interface NonterminalNode<TChildren extends CstNodeChildren = CstNodeChildren> extends CstNodeBase {
|
|
63
|
+
type: typeof CstNodeType.NONTERMINAL;
|
|
64
|
+
ctorName: string;
|
|
65
|
+
leadingSpaces?: NonterminalNode;
|
|
66
|
+
children: TChildren;
|
|
67
|
+
isSyntactic(ruleName?: string): boolean;
|
|
68
|
+
isLexical(ruleName?: string): boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface TerminalNode extends CstNodeBase {
|
|
71
|
+
type: typeof CstNodeType.TERMINAL;
|
|
72
|
+
ctorName: '_terminal';
|
|
73
|
+
leadingSpaces?: NonterminalNode;
|
|
74
|
+
value: string;
|
|
75
|
+
}
|
|
76
|
+
export interface ListNode<TNode extends CstNode = CstNode> extends CstNodeBase {
|
|
77
|
+
type: typeof CstNodeType.LIST;
|
|
78
|
+
ctorName: '_list';
|
|
79
|
+
children: readonly TNode[];
|
|
80
|
+
collect: <R>(cb: (...children: CstNode[]) => R) => R[];
|
|
81
|
+
}
|
|
82
|
+
export interface OptNode<TChild extends CstNode = CstNode> extends CstNodeBase {
|
|
83
|
+
type: typeof CstNodeType.OPT;
|
|
84
|
+
ctorName: '_opt';
|
|
85
|
+
children: [] | [TChild];
|
|
86
|
+
ifPresent<R>(consume: TChild extends SeqNode<infer T> ? (...children: T) => R : (child: TChild) => R, orElse?: () => R): R;
|
|
87
|
+
ifPresent<R>(consume: TChild extends SeqNode<infer T> ? (...children: T) => R : (child: TChild) => R): R | undefined;
|
|
88
|
+
isPresent(): boolean;
|
|
89
|
+
isEmpty(): boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface SeqNode<TChildren extends CstNodeChildren = CstNodeChildren> extends CstNodeBase {
|
|
92
|
+
type: typeof CstNodeType.SEQ;
|
|
93
|
+
ctorName: '_seq';
|
|
94
|
+
children: TChildren;
|
|
95
|
+
unpack: <R>(cb: (...children: TChildren) => R) => R;
|
|
96
|
+
}
|
|
97
|
+
export declare class CstNodeImpl implements CstNodeBase {
|
|
98
|
+
_ctx: MatchContext;
|
|
99
|
+
_children?: CstNodeChildren;
|
|
37
100
|
_base: number;
|
|
38
|
-
_input: string;
|
|
39
101
|
startIdx: number;
|
|
40
|
-
leadingSpaces
|
|
102
|
+
leadingSpaces?: NonterminalNode;
|
|
41
103
|
source: {
|
|
42
104
|
startIdx: number;
|
|
43
105
|
endIdx: number;
|
|
44
106
|
};
|
|
45
|
-
constructor(
|
|
46
|
-
get type():
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
107
|
+
constructor(ctx: MatchContext, ptr: number, startIdx: number);
|
|
108
|
+
get type(): CstNodeType;
|
|
109
|
+
private get matchRecordType();
|
|
110
|
+
isNonterminal(): this is NonterminalNode;
|
|
111
|
+
isTerminal(): this is TerminalNode;
|
|
112
|
+
isList(): this is ListNode;
|
|
113
|
+
isOptional(): this is OptNode;
|
|
114
|
+
isSeq(): this is SeqNode;
|
|
51
115
|
get ctorName(): string;
|
|
52
|
-
get ruleName(): string;
|
|
53
116
|
get count(): number;
|
|
54
117
|
get matchLength(): number;
|
|
55
118
|
get _typeAndDetails(): number;
|
|
56
119
|
get arity(): number;
|
|
57
|
-
get children():
|
|
58
|
-
_computeChildren():
|
|
120
|
+
get children(): CstNodeChildren;
|
|
121
|
+
_computeChildren(): CstNodeImpl[];
|
|
59
122
|
get sourceString(): string;
|
|
60
123
|
isSyntactic(ruleName?: string): boolean;
|
|
61
124
|
isLexical(ruleName?: string): boolean;
|
|
62
125
|
toString(): string;
|
|
63
|
-
collect<T>(callbackFn: (...args: CstNode[]) => T): T[];
|
|
64
|
-
when<T>({ Some, None }: {
|
|
65
|
-
Some: (...args: CstNode[]) => T;
|
|
66
|
-
None: () => T;
|
|
67
|
-
}): T;
|
|
68
126
|
}
|
|
69
|
-
export declare
|
|
127
|
+
export declare class SeqNodeImpl<TChildren extends CstNodeChildren = CstNodeChildren> implements SeqNode<TChildren> {
|
|
128
|
+
type: 4;
|
|
129
|
+
ctorName: "_seq";
|
|
130
|
+
children: TChildren;
|
|
131
|
+
source: {
|
|
132
|
+
startIdx: number;
|
|
133
|
+
endIdx: number;
|
|
134
|
+
};
|
|
135
|
+
sourceString: string;
|
|
136
|
+
get matchLength(): number;
|
|
137
|
+
constructor(children: TChildren, source: {
|
|
138
|
+
startIdx: number;
|
|
139
|
+
endIdx: number;
|
|
140
|
+
}, sourceString: string);
|
|
141
|
+
isNonterminal(): this is NonterminalNode;
|
|
142
|
+
isTerminal(): this is TerminalNode;
|
|
143
|
+
isOptional(): this is OptNode;
|
|
144
|
+
isSeq(): this is SeqNode;
|
|
145
|
+
isList(): this is ListNode;
|
|
146
|
+
unpack<R>(cb: (...args: TChildren) => R): R;
|
|
147
|
+
}
|
|
148
|
+
export declare class ListNodeImpl<TNode extends CstNode = CstNode> implements ListNode<TNode> {
|
|
149
|
+
type: 2;
|
|
150
|
+
ctorName: "_list";
|
|
151
|
+
children: readonly TNode[];
|
|
152
|
+
source: {
|
|
153
|
+
startIdx: number;
|
|
154
|
+
endIdx: number;
|
|
155
|
+
};
|
|
156
|
+
sourceString: string;
|
|
157
|
+
get matchLength(): number;
|
|
158
|
+
constructor(children: readonly TNode[], source: {
|
|
159
|
+
startIdx: number;
|
|
160
|
+
endIdx: number;
|
|
161
|
+
}, sourceString: string);
|
|
162
|
+
isNonterminal(): this is NonterminalNode;
|
|
163
|
+
isTerminal(): this is TerminalNode;
|
|
164
|
+
isList(): this is ListNode;
|
|
165
|
+
isOptional(): this is OptNode;
|
|
166
|
+
isSeq(): this is SeqNode;
|
|
167
|
+
collect<R>(cb: (...args: CstNode[]) => R): R[];
|
|
168
|
+
}
|
|
169
|
+
export declare class OptNodeImpl<TNode extends CstNode = CstNode> implements OptNode<TNode> {
|
|
170
|
+
type: 3;
|
|
171
|
+
ctorName: "_opt";
|
|
172
|
+
children: [] | [TNode];
|
|
173
|
+
source: {
|
|
174
|
+
startIdx: number;
|
|
175
|
+
endIdx: number;
|
|
176
|
+
};
|
|
177
|
+
sourceString: string;
|
|
178
|
+
get matchLength(): number;
|
|
179
|
+
constructor(child: TNode | undefined, source: {
|
|
180
|
+
startIdx: number;
|
|
181
|
+
endIdx: number;
|
|
182
|
+
}, sourceString: string);
|
|
183
|
+
isNonterminal(): this is NonterminalNode;
|
|
184
|
+
isTerminal(): this is TerminalNode;
|
|
185
|
+
isList(): this is ListNode;
|
|
186
|
+
isOptional(): this is OptNode;
|
|
187
|
+
isSeq(): this is SeqNode;
|
|
188
|
+
ifPresent<R>(consume: TNode extends SeqNode<infer T> ? (...children: T) => R : (child: TNode) => R, orElse?: () => R): R | undefined;
|
|
189
|
+
isPresent(): boolean;
|
|
190
|
+
isEmpty(): boolean;
|
|
191
|
+
}
|
|
70
192
|
export declare class MatchResult {
|
|
71
193
|
grammar: WasmGrammar;
|
|
72
|
-
input: string;
|
|
73
194
|
startExpr: string;
|
|
74
|
-
|
|
195
|
+
_ctx: MatchContext;
|
|
196
|
+
_succeeded: boolean;
|
|
197
|
+
constructor(grammar: WasmGrammar, startExpr: string, ctx: MatchContext, succeeded: boolean);
|
|
198
|
+
[Symbol.dispose](): void;
|
|
199
|
+
detach(): void;
|
|
200
|
+
succeeded(): this is SucceededMatchResult;
|
|
201
|
+
failed(): this is FailedMatchResult;
|
|
202
|
+
toString(): string;
|
|
203
|
+
use<T>(cb: (r: MatchResult) => T): T;
|
|
204
|
+
}
|
|
205
|
+
declare class SucceededMatchResult extends MatchResult {
|
|
206
|
+
_cst: CstNode;
|
|
207
|
+
constructor(grammar: WasmGrammar, startExpr: string, ctx: MatchContext, succeeded: boolean);
|
|
208
|
+
}
|
|
209
|
+
declare class FailedMatchResult extends MatchResult {
|
|
210
|
+
constructor(grammar: WasmGrammar, startExpr: string, ctx: MatchContext, succeeded: boolean, rightmostFailurePosition: number, optRecordedFailures?: any);
|
|
75
211
|
_rightmostFailurePosition: number;
|
|
76
212
|
_rightmostFailures: any;
|
|
77
213
|
shortMessage?: string;
|
|
78
214
|
message?: string;
|
|
79
|
-
constructor(matcher: WasmGrammar, input: string, startExpr: string, cst: CstNode | null, rightmostFailurePosition: number, optRecordedFailures?: any);
|
|
80
|
-
[Symbol.dispose](): void;
|
|
81
|
-
detach(): void;
|
|
82
|
-
succeeded(): boolean;
|
|
83
|
-
failed(): boolean;
|
|
84
215
|
getRightmostFailurePosition(): number;
|
|
85
216
|
getRightmostFailures(): any;
|
|
86
|
-
toString(): string;
|
|
87
217
|
getExpectedText(): string;
|
|
88
218
|
getInterval(): void;
|
|
89
|
-
use<T>(cb: (r: MatchResult) => T): T;
|
|
90
219
|
}
|
|
220
|
+
export {};
|
|
91
221
|
//# sourceMappingURL=miniohm.d.ts.map
|
package/dist/src/miniohm.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { assert, checkNotNull } from "./assert.js";
|
|
2
|
-
const
|
|
3
|
-
|
|
2
|
+
const MATCH_RECORD_TYPE_MASK = 0b11;
|
|
3
|
+
// A MatchRecord is the representation of a CstNode in Wasm linear memory.
|
|
4
|
+
const MatchRecordType = {
|
|
4
5
|
NONTERMINAL: 0,
|
|
5
6
|
TERMINAL: 1,
|
|
6
7
|
ITER_FLAG: 2,
|
|
7
8
|
OPTIONAL: 3,
|
|
8
9
|
};
|
|
10
|
+
// A _CST node_ is the user-facing representation, built from a match record.
|
|
11
|
+
export const CstNodeType = {
|
|
12
|
+
NONTERMINAL: 0,
|
|
13
|
+
TERMINAL: 1,
|
|
14
|
+
LIST: 2,
|
|
15
|
+
OPT: 3,
|
|
16
|
+
SEQ: 4,
|
|
17
|
+
};
|
|
9
18
|
const compileOptions = {
|
|
10
19
|
builtins: ['js-string'],
|
|
11
20
|
};
|
|
@@ -189,7 +198,12 @@ export class WasmGrammar {
|
|
|
189
198
|
debugger; // eslint-disable-line no-debugger
|
|
190
199
|
const ruleId = checkNotNull(this._ruleIds.get(ruleName || this._ruleNames[0]), `unknown rule: '${ruleName}'`);
|
|
191
200
|
const succeeded = this._instance.exports.match(input, ruleId);
|
|
192
|
-
const
|
|
201
|
+
const ctx = {
|
|
202
|
+
ruleNames: this._ruleNames,
|
|
203
|
+
view: new DataView(this._instance.exports.memory.buffer),
|
|
204
|
+
input: this._instance.exports.input.value,
|
|
205
|
+
};
|
|
206
|
+
const result = createMatchResult(this, ruleName || this._ruleNames[0], ctx, !!succeeded);
|
|
193
207
|
result.detach = this._detachMatchResult.bind(this, result);
|
|
194
208
|
this._resultStack.push(result);
|
|
195
209
|
return result;
|
|
@@ -200,13 +214,19 @@ export class WasmGrammar {
|
|
|
200
214
|
getCstRoot() {
|
|
201
215
|
const { exports } = this._instance;
|
|
202
216
|
const { buffer } = exports.memory;
|
|
203
|
-
const
|
|
217
|
+
const ctx = {
|
|
218
|
+
ruleNames: this._ruleNames,
|
|
219
|
+
view: new DataView(buffer),
|
|
220
|
+
input: exports.input.value,
|
|
221
|
+
};
|
|
222
|
+
const firstNode = new CstNodeImpl(ctx, exports.bindingsAt(0), 0);
|
|
223
|
+
assert(firstNode.isNonterminal());
|
|
204
224
|
if (firstNode.ctorName !== '$spaces') {
|
|
205
225
|
return firstNode;
|
|
206
226
|
}
|
|
207
|
-
assert(exports.getBindingsLength() > 1);
|
|
227
|
+
assert(exports.getBindingsLength() > 1 && firstNode.ctorName === '$spaces');
|
|
208
228
|
const nextAddr = exports.bindingsAt(1);
|
|
209
|
-
const root = new
|
|
229
|
+
const root = new CstNodeImpl(ctx, nextAddr, firstNode.matchLength);
|
|
210
230
|
root.leadingSpaces = firstNode;
|
|
211
231
|
return root;
|
|
212
232
|
}
|
|
@@ -222,33 +242,40 @@ export class WasmGrammar {
|
|
|
222
242
|
return this._instance.exports.rightmostFailurePos.value;
|
|
223
243
|
}
|
|
224
244
|
}
|
|
225
|
-
export class
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
_children;
|
|
245
|
+
export class CstNodeImpl {
|
|
246
|
+
_ctx;
|
|
247
|
+
_children = undefined;
|
|
229
248
|
_base;
|
|
230
|
-
_input;
|
|
231
249
|
startIdx;
|
|
232
|
-
leadingSpaces;
|
|
250
|
+
leadingSpaces = undefined;
|
|
233
251
|
source;
|
|
234
|
-
constructor(
|
|
252
|
+
constructor(ctx, ptr, startIdx) {
|
|
235
253
|
// Non-enumerable properties
|
|
236
254
|
Object.defineProperties(this, {
|
|
237
|
-
|
|
238
|
-
_view: { value: dataView },
|
|
255
|
+
_ctx: { value: ctx },
|
|
239
256
|
_children: { writable: true },
|
|
240
257
|
});
|
|
241
258
|
this._base = ptr;
|
|
242
|
-
this._input = input;
|
|
243
259
|
this.startIdx = startIdx;
|
|
244
|
-
this.leadingSpaces = undefined;
|
|
245
260
|
this.source = {
|
|
246
261
|
startIdx,
|
|
247
262
|
endIdx: startIdx + this.sourceString.length,
|
|
248
263
|
};
|
|
249
264
|
}
|
|
250
265
|
get type() {
|
|
251
|
-
|
|
266
|
+
switch (this._typeAndDetails & MATCH_RECORD_TYPE_MASK) {
|
|
267
|
+
case MatchRecordType.NONTERMINAL:
|
|
268
|
+
return CstNodeType.NONTERMINAL;
|
|
269
|
+
case MatchRecordType.TERMINAL:
|
|
270
|
+
return CstNodeType.TERMINAL;
|
|
271
|
+
case MatchRecordType.ITER_FLAG:
|
|
272
|
+
return CstNodeType.LIST;
|
|
273
|
+
default:
|
|
274
|
+
throw new Error('unreachable');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
get matchRecordType() {
|
|
278
|
+
return (this._typeAndDetails & MATCH_RECORD_TYPE_MASK);
|
|
252
279
|
}
|
|
253
280
|
isNonterminal() {
|
|
254
281
|
return this.type === CstNodeType.NONTERMINAL;
|
|
@@ -256,50 +283,90 @@ export class CstNode {
|
|
|
256
283
|
isTerminal() {
|
|
257
284
|
return this.type === CstNodeType.TERMINAL;
|
|
258
285
|
}
|
|
259
|
-
|
|
260
|
-
return
|
|
286
|
+
isList() {
|
|
287
|
+
return this.type === CstNodeType.LIST;
|
|
261
288
|
}
|
|
262
289
|
isOptional() {
|
|
263
|
-
return this.type === CstNodeType.
|
|
290
|
+
return this.type === CstNodeType.OPT;
|
|
264
291
|
}
|
|
265
|
-
|
|
266
|
-
return this.
|
|
292
|
+
isSeq() {
|
|
293
|
+
return this.type === CstNodeType.SEQ;
|
|
267
294
|
}
|
|
268
|
-
get
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
295
|
+
get ctorName() {
|
|
296
|
+
switch (this.type) {
|
|
297
|
+
case CstNodeType.NONTERMINAL: {
|
|
298
|
+
const { ruleNames, view } = this._ctx;
|
|
299
|
+
const ruleId = view.getInt32(this._base + 8, true) >>> 2;
|
|
300
|
+
return ruleNames[ruleId].split('<')[0];
|
|
301
|
+
}
|
|
302
|
+
case CstNodeType.TERMINAL:
|
|
303
|
+
return '_terminal';
|
|
304
|
+
case CstNodeType.LIST:
|
|
305
|
+
return '_list';
|
|
306
|
+
case CstNodeType.OPT:
|
|
307
|
+
return '_opt';
|
|
308
|
+
case CstNodeType.SEQ:
|
|
309
|
+
return '_seq';
|
|
310
|
+
}
|
|
272
311
|
}
|
|
273
312
|
get count() {
|
|
274
|
-
return this.
|
|
313
|
+
return this._ctx.view.getUint32(this._base, true);
|
|
275
314
|
}
|
|
276
315
|
get matchLength() {
|
|
277
|
-
return this.
|
|
316
|
+
return this._ctx.view.getUint32(this._base + 4, true);
|
|
278
317
|
}
|
|
279
318
|
get _typeAndDetails() {
|
|
280
|
-
return this.
|
|
319
|
+
return this._ctx.view.getInt32(this._base + 8, true);
|
|
281
320
|
}
|
|
282
321
|
get arity() {
|
|
283
322
|
return this._typeAndDetails >>> 2;
|
|
284
323
|
}
|
|
285
324
|
get children() {
|
|
286
325
|
if (!this._children) {
|
|
287
|
-
this._children = this._computeChildren()
|
|
326
|
+
this._children = this._computeChildren().map((n) => {
|
|
327
|
+
const { matchRecordType } = n;
|
|
328
|
+
if (matchRecordType === MatchRecordType.OPTIONAL) {
|
|
329
|
+
const child = n.children.length <= 1
|
|
330
|
+
? n.children[0]
|
|
331
|
+
: new SeqNodeImpl(n.children, n.source, n.sourceString);
|
|
332
|
+
return new OptNodeImpl(child, n.source, n.sourceString);
|
|
333
|
+
}
|
|
334
|
+
else if (matchRecordType === MatchRecordType.ITER_FLAG) {
|
|
335
|
+
if (n.arity <= 1) {
|
|
336
|
+
return new ListNodeImpl(n.children, n.source, n.sourceString);
|
|
337
|
+
}
|
|
338
|
+
const arr = [];
|
|
339
|
+
let startIdx = n.startIdx;
|
|
340
|
+
for (let i = 0; i < n.children.length; i += n.arity) {
|
|
341
|
+
// FIXME: We don't need any of this nonsense if we actually build the SeqNodes at parse time.
|
|
342
|
+
const seqChildren = n.children.slice(i, i + n.arity);
|
|
343
|
+
const endIdx = checkNotNull(seqChildren.at(-1)).source.endIdx;
|
|
344
|
+
const sourceString = n._ctx.input.slice(startIdx, endIdx);
|
|
345
|
+
arr.push(new SeqNodeImpl(seqChildren, { startIdx, endIdx }, sourceString));
|
|
346
|
+
startIdx = endIdx;
|
|
347
|
+
}
|
|
348
|
+
assert(startIdx === n.source.endIdx);
|
|
349
|
+
return new ListNodeImpl(arr, n.source, n.sourceString);
|
|
350
|
+
}
|
|
351
|
+
return n; // FIXME
|
|
352
|
+
});
|
|
288
353
|
}
|
|
289
354
|
return this._children;
|
|
290
355
|
}
|
|
291
356
|
_computeChildren() {
|
|
292
357
|
const children = [];
|
|
358
|
+
const { ruleNames, view, input } = this._ctx;
|
|
293
359
|
let spaces;
|
|
294
360
|
let { startIdx } = this;
|
|
295
361
|
for (let i = 0; i < this.count; i++) {
|
|
296
362
|
const slotOffset = this._base + 16 + i * 4;
|
|
297
|
-
const ptr =
|
|
363
|
+
const ptr = view.getUint32(slotOffset, true);
|
|
298
364
|
// TODO: Avoid allocating $spaces nodes altogether?
|
|
299
|
-
const node = new
|
|
300
|
-
if (node.
|
|
365
|
+
const node = new CstNodeImpl(this._ctx, ptr, startIdx);
|
|
366
|
+
if (node.matchRecordType === MatchRecordType.NONTERMINAL &&
|
|
367
|
+
node.ctorName === '$spaces') {
|
|
301
368
|
assert(!spaces, 'Multiple $spaces nodes found');
|
|
302
|
-
spaces = node;
|
|
369
|
+
spaces = node; // FIXME
|
|
303
370
|
}
|
|
304
371
|
else {
|
|
305
372
|
if (spaces) {
|
|
@@ -314,70 +381,161 @@ export class CstNode {
|
|
|
314
381
|
return children;
|
|
315
382
|
}
|
|
316
383
|
get sourceString() {
|
|
317
|
-
return this.
|
|
384
|
+
return this._ctx.input.slice(this.startIdx, this.startIdx + this.matchLength);
|
|
318
385
|
}
|
|
319
386
|
isSyntactic(ruleName) {
|
|
320
|
-
|
|
387
|
+
assert(this.isNonterminal(), 'Not a nonterminal');
|
|
388
|
+
const firstChar = this.ctorName[0];
|
|
321
389
|
return firstChar === firstChar.toUpperCase();
|
|
322
390
|
}
|
|
323
391
|
isLexical(ruleName) {
|
|
392
|
+
assert(this.isNonterminal(), 'Not a nonterminal');
|
|
324
393
|
return !this.isSyntactic(ruleName);
|
|
325
394
|
}
|
|
326
395
|
toString() {
|
|
327
|
-
const ctorName = this.isTerminal() ? '_terminal' : this.
|
|
396
|
+
const ctorName = this.isTerminal() ? '_terminal' : this.isSeq() ? '_iter' : this.ctorName;
|
|
328
397
|
const { sourceString, startIdx } = this;
|
|
329
398
|
return `CstNode {ctorName: ${ctorName}, sourceString: ${sourceString}, startIdx: ${startIdx} }`;
|
|
330
399
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
return
|
|
400
|
+
}
|
|
401
|
+
export class SeqNodeImpl {
|
|
402
|
+
type = CstNodeType.SEQ;
|
|
403
|
+
ctorName = '_seq';
|
|
404
|
+
children;
|
|
405
|
+
source;
|
|
406
|
+
sourceString;
|
|
407
|
+
get matchLength() {
|
|
408
|
+
return this.sourceString.length;
|
|
409
|
+
}
|
|
410
|
+
constructor(children, source, sourceString) {
|
|
411
|
+
this.children = children;
|
|
412
|
+
this.source = source;
|
|
413
|
+
this.sourceString = sourceString;
|
|
414
|
+
}
|
|
415
|
+
isNonterminal() {
|
|
416
|
+
return false;
|
|
340
417
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
418
|
+
isTerminal() {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
isOptional() {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
isSeq() {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
isList() {
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
unpack(cb) {
|
|
431
|
+
assert(cb.length === this.children.length, `bad arity: expected ${this.children.length}, got ${cb.length}`);
|
|
432
|
+
return cb.call(null, ...this.children); // FIXME
|
|
347
433
|
}
|
|
348
434
|
}
|
|
349
|
-
export
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
)
|
|
356
|
-
|
|
435
|
+
export class ListNodeImpl {
|
|
436
|
+
type = CstNodeType.LIST;
|
|
437
|
+
ctorName = '_list';
|
|
438
|
+
children;
|
|
439
|
+
source;
|
|
440
|
+
sourceString;
|
|
441
|
+
get matchLength() {
|
|
442
|
+
return this.sourceString.length;
|
|
443
|
+
}
|
|
444
|
+
constructor(children, source, sourceString) {
|
|
445
|
+
this.children = children;
|
|
446
|
+
this.source = source;
|
|
447
|
+
this.sourceString = sourceString;
|
|
448
|
+
}
|
|
449
|
+
isNonterminal() {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
isTerminal() {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
isList() {
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
isOptional() {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
isSeq() {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
collect(cb) {
|
|
465
|
+
return this.children.map(c => {
|
|
466
|
+
return c?.isSeq() ? c.unpack(cb) : cb(c);
|
|
467
|
+
});
|
|
468
|
+
}
|
|
357
469
|
}
|
|
470
|
+
export class OptNodeImpl {
|
|
471
|
+
type = CstNodeType.OPT;
|
|
472
|
+
ctorName = '_opt';
|
|
473
|
+
children;
|
|
474
|
+
source;
|
|
475
|
+
sourceString;
|
|
476
|
+
get matchLength() {
|
|
477
|
+
return this.sourceString.length;
|
|
478
|
+
}
|
|
479
|
+
constructor(child, source, sourceString) {
|
|
480
|
+
this.children = child ? [child] : [];
|
|
481
|
+
this.source = source;
|
|
482
|
+
this.sourceString = sourceString;
|
|
483
|
+
}
|
|
484
|
+
isNonterminal() {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
isTerminal() {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
isList() {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
isOptional() {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
isSeq() {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
ifPresent(consume, orElse) {
|
|
500
|
+
const child = this.children[0];
|
|
501
|
+
if (child) {
|
|
502
|
+
return child.isSeq()
|
|
503
|
+
? child.unpack(consume)
|
|
504
|
+
: consume(child);
|
|
505
|
+
}
|
|
506
|
+
if (orElse)
|
|
507
|
+
return orElse();
|
|
508
|
+
}
|
|
509
|
+
isPresent() {
|
|
510
|
+
return this.children.length > 0;
|
|
511
|
+
}
|
|
512
|
+
isEmpty() {
|
|
513
|
+
return this.children.length === 0;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
// export function dumpCstNode(node: CstNode, depth = 0): void {
|
|
517
|
+
// const {_base, children, ctorName, matchLength, startIdx} = node;
|
|
518
|
+
// const indent = Array.from({length: depth}).join(' ');
|
|
519
|
+
// const addr = _base.toString(16);
|
|
520
|
+
// // eslint-disable-next-line no-console
|
|
521
|
+
// console.log(
|
|
522
|
+
// `${indent}${addr} ${ctorName}@${startIdx}, matchLength ${matchLength}, children ${children.length}` // eslint-disable-line max-len
|
|
523
|
+
// );
|
|
524
|
+
// node.children.forEach(c => dumpCstNode(c, depth + 1));
|
|
525
|
+
// }
|
|
358
526
|
export class MatchResult {
|
|
359
527
|
// Note: This is different from the JS implementation, which has:
|
|
360
528
|
// matcher: Matcher;
|
|
361
529
|
// …instead.
|
|
362
530
|
grammar;
|
|
363
|
-
input;
|
|
364
531
|
startExpr;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
message;
|
|
370
|
-
constructor(matcher, input, startExpr, cst, rightmostFailurePosition, optRecordedFailures) {
|
|
371
|
-
this.grammar = matcher;
|
|
372
|
-
this.input = input;
|
|
532
|
+
_ctx;
|
|
533
|
+
_succeeded;
|
|
534
|
+
constructor(grammar, startExpr, ctx, succeeded) {
|
|
535
|
+
this.grammar = grammar;
|
|
373
536
|
this.startExpr = startExpr;
|
|
374
|
-
this.
|
|
375
|
-
this.
|
|
376
|
-
this._rightmostFailures = optRecordedFailures;
|
|
377
|
-
// TODO: Define these as lazy properties, like in the JS implementation.
|
|
378
|
-
if (this.failed()) {
|
|
379
|
-
this.shortMessage = this.message = `Match failed at pos ${rightmostFailurePosition}`;
|
|
380
|
-
}
|
|
537
|
+
this._ctx = ctx;
|
|
538
|
+
this._succeeded = succeeded;
|
|
381
539
|
}
|
|
382
540
|
[Symbol.dispose]() {
|
|
383
541
|
this.detach();
|
|
@@ -386,41 +544,66 @@ export class MatchResult {
|
|
|
386
544
|
throw new Error('MatchResult is not attached to any grammar');
|
|
387
545
|
}
|
|
388
546
|
succeeded() {
|
|
389
|
-
return
|
|
547
|
+
return this._succeeded;
|
|
390
548
|
}
|
|
391
549
|
failed() {
|
|
392
|
-
return !this.
|
|
550
|
+
return !this._succeeded;
|
|
551
|
+
}
|
|
552
|
+
toString() {
|
|
553
|
+
return this.failed()
|
|
554
|
+
? '[match failed at position ' + this.getRightmostFailurePosition() + ']'
|
|
555
|
+
: '[match succeeded]';
|
|
556
|
+
}
|
|
557
|
+
use(cb) {
|
|
558
|
+
try {
|
|
559
|
+
this.grammar._beginUse(this);
|
|
560
|
+
return cb(this);
|
|
561
|
+
}
|
|
562
|
+
finally {
|
|
563
|
+
this.grammar._endUse(this);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function createMatchResult(grammar, startExpr, ctx, succeeded) {
|
|
568
|
+
return succeeded
|
|
569
|
+
? new SucceededMatchResult(grammar, startExpr, ctx, succeeded)
|
|
570
|
+
: new FailedMatchResult(grammar, startExpr, ctx, succeeded, grammar.getRightmostFailurePosition());
|
|
571
|
+
}
|
|
572
|
+
class SucceededMatchResult extends MatchResult {
|
|
573
|
+
_cst;
|
|
574
|
+
constructor(grammar, startExpr, ctx, succeeded) {
|
|
575
|
+
super(grammar, startExpr, ctx, succeeded);
|
|
576
|
+
this._cst = grammar.getCstRoot();
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
class FailedMatchResult extends MatchResult {
|
|
580
|
+
constructor(grammar, startExpr, ctx, succeeded, rightmostFailurePosition, optRecordedFailures) {
|
|
581
|
+
super(grammar, startExpr, ctx, succeeded);
|
|
582
|
+
this._rightmostFailurePosition = rightmostFailurePosition;
|
|
583
|
+
this._rightmostFailures = optRecordedFailures;
|
|
584
|
+
// TODO: Define these as lazy properties, like in the JS implementation.
|
|
585
|
+
if (this.failed()) {
|
|
586
|
+
this.shortMessage = this.message = `Match failed at pos ${rightmostFailurePosition}`;
|
|
587
|
+
}
|
|
393
588
|
}
|
|
589
|
+
_rightmostFailurePosition;
|
|
590
|
+
_rightmostFailures;
|
|
591
|
+
shortMessage;
|
|
592
|
+
message;
|
|
394
593
|
getRightmostFailurePosition() {
|
|
395
594
|
return this._rightmostFailurePosition;
|
|
396
595
|
}
|
|
397
596
|
getRightmostFailures() {
|
|
398
597
|
throw new Error('Not implemented yet: getRightmostFailures');
|
|
399
598
|
}
|
|
400
|
-
toString() {
|
|
401
|
-
return this.succeeded()
|
|
402
|
-
? '[match succeeded]'
|
|
403
|
-
: '[match failed at position ' + this.getRightmostFailurePosition() + ']';
|
|
404
|
-
}
|
|
405
599
|
// Return a string summarizing the expected contents of the input stream when
|
|
406
600
|
// the match failure occurred.
|
|
407
601
|
getExpectedText() {
|
|
408
|
-
|
|
409
|
-
throw new Error('cannot get expected text of a successful MatchResult');
|
|
410
|
-
}
|
|
602
|
+
assert(!this._succeeded, 'cannot get expected text of a successful MatchResult');
|
|
411
603
|
throw new Error('Not implemented yet: getExpectedText');
|
|
412
604
|
}
|
|
413
605
|
getInterval() {
|
|
414
606
|
throw new Error('Not implemented yet: getInterval');
|
|
415
607
|
}
|
|
416
|
-
use(cb) {
|
|
417
|
-
try {
|
|
418
|
-
this.grammar._beginUse(this);
|
|
419
|
-
return cb(this);
|
|
420
|
-
}
|
|
421
|
-
finally {
|
|
422
|
-
this.grammar._endUse(this);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
608
|
}
|
|
426
609
|
//# sourceMappingURL=miniohm.js.map
|