grammar-well 1.1.2 → 1.1.4
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/build/compiler/compiler.d.ts +49 -49
- package/build/compiler/compiler.js +227 -227
- package/build/compiler/generator.d.ts +23 -23
- package/build/compiler/generator.js +213 -212
- package/build/compiler/generator.js.map +1 -1
- package/build/compiler/import-resolver.d.ts +15 -15
- package/build/compiler/import-resolver.js +36 -36
- package/build/compiler/outputs/javascript.d.ts +3 -3
- package/build/compiler/outputs/javascript.js +14 -14
- package/build/compiler/outputs/json.d.ts +2 -2
- package/build/compiler/outputs/json.js +7 -7
- package/build/compiler/outputs/typescript.d.ts +2 -2
- package/build/compiler/outputs/typescript.js +9 -8
- package/build/compiler/outputs/typescript.js.map +1 -1
- package/build/grammars/gwell.d.ts +1023 -997
- package/build/grammars/gwell.js +540 -536
- package/build/grammars/gwell.js.map +1 -1
- package/build/grammars/json.d.ts +151 -151
- package/build/grammars/json.js +111 -111
- package/build/grammars/number.d.ts +239 -239
- package/build/grammars/number.js +114 -114
- package/build/grammars/number.json +1 -1
- package/build/grammars/string.d.ts +116 -116
- package/build/grammars/string.js +49 -49
- package/build/grammars/string.json +1 -1
- package/build/grammars/whitespace.d.ts +51 -51
- package/build/grammars/whitespace.js +29 -29
- package/build/grammars/whitespace.json +1 -1
- package/build/index.d.ts +4 -4
- package/build/index.js +20 -20
- package/build/lexers/character-lexer.d.ts +27 -27
- package/build/lexers/character-lexer.js +70 -70
- package/build/lexers/stateful-lexer.d.ts +48 -48
- package/build/lexers/stateful-lexer.js +308 -308
- package/build/lexers/token-buffer.d.ts +32 -32
- package/build/lexers/token-buffer.js +91 -91
- package/build/parser/algorithms/cyk.d.ts +16 -16
- package/build/parser/algorithms/cyk.js +57 -57
- package/build/parser/algorithms/earley.d.ts +48 -48
- package/build/parser/algorithms/earley.js +157 -157
- package/build/parser/algorithms/lr.d.ts +10 -10
- package/build/parser/algorithms/lr.js +33 -33
- package/build/parser/parser.d.ts +26 -26
- package/build/parser/parser.js +73 -73
- package/build/typings.d.ts +199 -198
- package/build/typings.js +2 -2
- package/build/utility/general.d.ts +55 -55
- package/build/utility/general.js +175 -165
- package/build/utility/general.js.map +1 -1
- package/build/utility/lint.d.ts +2 -2
- package/build/utility/lint.js +27 -27
- package/build/utility/lr.d.ts +52 -52
- package/build/utility/lr.js +129 -129
- package/build/utility/text-format.d.ts +11 -11
- package/build/utility/text-format.js +83 -83
- package/package.json +1 -1
- package/src/compiler/generator.ts +1 -0
- package/src/compiler/outputs/typescript.ts +2 -1
- package/src/grammars/gwell.gwell +15 -13
- package/src/grammars/gwell.js +17 -13
- package/src/grammars/gwell.json +1 -1
- package/src/typings.ts +1 -0
- package/src/utility/general.ts +31 -19
|
@@ -1,309 +1,309 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ResolveStates = exports.StatefulLexer = void 0;
|
|
4
|
-
class StatefulLexer {
|
|
5
|
-
start;
|
|
6
|
-
states = Object.create(null);
|
|
7
|
-
buffer;
|
|
8
|
-
stack;
|
|
9
|
-
index;
|
|
10
|
-
line;
|
|
11
|
-
column;
|
|
12
|
-
prefetched;
|
|
13
|
-
current;
|
|
14
|
-
unmatched;
|
|
15
|
-
rules;
|
|
16
|
-
regexp;
|
|
17
|
-
tags = new Map();
|
|
18
|
-
constructor({ states, start }) {
|
|
19
|
-
ResolveStates(states, start);
|
|
20
|
-
for (const key in states) {
|
|
21
|
-
this.states[key] = {
|
|
22
|
-
regexp: CompileRegExp(states[key]),
|
|
23
|
-
rules: states[key].rules,
|
|
24
|
-
unmatched: states[key].unmatched ? { type: states[key].unmatched } : null
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
this.start = start;
|
|
28
|
-
this.buffer = '';
|
|
29
|
-
this.stack = [];
|
|
30
|
-
this.feed();
|
|
31
|
-
}
|
|
32
|
-
feed(data, state) {
|
|
33
|
-
this.buffer = data || '';
|
|
34
|
-
this.index = 0;
|
|
35
|
-
this.line = state ? state.line : 1;
|
|
36
|
-
this.column = state ? state.column : 1;
|
|
37
|
-
this.prefetched = state?.prefetched;
|
|
38
|
-
this.set(state ? state.state : this.start);
|
|
39
|
-
this.stack = state && state.stack ? state.stack.slice() : [];
|
|
40
|
-
}
|
|
41
|
-
state() {
|
|
42
|
-
return {
|
|
43
|
-
line: this.line,
|
|
44
|
-
column: this.column,
|
|
45
|
-
state: this.current,
|
|
46
|
-
stack: this.stack.slice(),
|
|
47
|
-
prefetched: this.prefetched,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
next() {
|
|
51
|
-
const next = this.matchNext();
|
|
52
|
-
if (!next) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
const { rule, text, index } = next;
|
|
56
|
-
if (!rule) {
|
|
57
|
-
throw new Error(`No matching rule for ${text}`);
|
|
58
|
-
}
|
|
59
|
-
const token = this.createToken(rule, text, index);
|
|
60
|
-
this.processRule(rule);
|
|
61
|
-
return token;
|
|
62
|
-
}
|
|
63
|
-
set(current) {
|
|
64
|
-
if (!current || this.current === current)
|
|
65
|
-
return;
|
|
66
|
-
const info = this.states[current];
|
|
67
|
-
this.current = current;
|
|
68
|
-
this.rules = info.rules;
|
|
69
|
-
this.unmatched = info.unmatched;
|
|
70
|
-
this.regexp = info.regexp;
|
|
71
|
-
}
|
|
72
|
-
pop() {
|
|
73
|
-
this.set(this.stack.pop());
|
|
74
|
-
}
|
|
75
|
-
goto(state) {
|
|
76
|
-
this.stack.push(this.current);
|
|
77
|
-
this.set(state);
|
|
78
|
-
}
|
|
79
|
-
matchNext() {
|
|
80
|
-
if (this.index === this.buffer.length) {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const { index, buffer } = this;
|
|
84
|
-
let text;
|
|
85
|
-
let rule;
|
|
86
|
-
let match;
|
|
87
|
-
this.regexp.lastIndex = index;
|
|
88
|
-
if (this.prefetched) {
|
|
89
|
-
match = this.prefetched;
|
|
90
|
-
this.prefetched = null;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
match = this.regexp.exec(buffer);
|
|
94
|
-
}
|
|
95
|
-
if (match == null) {
|
|
96
|
-
rule = this.unmatched;
|
|
97
|
-
text = buffer.slice(index, buffer.length);
|
|
98
|
-
}
|
|
99
|
-
else if (match.index !== index) {
|
|
100
|
-
rule = this.unmatched;
|
|
101
|
-
text = buffer.slice(index, match.index);
|
|
102
|
-
this.prefetched = match;
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
rule = this.getGroup(match);
|
|
106
|
-
text = match[0];
|
|
107
|
-
}
|
|
108
|
-
return { index, rule, text };
|
|
109
|
-
}
|
|
110
|
-
createToken(rule, text, offset) {
|
|
111
|
-
const token = {
|
|
112
|
-
type: rule.type,
|
|
113
|
-
tag: this.getTags(rule.tag),
|
|
114
|
-
value: text,
|
|
115
|
-
text: text,
|
|
116
|
-
offset: offset,
|
|
117
|
-
line: this.line,
|
|
118
|
-
column: this.column,
|
|
119
|
-
state: this.current
|
|
120
|
-
};
|
|
121
|
-
for (let i = 0; i < text.length; i++) {
|
|
122
|
-
this.index++;
|
|
123
|
-
this.column++;
|
|
124
|
-
if (text[i] == '\n') {
|
|
125
|
-
this.line++;
|
|
126
|
-
this.column = 1;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return token;
|
|
130
|
-
}
|
|
131
|
-
getTags(tags) {
|
|
132
|
-
if (!tags)
|
|
133
|
-
return undefined;
|
|
134
|
-
if (!this.tags.has(tags))
|
|
135
|
-
this.tags.set(tags, new Set(tags));
|
|
136
|
-
return this.tags.get(tags);
|
|
137
|
-
}
|
|
138
|
-
processRule(rule) {
|
|
139
|
-
if (rule.pop) {
|
|
140
|
-
let i = rule.pop === 'all' ? this.stack.length : rule.pop;
|
|
141
|
-
while (i-- > 0) {
|
|
142
|
-
this.pop();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
if (rule.set) {
|
|
146
|
-
this.set(rule.set);
|
|
147
|
-
}
|
|
148
|
-
if (rule.goto) {
|
|
149
|
-
this.goto(rule.goto);
|
|
150
|
-
}
|
|
151
|
-
if (rule.inset) {
|
|
152
|
-
let i = rule.inset;
|
|
153
|
-
while (--i >= 0) {
|
|
154
|
-
this.goto(this.current);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
getGroup(match) {
|
|
159
|
-
for (let i = 0; i < this.rules.length; i++) {
|
|
160
|
-
if (match[i + 1] !== undefined) {
|
|
161
|
-
return this.rules[i];
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
throw new Error('Cannot find token type for matched text');
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
exports.StatefulLexer = StatefulLexer;
|
|
168
|
-
class RegexLib {
|
|
169
|
-
static IsRegex(o) {
|
|
170
|
-
return o instanceof RegExp;
|
|
171
|
-
}
|
|
172
|
-
static Escape(s) {
|
|
173
|
-
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
174
|
-
}
|
|
175
|
-
static HasGroups(s) {
|
|
176
|
-
return (new RegExp('|' + s)).exec('').length > 1;
|
|
177
|
-
}
|
|
178
|
-
static Capture(source) {
|
|
179
|
-
return '(' + source + ')';
|
|
180
|
-
}
|
|
181
|
-
static Join(regexps) {
|
|
182
|
-
if (!regexps.length)
|
|
183
|
-
return '(?!)';
|
|
184
|
-
const source = regexps.map((s) => `(?:${s})`).join('|');
|
|
185
|
-
return `(?:${source})`;
|
|
186
|
-
}
|
|
187
|
-
static Source(search) {
|
|
188
|
-
if (typeof search === 'string') {
|
|
189
|
-
return `(?:${RegexLib.Escape(search)})`;
|
|
190
|
-
}
|
|
191
|
-
if (RegexLib.IsRegex(search)) {
|
|
192
|
-
return search.source;
|
|
193
|
-
}
|
|
194
|
-
throw new Error('Not a pattern: ' + search);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function CompileRegExp(state) {
|
|
198
|
-
const rules = [];
|
|
199
|
-
const subexpressions = [];
|
|
200
|
-
let isUnicode = null;
|
|
201
|
-
let isCI = null;
|
|
202
|
-
for (const options of state.rules) {
|
|
203
|
-
if (RegexLib.IsRegex(options.when)) {
|
|
204
|
-
const when = options.when;
|
|
205
|
-
if (isUnicode === null) {
|
|
206
|
-
isUnicode = when.unicode;
|
|
207
|
-
}
|
|
208
|
-
else if (isUnicode !== when.unicode && !state.unmatched) {
|
|
209
|
-
throw new Error(`Inconsistent Regex Flag /u in state: ${state.name}`);
|
|
210
|
-
}
|
|
211
|
-
if (isCI === null) {
|
|
212
|
-
isCI = when.ignoreCase;
|
|
213
|
-
}
|
|
214
|
-
else if (isCI !== when.ignoreCase) {
|
|
215
|
-
throw new Error(`Inconsistent Regex Flag /i in state: ${state.name}`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
if (isCI == null) {
|
|
220
|
-
isCI = false;
|
|
221
|
-
}
|
|
222
|
-
else if (isCI != false) {
|
|
223
|
-
throw new Error(`Inconsistent Regex Flag /i in state: ${state.name}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
rules.push(options);
|
|
227
|
-
const pat = RegexLib.Source(options.when);
|
|
228
|
-
const regexp = new RegExp(pat);
|
|
229
|
-
if (regexp.test("")) {
|
|
230
|
-
throw new Error("RegExp matches empty string: " + regexp);
|
|
231
|
-
}
|
|
232
|
-
if (RegexLib.HasGroups(pat)) {
|
|
233
|
-
throw new Error("RegExp has capture groups: " + regexp + "\nUse (?: … ) instead");
|
|
234
|
-
}
|
|
235
|
-
subexpressions.push(RegexLib.Capture(pat));
|
|
236
|
-
}
|
|
237
|
-
let flags = !state.unmatched ? 'ym' : 'gm';
|
|
238
|
-
if (isUnicode === true)
|
|
239
|
-
flags += "u";
|
|
240
|
-
if (isCI === true)
|
|
241
|
-
flags += "i";
|
|
242
|
-
return new RegExp(RegexLib.Join(subexpressions), flags);
|
|
243
|
-
}
|
|
244
|
-
function ResolveStates(states, start) {
|
|
245
|
-
const resolved = new Set();
|
|
246
|
-
const resolving = new Set();
|
|
247
|
-
const chain = new Set();
|
|
248
|
-
ResolveRuleImports(start, states, resolved, resolving, chain);
|
|
249
|
-
for (const key in states) {
|
|
250
|
-
if (!resolved.has(key)) {
|
|
251
|
-
delete states[key];
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return states;
|
|
255
|
-
}
|
|
256
|
-
exports.ResolveStates = ResolveStates;
|
|
257
|
-
function ResolveRuleImports(name, states, resolved, resolving, chain) {
|
|
258
|
-
if (chain.has(name))
|
|
259
|
-
throw new Error(`Can not resolve circular import of ${name}`);
|
|
260
|
-
if (!states[name])
|
|
261
|
-
throw new Error(`Can not import unknown state ${name}`);
|
|
262
|
-
if (resolved.has(name) || resolving.has(name))
|
|
263
|
-
return;
|
|
264
|
-
const state = states[name];
|
|
265
|
-
const rules = new UniqueRules();
|
|
266
|
-
chain.add(name);
|
|
267
|
-
resolving.add(name);
|
|
268
|
-
for (let i = 0; i < state.rules.length; i++) {
|
|
269
|
-
const rule = state.rules[i];
|
|
270
|
-
if ("import" in rule) {
|
|
271
|
-
for (const ref of rule.import) {
|
|
272
|
-
ResolveRuleImports(ref, states, resolved, resolving, chain);
|
|
273
|
-
rules.push(...states[ref].rules);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
else {
|
|
277
|
-
rules.push(rule);
|
|
278
|
-
if ("set" in rule && !resolving.has(rule.set)) {
|
|
279
|
-
ResolveRuleImports(rule.set, states, resolved, resolving, new Set());
|
|
280
|
-
}
|
|
281
|
-
if ("goto" in rule && !resolving.has(rule.goto)) {
|
|
282
|
-
ResolveRuleImports(rule.goto, states, resolved, resolving, new Set());
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
state.rules = rules.rules;
|
|
287
|
-
chain.delete(name);
|
|
288
|
-
resolved.add(name);
|
|
289
|
-
}
|
|
290
|
-
class UniqueRules {
|
|
291
|
-
regexps = new Set();
|
|
292
|
-
strings = new Set();
|
|
293
|
-
rules = [];
|
|
294
|
-
push(...rules) {
|
|
295
|
-
for (const rule of rules) {
|
|
296
|
-
if (RegexLib.IsRegex(rule.when)) {
|
|
297
|
-
if (!this.regexps.has(rule.when.source)) {
|
|
298
|
-
this.rules.push(rule);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
if (!this.strings.has(rule.when)) {
|
|
303
|
-
this.rules.push(rule);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResolveStates = exports.StatefulLexer = void 0;
|
|
4
|
+
class StatefulLexer {
|
|
5
|
+
start;
|
|
6
|
+
states = Object.create(null);
|
|
7
|
+
buffer;
|
|
8
|
+
stack;
|
|
9
|
+
index;
|
|
10
|
+
line;
|
|
11
|
+
column;
|
|
12
|
+
prefetched;
|
|
13
|
+
current;
|
|
14
|
+
unmatched;
|
|
15
|
+
rules;
|
|
16
|
+
regexp;
|
|
17
|
+
tags = new Map();
|
|
18
|
+
constructor({ states, start }) {
|
|
19
|
+
ResolveStates(states, start);
|
|
20
|
+
for (const key in states) {
|
|
21
|
+
this.states[key] = {
|
|
22
|
+
regexp: CompileRegExp(states[key]),
|
|
23
|
+
rules: states[key].rules,
|
|
24
|
+
unmatched: states[key].unmatched ? { type: states[key].unmatched } : null
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
this.start = start;
|
|
28
|
+
this.buffer = '';
|
|
29
|
+
this.stack = [];
|
|
30
|
+
this.feed();
|
|
31
|
+
}
|
|
32
|
+
feed(data, state) {
|
|
33
|
+
this.buffer = data || '';
|
|
34
|
+
this.index = 0;
|
|
35
|
+
this.line = state ? state.line : 1;
|
|
36
|
+
this.column = state ? state.column : 1;
|
|
37
|
+
this.prefetched = state?.prefetched;
|
|
38
|
+
this.set(state ? state.state : this.start);
|
|
39
|
+
this.stack = state && state.stack ? state.stack.slice() : [];
|
|
40
|
+
}
|
|
41
|
+
state() {
|
|
42
|
+
return {
|
|
43
|
+
line: this.line,
|
|
44
|
+
column: this.column,
|
|
45
|
+
state: this.current,
|
|
46
|
+
stack: this.stack.slice(),
|
|
47
|
+
prefetched: this.prefetched,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
next() {
|
|
51
|
+
const next = this.matchNext();
|
|
52
|
+
if (!next) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const { rule, text, index } = next;
|
|
56
|
+
if (!rule) {
|
|
57
|
+
throw new Error(`No matching rule for ${text}`);
|
|
58
|
+
}
|
|
59
|
+
const token = this.createToken(rule, text, index);
|
|
60
|
+
this.processRule(rule);
|
|
61
|
+
return token;
|
|
62
|
+
}
|
|
63
|
+
set(current) {
|
|
64
|
+
if (!current || this.current === current)
|
|
65
|
+
return;
|
|
66
|
+
const info = this.states[current];
|
|
67
|
+
this.current = current;
|
|
68
|
+
this.rules = info.rules;
|
|
69
|
+
this.unmatched = info.unmatched;
|
|
70
|
+
this.regexp = info.regexp;
|
|
71
|
+
}
|
|
72
|
+
pop() {
|
|
73
|
+
this.set(this.stack.pop());
|
|
74
|
+
}
|
|
75
|
+
goto(state) {
|
|
76
|
+
this.stack.push(this.current);
|
|
77
|
+
this.set(state);
|
|
78
|
+
}
|
|
79
|
+
matchNext() {
|
|
80
|
+
if (this.index === this.buffer.length) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const { index, buffer } = this;
|
|
84
|
+
let text;
|
|
85
|
+
let rule;
|
|
86
|
+
let match;
|
|
87
|
+
this.regexp.lastIndex = index;
|
|
88
|
+
if (this.prefetched) {
|
|
89
|
+
match = this.prefetched;
|
|
90
|
+
this.prefetched = null;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
match = this.regexp.exec(buffer);
|
|
94
|
+
}
|
|
95
|
+
if (match == null) {
|
|
96
|
+
rule = this.unmatched;
|
|
97
|
+
text = buffer.slice(index, buffer.length);
|
|
98
|
+
}
|
|
99
|
+
else if (match.index !== index) {
|
|
100
|
+
rule = this.unmatched;
|
|
101
|
+
text = buffer.slice(index, match.index);
|
|
102
|
+
this.prefetched = match;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
rule = this.getGroup(match);
|
|
106
|
+
text = match[0];
|
|
107
|
+
}
|
|
108
|
+
return { index, rule, text };
|
|
109
|
+
}
|
|
110
|
+
createToken(rule, text, offset) {
|
|
111
|
+
const token = {
|
|
112
|
+
type: rule.type,
|
|
113
|
+
tag: this.getTags(rule.tag),
|
|
114
|
+
value: text,
|
|
115
|
+
text: text,
|
|
116
|
+
offset: offset,
|
|
117
|
+
line: this.line,
|
|
118
|
+
column: this.column,
|
|
119
|
+
state: this.current
|
|
120
|
+
};
|
|
121
|
+
for (let i = 0; i < text.length; i++) {
|
|
122
|
+
this.index++;
|
|
123
|
+
this.column++;
|
|
124
|
+
if (text[i] == '\n') {
|
|
125
|
+
this.line++;
|
|
126
|
+
this.column = 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return token;
|
|
130
|
+
}
|
|
131
|
+
getTags(tags) {
|
|
132
|
+
if (!tags)
|
|
133
|
+
return undefined;
|
|
134
|
+
if (!this.tags.has(tags))
|
|
135
|
+
this.tags.set(tags, new Set(tags));
|
|
136
|
+
return this.tags.get(tags);
|
|
137
|
+
}
|
|
138
|
+
processRule(rule) {
|
|
139
|
+
if (rule.pop) {
|
|
140
|
+
let i = rule.pop === 'all' ? this.stack.length : rule.pop;
|
|
141
|
+
while (i-- > 0) {
|
|
142
|
+
this.pop();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (rule.set) {
|
|
146
|
+
this.set(rule.set);
|
|
147
|
+
}
|
|
148
|
+
if (rule.goto) {
|
|
149
|
+
this.goto(rule.goto);
|
|
150
|
+
}
|
|
151
|
+
if (rule.inset) {
|
|
152
|
+
let i = rule.inset;
|
|
153
|
+
while (--i >= 0) {
|
|
154
|
+
this.goto(this.current);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
getGroup(match) {
|
|
159
|
+
for (let i = 0; i < this.rules.length; i++) {
|
|
160
|
+
if (match[i + 1] !== undefined) {
|
|
161
|
+
return this.rules[i];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
throw new Error('Cannot find token type for matched text');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.StatefulLexer = StatefulLexer;
|
|
168
|
+
class RegexLib {
|
|
169
|
+
static IsRegex(o) {
|
|
170
|
+
return o instanceof RegExp;
|
|
171
|
+
}
|
|
172
|
+
static Escape(s) {
|
|
173
|
+
return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
174
|
+
}
|
|
175
|
+
static HasGroups(s) {
|
|
176
|
+
return (new RegExp('|' + s)).exec('').length > 1;
|
|
177
|
+
}
|
|
178
|
+
static Capture(source) {
|
|
179
|
+
return '(' + source + ')';
|
|
180
|
+
}
|
|
181
|
+
static Join(regexps) {
|
|
182
|
+
if (!regexps.length)
|
|
183
|
+
return '(?!)';
|
|
184
|
+
const source = regexps.map((s) => `(?:${s})`).join('|');
|
|
185
|
+
return `(?:${source})`;
|
|
186
|
+
}
|
|
187
|
+
static Source(search) {
|
|
188
|
+
if (typeof search === 'string') {
|
|
189
|
+
return `(?:${RegexLib.Escape(search)})`;
|
|
190
|
+
}
|
|
191
|
+
if (RegexLib.IsRegex(search)) {
|
|
192
|
+
return search.source;
|
|
193
|
+
}
|
|
194
|
+
throw new Error('Not a pattern: ' + search);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function CompileRegExp(state) {
|
|
198
|
+
const rules = [];
|
|
199
|
+
const subexpressions = [];
|
|
200
|
+
let isUnicode = null;
|
|
201
|
+
let isCI = null;
|
|
202
|
+
for (const options of state.rules) {
|
|
203
|
+
if (RegexLib.IsRegex(options.when)) {
|
|
204
|
+
const when = options.when;
|
|
205
|
+
if (isUnicode === null) {
|
|
206
|
+
isUnicode = when.unicode;
|
|
207
|
+
}
|
|
208
|
+
else if (isUnicode !== when.unicode && !state.unmatched) {
|
|
209
|
+
throw new Error(`Inconsistent Regex Flag /u in state: ${state.name}`);
|
|
210
|
+
}
|
|
211
|
+
if (isCI === null) {
|
|
212
|
+
isCI = when.ignoreCase;
|
|
213
|
+
}
|
|
214
|
+
else if (isCI !== when.ignoreCase) {
|
|
215
|
+
throw new Error(`Inconsistent Regex Flag /i in state: ${state.name}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
if (isCI == null) {
|
|
220
|
+
isCI = false;
|
|
221
|
+
}
|
|
222
|
+
else if (isCI != false) {
|
|
223
|
+
throw new Error(`Inconsistent Regex Flag /i in state: ${state.name}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
rules.push(options);
|
|
227
|
+
const pat = RegexLib.Source(options.when);
|
|
228
|
+
const regexp = new RegExp(pat);
|
|
229
|
+
if (regexp.test("")) {
|
|
230
|
+
throw new Error("RegExp matches empty string: " + regexp);
|
|
231
|
+
}
|
|
232
|
+
if (RegexLib.HasGroups(pat)) {
|
|
233
|
+
throw new Error("RegExp has capture groups: " + regexp + "\nUse (?: … ) instead");
|
|
234
|
+
}
|
|
235
|
+
subexpressions.push(RegexLib.Capture(pat));
|
|
236
|
+
}
|
|
237
|
+
let flags = !state.unmatched ? 'ym' : 'gm';
|
|
238
|
+
if (isUnicode === true)
|
|
239
|
+
flags += "u";
|
|
240
|
+
if (isCI === true)
|
|
241
|
+
flags += "i";
|
|
242
|
+
return new RegExp(RegexLib.Join(subexpressions), flags);
|
|
243
|
+
}
|
|
244
|
+
function ResolveStates(states, start) {
|
|
245
|
+
const resolved = new Set();
|
|
246
|
+
const resolving = new Set();
|
|
247
|
+
const chain = new Set();
|
|
248
|
+
ResolveRuleImports(start, states, resolved, resolving, chain);
|
|
249
|
+
for (const key in states) {
|
|
250
|
+
if (!resolved.has(key)) {
|
|
251
|
+
delete states[key];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return states;
|
|
255
|
+
}
|
|
256
|
+
exports.ResolveStates = ResolveStates;
|
|
257
|
+
function ResolveRuleImports(name, states, resolved, resolving, chain) {
|
|
258
|
+
if (chain.has(name))
|
|
259
|
+
throw new Error(`Can not resolve circular import of ${name}`);
|
|
260
|
+
if (!states[name])
|
|
261
|
+
throw new Error(`Can not import unknown state ${name}`);
|
|
262
|
+
if (resolved.has(name) || resolving.has(name))
|
|
263
|
+
return;
|
|
264
|
+
const state = states[name];
|
|
265
|
+
const rules = new UniqueRules();
|
|
266
|
+
chain.add(name);
|
|
267
|
+
resolving.add(name);
|
|
268
|
+
for (let i = 0; i < state.rules.length; i++) {
|
|
269
|
+
const rule = state.rules[i];
|
|
270
|
+
if ("import" in rule) {
|
|
271
|
+
for (const ref of rule.import) {
|
|
272
|
+
ResolveRuleImports(ref, states, resolved, resolving, chain);
|
|
273
|
+
rules.push(...states[ref].rules);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
rules.push(rule);
|
|
278
|
+
if ("set" in rule && !resolving.has(rule.set)) {
|
|
279
|
+
ResolveRuleImports(rule.set, states, resolved, resolving, new Set());
|
|
280
|
+
}
|
|
281
|
+
if ("goto" in rule && !resolving.has(rule.goto)) {
|
|
282
|
+
ResolveRuleImports(rule.goto, states, resolved, resolving, new Set());
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
state.rules = rules.rules;
|
|
287
|
+
chain.delete(name);
|
|
288
|
+
resolved.add(name);
|
|
289
|
+
}
|
|
290
|
+
class UniqueRules {
|
|
291
|
+
regexps = new Set();
|
|
292
|
+
strings = new Set();
|
|
293
|
+
rules = [];
|
|
294
|
+
push(...rules) {
|
|
295
|
+
for (const rule of rules) {
|
|
296
|
+
if (RegexLib.IsRegex(rule.when)) {
|
|
297
|
+
if (!this.regexps.has(rule.when.source)) {
|
|
298
|
+
this.rules.push(rule);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
if (!this.strings.has(rule.when)) {
|
|
303
|
+
this.rules.push(rule);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
309
|
//# sourceMappingURL=stateful-lexer.js.map
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { Lexer, TQRestorePoint, LexerToken } from '../typings';
|
|
2
|
-
export declare class TokenBuffer {
|
|
3
|
-
private lexer;
|
|
4
|
-
private history;
|
|
5
|
-
private queued;
|
|
6
|
-
private $historyIndex;
|
|
7
|
-
get offset(): number;
|
|
8
|
-
get line(): number;
|
|
9
|
-
get column(): number;
|
|
10
|
-
get active(): LexerToken;
|
|
11
|
-
get state(): TQRestorePoint;
|
|
12
|
-
constructor(lexer: Lexer);
|
|
13
|
-
reset(buffer: string): void;
|
|
14
|
-
restore(state: TQRestorePoint): void;
|
|
15
|
-
feed(buffer: string, flush?: boolean): void;
|
|
16
|
-
flush(): void;
|
|
17
|
-
previous(): LexerToken;
|
|
18
|
-
next(): LexerToken;
|
|
19
|
-
peek(offset: number): LexerToken;
|
|
20
|
-
private lexerNext;
|
|
21
|
-
[Symbol.iterator](): TokenIterator;
|
|
22
|
-
}
|
|
23
|
-
declare class TokenIterator {
|
|
24
|
-
private buffer;
|
|
25
|
-
constructor(buffer: TokenBuffer);
|
|
26
|
-
next(): {
|
|
27
|
-
value: LexerToken;
|
|
28
|
-
done: boolean;
|
|
29
|
-
};
|
|
30
|
-
[Symbol.iterator](): this;
|
|
31
|
-
}
|
|
32
|
-
export {};
|
|
1
|
+
import { Lexer, TQRestorePoint, LexerToken } from '../typings';
|
|
2
|
+
export declare class TokenBuffer {
|
|
3
|
+
private lexer;
|
|
4
|
+
private history;
|
|
5
|
+
private queued;
|
|
6
|
+
private $historyIndex;
|
|
7
|
+
get offset(): number;
|
|
8
|
+
get line(): number;
|
|
9
|
+
get column(): number;
|
|
10
|
+
get active(): LexerToken;
|
|
11
|
+
get state(): TQRestorePoint;
|
|
12
|
+
constructor(lexer: Lexer);
|
|
13
|
+
reset(buffer: string): void;
|
|
14
|
+
restore(state: TQRestorePoint): void;
|
|
15
|
+
feed(buffer: string, flush?: boolean): void;
|
|
16
|
+
flush(): void;
|
|
17
|
+
previous(): LexerToken;
|
|
18
|
+
next(): LexerToken;
|
|
19
|
+
peek(offset: number): LexerToken;
|
|
20
|
+
private lexerNext;
|
|
21
|
+
[Symbol.iterator](): TokenIterator;
|
|
22
|
+
}
|
|
23
|
+
declare class TokenIterator {
|
|
24
|
+
private buffer;
|
|
25
|
+
constructor(buffer: TokenBuffer);
|
|
26
|
+
next(): {
|
|
27
|
+
value: LexerToken;
|
|
28
|
+
done: boolean;
|
|
29
|
+
};
|
|
30
|
+
[Symbol.iterator](): this;
|
|
31
|
+
}
|
|
32
|
+
export {};
|