grammatik 0.1.0
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/LICENSE +21 -0
- package/README.md +443 -0
- package/bin/grammatik.js +115 -0
- package/dist/cjs/index.js +3010 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/runtime.js +639 -0
- package/dist/esm/index.js +2959 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/runtime.js +605 -0
- package/dist/types/index.d.ts +214 -0
- package/dist/types/runtime.d.ts +40 -0
- package/package.json +65 -0
- package/src/analyze.d.ts +78 -0
- package/src/analyze.js +602 -0
- package/src/codegen.d.ts +36 -0
- package/src/codegen.js +635 -0
- package/src/definition.d.ts +133 -0
- package/src/definition.js +291 -0
- package/src/emit.d.ts +23 -0
- package/src/emit.js +374 -0
- package/src/firstchars.d.ts +6 -0
- package/src/firstchars.js +353 -0
- package/src/index.d.ts +7 -0
- package/src/index.js +15 -0
- package/src/lexer.d.ts +96 -0
- package/src/lexer.js +358 -0
- package/src/methods.d.ts +20 -0
- package/src/methods.js +61 -0
- package/src/methodsblock.js +197 -0
- package/src/parser.d.ts +130 -0
- package/src/parser.js +194 -0
- package/src/plan.d.ts +29 -0
- package/src/plan.js +938 -0
- package/src/runtime.js +16 -0
- package/src/state.d.ts +98 -0
- package/src/state.js +121 -0
- package/src/values.js +67 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
// src/state.js
|
|
2
|
+
var FAIL = /* @__PURE__ */ Symbol("grammatik.fail");
|
|
3
|
+
var createState = (tokens, grammar, options = {}) => ({
|
|
4
|
+
tokens,
|
|
5
|
+
idx: 0,
|
|
6
|
+
maxIdx: 0,
|
|
7
|
+
expected: [],
|
|
8
|
+
grammar,
|
|
9
|
+
memo: options.memo ? /* @__PURE__ */ new Map() : null,
|
|
10
|
+
// Entering a rule is one array store rather than an object allocation, and
|
|
11
|
+
// the stack is snapshotted only on a furthest failure — by the time an error
|
|
12
|
+
// is built it would otherwise have unwound to just the start rule.
|
|
13
|
+
ruleNames: [],
|
|
14
|
+
ruleDepth: 0,
|
|
15
|
+
failureStack: null,
|
|
16
|
+
track: options.track === true,
|
|
17
|
+
steps: 0,
|
|
18
|
+
maxSteps: options.maxSteps || 0
|
|
19
|
+
});
|
|
20
|
+
function expect(state, expectation) {
|
|
21
|
+
if (state.idx > state.maxIdx) {
|
|
22
|
+
state.maxIdx = state.idx;
|
|
23
|
+
if (!state.track) return;
|
|
24
|
+
state.expected = [];
|
|
25
|
+
state.failureStack = state.ruleNames.slice(0, state.ruleDepth);
|
|
26
|
+
}
|
|
27
|
+
if (!state.track || state.idx !== state.maxIdx) return;
|
|
28
|
+
if (!state.expected.includes(expectation)) state.expected.push(expectation);
|
|
29
|
+
}
|
|
30
|
+
var ParseError = class extends Error {
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} message
|
|
33
|
+
* @param {{ token: import('./lexer.js').Token | null, expected: string[], ruleStack: string[] }} details
|
|
34
|
+
*/
|
|
35
|
+
constructor(message, details) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = "ParseError";
|
|
38
|
+
this.token = details.token;
|
|
39
|
+
this.expected = details.expected;
|
|
40
|
+
this.ruleStack = details.ruleStack;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var describeExpected = (expected) => expected.length === 0 ? "nothing" : expected.length === 1 ? expected[0] : `one of [${expected.join(", ")}]`;
|
|
44
|
+
function buildParseError(state) {
|
|
45
|
+
const token = state.tokens[state.maxIdx] || null;
|
|
46
|
+
const expected = [...state.expected].sort();
|
|
47
|
+
const ruleStack = state.failureStack || state.ruleNames.slice(0, state.ruleDepth);
|
|
48
|
+
const at = (t) => state.positions === "offset" ? `offset ${t.start}` : `line ${t.line} column ${t.col}`;
|
|
49
|
+
const found = token ? `--> ${token.image} <-- (${token.type}) at ${at(token)}` : "end of input";
|
|
50
|
+
return new ParseError(
|
|
51
|
+
`Expecting ${describeExpected(expected)} but found ${found}`,
|
|
52
|
+
{ token, expected, ruleStack }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/firstchars.js
|
|
57
|
+
var DIGITS = [];
|
|
58
|
+
for (let c = 48; c <= 57; c++) DIGITS.push(c);
|
|
59
|
+
var WORD = DIGITS.concat([95]);
|
|
60
|
+
for (let c = 65; c <= 90; c++) WORD.push(c);
|
|
61
|
+
for (let c = 97; c <= 122; c++) WORD.push(c);
|
|
62
|
+
var SPACE = [9, 10, 11, 12, 13, 32, 160, 5760, 8232, 8233, 8239, 8287, 12288, 65279];
|
|
63
|
+
for (let c = 8192; c <= 8202; c++) SPACE.push(c);
|
|
64
|
+
var SIMPLE_ESCAPES = {
|
|
65
|
+
n: 10,
|
|
66
|
+
r: 13,
|
|
67
|
+
t: 9,
|
|
68
|
+
f: 12,
|
|
69
|
+
v: 11,
|
|
70
|
+
0: 0
|
|
71
|
+
};
|
|
72
|
+
function firstCharSet(source, ignoreCase) {
|
|
73
|
+
const found = /* @__PURE__ */ new Set();
|
|
74
|
+
let pos = 0;
|
|
75
|
+
let bailed = false;
|
|
76
|
+
const bail = () => {
|
|
77
|
+
bailed = true;
|
|
78
|
+
};
|
|
79
|
+
function add(code) {
|
|
80
|
+
found.add(code);
|
|
81
|
+
if (!ignoreCase) return;
|
|
82
|
+
const char = String.fromCharCode(code);
|
|
83
|
+
const upper = char.toUpperCase();
|
|
84
|
+
const lower = char.toLowerCase();
|
|
85
|
+
if (upper.length === 1) found.add(upper.charCodeAt(0));
|
|
86
|
+
if (lower.length === 1) found.add(lower.charCodeAt(0));
|
|
87
|
+
}
|
|
88
|
+
function addAll(codes) {
|
|
89
|
+
for (const code of codes) add(code);
|
|
90
|
+
}
|
|
91
|
+
function alternation(collect) {
|
|
92
|
+
let nullable = false;
|
|
93
|
+
for (; ; ) {
|
|
94
|
+
if (sequence(collect)) nullable = true;
|
|
95
|
+
if (bailed) return true;
|
|
96
|
+
if (source[pos] === "|") {
|
|
97
|
+
pos++;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
return nullable;
|
|
103
|
+
}
|
|
104
|
+
function sequence(collect) {
|
|
105
|
+
let stillNullable = true;
|
|
106
|
+
while (pos < source.length && source[pos] !== "|" && source[pos] !== ")") {
|
|
107
|
+
const nullable = element(collect && stillNullable);
|
|
108
|
+
if (bailed) return true;
|
|
109
|
+
if (!nullable) stillNullable = false;
|
|
110
|
+
}
|
|
111
|
+
return stillNullable;
|
|
112
|
+
}
|
|
113
|
+
function element(collect) {
|
|
114
|
+
const zeroWidth = atom(collect);
|
|
115
|
+
if (bailed) return true;
|
|
116
|
+
const quantifier = source[pos];
|
|
117
|
+
if (quantifier === "?" || quantifier === "*") {
|
|
118
|
+
pos++;
|
|
119
|
+
if (source[pos] === "?" || source[pos] === "+") pos++;
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
if (quantifier === "+") {
|
|
123
|
+
pos++;
|
|
124
|
+
if (source[pos] === "?" || source[pos] === "+") pos++;
|
|
125
|
+
return zeroWidth;
|
|
126
|
+
}
|
|
127
|
+
if (quantifier === "{") {
|
|
128
|
+
const close = source.indexOf("}", pos);
|
|
129
|
+
if (close === -1) {
|
|
130
|
+
bail();
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
const body = source.slice(pos + 1, close);
|
|
134
|
+
if (!/^\d+(,\d*)?$/.test(body)) {
|
|
135
|
+
bail();
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
pos = close + 1;
|
|
139
|
+
if (source[pos] === "?" || source[pos] === "+") pos++;
|
|
140
|
+
const min = parseInt(body, 10);
|
|
141
|
+
return min === 0 ? true : zeroWidth;
|
|
142
|
+
}
|
|
143
|
+
return zeroWidth;
|
|
144
|
+
}
|
|
145
|
+
function atom(collect) {
|
|
146
|
+
const char = source[pos];
|
|
147
|
+
if (char === "(") {
|
|
148
|
+
let lookaround = false;
|
|
149
|
+
if (source.startsWith("(?:", pos)) pos += 3;
|
|
150
|
+
else if (source.startsWith("(?=", pos) || source.startsWith("(?!", pos)) {
|
|
151
|
+
pos += 3;
|
|
152
|
+
lookaround = true;
|
|
153
|
+
} else if (source.startsWith("(?<=", pos) || source.startsWith("(?<!", pos)) {
|
|
154
|
+
pos += 4;
|
|
155
|
+
lookaround = true;
|
|
156
|
+
} else if (source.startsWith("(?<", pos)) {
|
|
157
|
+
const close = source.indexOf(">", pos);
|
|
158
|
+
if (close === -1) {
|
|
159
|
+
if (collect) bail();
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
pos = close + 1;
|
|
163
|
+
} else pos += 1;
|
|
164
|
+
const nullable = alternation(collect && !lookaround);
|
|
165
|
+
if (bailed) return true;
|
|
166
|
+
if (source[pos] !== ")") {
|
|
167
|
+
if (collect) bail();
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
pos++;
|
|
171
|
+
return lookaround ? true : nullable;
|
|
172
|
+
}
|
|
173
|
+
if (char === "[") return charClass(collect);
|
|
174
|
+
if (char === "\\") return escape(collect);
|
|
175
|
+
if (char === ".") {
|
|
176
|
+
if (collect) {
|
|
177
|
+
bail();
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
pos++;
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (char === "^" || char === "$") {
|
|
184
|
+
pos++;
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (char === void 0) {
|
|
188
|
+
bail();
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
if (collect) add(source.charCodeAt(pos));
|
|
192
|
+
pos++;
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
function skipCharClass() {
|
|
196
|
+
pos++;
|
|
197
|
+
if (source[pos] === "^") pos++;
|
|
198
|
+
if (source[pos] === "]") pos++;
|
|
199
|
+
while (pos < source.length && source[pos] !== "]") {
|
|
200
|
+
if (source[pos] === "\\") pos += 2;
|
|
201
|
+
else pos++;
|
|
202
|
+
}
|
|
203
|
+
if (source[pos] !== "]") {
|
|
204
|
+
bail();
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
pos++;
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function charClass(collect) {
|
|
211
|
+
if (source[pos + 1] === "^") {
|
|
212
|
+
if (collect) {
|
|
213
|
+
bail();
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
return skipCharClass();
|
|
217
|
+
}
|
|
218
|
+
if (!collect) return skipCharClass();
|
|
219
|
+
pos++;
|
|
220
|
+
while (pos < source.length && source[pos] !== "]") {
|
|
221
|
+
let low;
|
|
222
|
+
if (source[pos] === "\\") {
|
|
223
|
+
const before = pos;
|
|
224
|
+
const zero = escape(collect, true);
|
|
225
|
+
if (bailed) return false;
|
|
226
|
+
if (zero === null) {
|
|
227
|
+
low = null;
|
|
228
|
+
} else {
|
|
229
|
+
low = zero;
|
|
230
|
+
}
|
|
231
|
+
if (pos === before) {
|
|
232
|
+
bail();
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
low = source.charCodeAt(pos);
|
|
237
|
+
pos++;
|
|
238
|
+
}
|
|
239
|
+
if (low !== null && source[pos] === "-" && source[pos + 1] !== "]" && pos + 1 < source.length) {
|
|
240
|
+
pos++;
|
|
241
|
+
let high;
|
|
242
|
+
if (source[pos] === "\\") {
|
|
243
|
+
high = escape(collect, true);
|
|
244
|
+
if (bailed) return false;
|
|
245
|
+
if (high === null) {
|
|
246
|
+
bail();
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
high = source.charCodeAt(pos);
|
|
251
|
+
pos++;
|
|
252
|
+
}
|
|
253
|
+
if (high < low) {
|
|
254
|
+
bail();
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
if (high - low > 4096) {
|
|
258
|
+
bail();
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
if (collect) for (let c = low; c <= high; c++) add(c);
|
|
262
|
+
} else if (low !== null && collect) {
|
|
263
|
+
add(low);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (source[pos] !== "]") {
|
|
267
|
+
bail();
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
pos++;
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
function escape(collect, inClass = false) {
|
|
274
|
+
pos++;
|
|
275
|
+
const char = source[pos];
|
|
276
|
+
if (char === void 0) {
|
|
277
|
+
bail();
|
|
278
|
+
return inClass ? null : true;
|
|
279
|
+
}
|
|
280
|
+
if (char === "d" || char === "w" || char === "s") {
|
|
281
|
+
pos++;
|
|
282
|
+
if (collect) addAll(char === "d" ? DIGITS : char === "w" ? WORD : SPACE);
|
|
283
|
+
return inClass ? null : false;
|
|
284
|
+
}
|
|
285
|
+
if (char === "D" || char === "W" || char === "S" || char === "p" || char === "P") {
|
|
286
|
+
if (collect) {
|
|
287
|
+
bail();
|
|
288
|
+
return inClass ? null : false;
|
|
289
|
+
}
|
|
290
|
+
pos++;
|
|
291
|
+
if ((char === "p" || char === "P") && source[pos] === "{") {
|
|
292
|
+
const close = source.indexOf("}", pos);
|
|
293
|
+
if (close === -1) {
|
|
294
|
+
bail();
|
|
295
|
+
return inClass ? null : false;
|
|
296
|
+
}
|
|
297
|
+
pos = close + 1;
|
|
298
|
+
}
|
|
299
|
+
return inClass ? null : false;
|
|
300
|
+
}
|
|
301
|
+
if (char === "b" || char === "B") {
|
|
302
|
+
pos++;
|
|
303
|
+
if (inClass) {
|
|
304
|
+
if (collect) add(8);
|
|
305
|
+
return 8;
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
if (char === "u") {
|
|
310
|
+
if (source[pos + 1] === "{") {
|
|
311
|
+
const close = source.indexOf("}", pos);
|
|
312
|
+
if (close === -1) {
|
|
313
|
+
bail();
|
|
314
|
+
return inClass ? null : false;
|
|
315
|
+
}
|
|
316
|
+
const code3 = parseInt(source.slice(pos + 2, close), 16);
|
|
317
|
+
pos = close + 1;
|
|
318
|
+
if (Number.isNaN(code3)) {
|
|
319
|
+
bail();
|
|
320
|
+
return inClass ? null : false;
|
|
321
|
+
}
|
|
322
|
+
if (collect) add(code3);
|
|
323
|
+
return inClass ? code3 : false;
|
|
324
|
+
}
|
|
325
|
+
const hex = source.slice(pos + 1, pos + 5);
|
|
326
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
327
|
+
bail();
|
|
328
|
+
return inClass ? null : false;
|
|
329
|
+
}
|
|
330
|
+
pos += 5;
|
|
331
|
+
const code2 = parseInt(hex, 16);
|
|
332
|
+
if (collect) add(code2);
|
|
333
|
+
return inClass ? code2 : false;
|
|
334
|
+
}
|
|
335
|
+
if (char === "x") {
|
|
336
|
+
const hex = source.slice(pos + 1, pos + 3);
|
|
337
|
+
if (!/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
338
|
+
bail();
|
|
339
|
+
return inClass ? null : false;
|
|
340
|
+
}
|
|
341
|
+
pos += 3;
|
|
342
|
+
const code2 = parseInt(hex, 16);
|
|
343
|
+
if (collect) add(code2);
|
|
344
|
+
return inClass ? code2 : false;
|
|
345
|
+
}
|
|
346
|
+
if (char >= "1" && char <= "9" && !inClass) {
|
|
347
|
+
if (collect) {
|
|
348
|
+
bail();
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
while (source[pos] >= "0" && source[pos] <= "9") pos++;
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
if (char in SIMPLE_ESCAPES) {
|
|
355
|
+
pos++;
|
|
356
|
+
const code2 = SIMPLE_ESCAPES[char];
|
|
357
|
+
if (collect) add(code2);
|
|
358
|
+
return inClass ? code2 : false;
|
|
359
|
+
}
|
|
360
|
+
pos++;
|
|
361
|
+
const code = char.charCodeAt(0);
|
|
362
|
+
if (collect) add(code);
|
|
363
|
+
return inClass ? code : false;
|
|
364
|
+
}
|
|
365
|
+
alternation(true);
|
|
366
|
+
if (bailed || pos !== source.length || found.size === 0) return null;
|
|
367
|
+
return found;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/lexer.js
|
|
371
|
+
var LexError = class extends Error {
|
|
372
|
+
/**
|
|
373
|
+
* @param {string} message
|
|
374
|
+
* @param {{ offset: number, line: number, col: number }} position
|
|
375
|
+
*/
|
|
376
|
+
constructor(message, position) {
|
|
377
|
+
super(message);
|
|
378
|
+
this.name = "LexError";
|
|
379
|
+
this.offset = position.offset;
|
|
380
|
+
this.line = position.line;
|
|
381
|
+
this.col = position.col;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
var DEFAULT_MODE = "default";
|
|
385
|
+
function modesOf(def) {
|
|
386
|
+
if (Array.isArray(def.modes) && def.modes.length > 0) return def.modes;
|
|
387
|
+
if (def.mode) return [def.mode];
|
|
388
|
+
return [DEFAULT_MODE];
|
|
389
|
+
}
|
|
390
|
+
function createLexer(defs, options = {}) {
|
|
391
|
+
const positions = options.positions === "offset" ? "offset" : "full";
|
|
392
|
+
const trackLines = positions === "full";
|
|
393
|
+
if (!Array.isArray(defs) || defs.length === 0) throw new Error("createLexer: expected a non-empty array of token definitions");
|
|
394
|
+
const tokenNames = defs.map((d) => d.name);
|
|
395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
396
|
+
for (const name of tokenNames) {
|
|
397
|
+
if (seen.has(name)) throw new Error(`createLexer: duplicate token name '${name}'`);
|
|
398
|
+
seen.add(name);
|
|
399
|
+
}
|
|
400
|
+
const modeIds = /* @__PURE__ */ new Map([[DEFAULT_MODE, 0]]);
|
|
401
|
+
const modeNames = [DEFAULT_MODE];
|
|
402
|
+
const idOf = (name) => {
|
|
403
|
+
let id = modeIds.get(name);
|
|
404
|
+
if (id === void 0) {
|
|
405
|
+
id = modeNames.length;
|
|
406
|
+
modeIds.set(name, id);
|
|
407
|
+
modeNames.push(name);
|
|
408
|
+
}
|
|
409
|
+
return id;
|
|
410
|
+
};
|
|
411
|
+
for (const def of defs) for (const mode of modesOf(def)) idOf(mode);
|
|
412
|
+
const compiled = defs.map((def) => {
|
|
413
|
+
if (!def.name) throw new Error("createLexer: every token definition needs a name");
|
|
414
|
+
if (!def.pattern && !def.literal) throw new Error(`createLexer: token '${def.name}' needs a pattern or a literal`);
|
|
415
|
+
if (def.pattern && def.literal) throw new Error(`createLexer: token '${def.name}' has both a pattern and a literal; pick one`);
|
|
416
|
+
if (def.longerAlt && !seen.has(def.longerAlt)) throw new Error(`createLexer: token '${def.name}' has longerAlt '${def.longerAlt}', which is not a defined token`);
|
|
417
|
+
if (def.pushMode && def.popMode) {
|
|
418
|
+
throw new Error(
|
|
419
|
+
`createLexer: token '${def.name}' both pushes '${def.pushMode}' and pops. One token cannot do both \u2014 split it into two tokens, one per mode.`
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
if (def.pushMode && !modeIds.has(def.pushMode)) {
|
|
423
|
+
throw new Error(`createLexer: token '${def.name}' pushes mode '${def.pushMode}', which no token belongs to`);
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
name: def.name,
|
|
427
|
+
skip: !!def.skip,
|
|
428
|
+
literal: def.literal,
|
|
429
|
+
longerAlt: def.longerAlt || null,
|
|
430
|
+
regex: def.pattern ? new RegExp(def.pattern, def.ignoreCase ? "iy" : "y") : null,
|
|
431
|
+
modes: modesOf(def),
|
|
432
|
+
pushMode: def.pushMode ? idOf(def.pushMode) : -1,
|
|
433
|
+
popMode: !!def.popMode
|
|
434
|
+
};
|
|
435
|
+
});
|
|
436
|
+
const byName = new Map(compiled.map((c) => [c.name, c]));
|
|
437
|
+
for (const c of compiled) if (c.longerAlt) c.longerAltDef = byName.get(c.longerAlt);
|
|
438
|
+
for (const c of compiled) {
|
|
439
|
+
if (!c.longerAltDef) continue;
|
|
440
|
+
for (const mode of c.modes) {
|
|
441
|
+
if (c.longerAltDef.modes.includes(mode)) continue;
|
|
442
|
+
throw new Error(
|
|
443
|
+
`createLexer: token '${c.name}' is active in mode '${mode}' with longerAlt '${c.longerAlt}', which is not active there. Add '${mode}' to '${c.longerAlt}'.`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
for (const c of compiled) {
|
|
448
|
+
c.firstChars = c.literal !== void 0 ? c.literal.length > 0 ? /* @__PURE__ */ new Set([c.literal.charCodeAt(0)]) : null : firstCharSet(c.regex.source, !!c.regex.ignoreCase);
|
|
449
|
+
}
|
|
450
|
+
const modes = modeNames.map((name) => {
|
|
451
|
+
const members = compiled.filter((c) => c.modes.includes(name));
|
|
452
|
+
const alwaysTry = members.filter((c) => c.firstChars === null);
|
|
453
|
+
const candidatesFor = (code) => members.filter((c) => c.firstChars === null || c.firstChars.has(code));
|
|
454
|
+
const ascii = new Array(128);
|
|
455
|
+
for (let code = 0; code < 128; code++) ascii[code] = candidatesFor(code);
|
|
456
|
+
return {
|
|
457
|
+
name,
|
|
458
|
+
members,
|
|
459
|
+
ascii,
|
|
460
|
+
wide: /* @__PURE__ */ new Map(),
|
|
461
|
+
candidatesFor,
|
|
462
|
+
// If nothing could be analyzed, dispatch is pure overhead; skip it.
|
|
463
|
+
dispatch: alwaysTry.length < members.length
|
|
464
|
+
};
|
|
465
|
+
});
|
|
466
|
+
for (const mode of modes) {
|
|
467
|
+
if (mode.members.length === 0) throw new Error(`createLexer: mode '${mode.name}' has no tokens`);
|
|
468
|
+
}
|
|
469
|
+
function candidates(mode, code) {
|
|
470
|
+
if (code < 128) return mode.ascii[code];
|
|
471
|
+
let list = mode.wide.get(code);
|
|
472
|
+
if (list === void 0) {
|
|
473
|
+
list = mode.candidatesFor(code);
|
|
474
|
+
mode.wide.set(code, list);
|
|
475
|
+
}
|
|
476
|
+
return list;
|
|
477
|
+
}
|
|
478
|
+
const multiMode = modes.length > 1 || compiled.some((c) => c.popMode || c.pushMode !== -1);
|
|
479
|
+
const namesModes = modes.length > 1;
|
|
480
|
+
const initial = modes[0];
|
|
481
|
+
function longerAltEnd(def, text, offset) {
|
|
482
|
+
const alt = def.longerAltDef;
|
|
483
|
+
if (alt.literal !== void 0) return text.startsWith(alt.literal, offset) ? offset + alt.literal.length : -1;
|
|
484
|
+
alt.regex.lastIndex = offset;
|
|
485
|
+
return alt.regex.test(text) ? alt.regex.lastIndex : -1;
|
|
486
|
+
}
|
|
487
|
+
function tokenize(text) {
|
|
488
|
+
if (typeof text !== "string") throw new TypeError("tokenize: expected a string");
|
|
489
|
+
const tokens = [];
|
|
490
|
+
let offset = 0;
|
|
491
|
+
let line = 1;
|
|
492
|
+
let lineStart = 0;
|
|
493
|
+
const length = text.length;
|
|
494
|
+
let mode = initial;
|
|
495
|
+
const stack = multiMode ? [] : null;
|
|
496
|
+
let depth = 0;
|
|
497
|
+
outer: while (offset < length) {
|
|
498
|
+
const applicable = mode.dispatch ? candidates(mode, text.charCodeAt(offset)) : mode.members;
|
|
499
|
+
for (let i = 0; i < applicable.length; i++) {
|
|
500
|
+
const def = applicable[i];
|
|
501
|
+
let end = -1;
|
|
502
|
+
let matched = null;
|
|
503
|
+
if (def.literal !== void 0) {
|
|
504
|
+
if (text.startsWith(def.literal, offset)) end = offset + def.literal.length;
|
|
505
|
+
} else if (def.skip) {
|
|
506
|
+
def.regex.lastIndex = offset;
|
|
507
|
+
if (def.regex.test(text) && def.regex.lastIndex > offset) end = def.regex.lastIndex;
|
|
508
|
+
} else {
|
|
509
|
+
def.regex.lastIndex = offset;
|
|
510
|
+
const match = def.regex.exec(text);
|
|
511
|
+
if (match !== null && match[0].length > 0) matched = match[0];
|
|
512
|
+
if (matched !== null) end = offset + matched.length;
|
|
513
|
+
}
|
|
514
|
+
if (end === -1) continue;
|
|
515
|
+
let winner = def;
|
|
516
|
+
if (def.longerAltDef) {
|
|
517
|
+
const altEnd = longerAltEnd(def, text, offset);
|
|
518
|
+
if (altEnd > end) {
|
|
519
|
+
end = altEnd;
|
|
520
|
+
winner = def.longerAltDef;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (!winner.skip) {
|
|
524
|
+
const image = winner.literal !== void 0 ? winner.literal : winner === def && matched !== null ? matched : text.slice(offset, end);
|
|
525
|
+
tokens.push({
|
|
526
|
+
type: winner.name,
|
|
527
|
+
image,
|
|
528
|
+
start: offset,
|
|
529
|
+
end,
|
|
530
|
+
line: trackLines ? line : 0,
|
|
531
|
+
col: trackLines ? offset - lineStart + 1 : 0
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
if (trackLines) {
|
|
535
|
+
for (let j = offset; j < end; j++) {
|
|
536
|
+
if (text.charCodeAt(j) !== 10) continue;
|
|
537
|
+
line++;
|
|
538
|
+
lineStart = j + 1;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (multiMode) {
|
|
542
|
+
if (winner.popMode) {
|
|
543
|
+
if (depth === 0) {
|
|
544
|
+
throw new LexError(
|
|
545
|
+
`Token '${winner.name}' popped the initial lexer mode at ${describe(offset, line, lineStart)}`,
|
|
546
|
+
{ offset, line: trackLines ? line : 0, col: trackLines ? offset - lineStart + 1 : 0 }
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
mode = stack[--depth];
|
|
550
|
+
} else if (winner.pushMode !== -1) {
|
|
551
|
+
stack[depth++] = mode;
|
|
552
|
+
mode = modes[winner.pushMode];
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
offset = end;
|
|
556
|
+
continue outer;
|
|
557
|
+
}
|
|
558
|
+
const column = offset - lineStart + 1;
|
|
559
|
+
throw new LexError(
|
|
560
|
+
trackLines ? `Unexpected character '${text[offset]}' at line ${line} column ${column}${inMode(mode)}` : `Unexpected character '${text[offset]}' at offset ${offset}${inMode(mode)}`,
|
|
561
|
+
{ offset, line: trackLines ? line : 0, col: trackLines ? column : 0 }
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
return tokens;
|
|
565
|
+
}
|
|
566
|
+
const inMode = (mode) => namesModes ? ` (in lexer mode '${mode.name}')` : "";
|
|
567
|
+
const describe = (offset, line, lineStart) => trackLines ? `line ${line} column ${offset - lineStart + 1}` : `offset ${offset}`;
|
|
568
|
+
return { tokenize, tokenNames, positions, modeNames };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// src/values.js
|
|
572
|
+
var badArity = (length) => new Error(`obj expects an even number of arguments (key, value, ...), received ${length}`);
|
|
573
|
+
var objectFrom = (args) => {
|
|
574
|
+
if (!Array.isArray(args) || args.length === 0 || args.length % 2 !== 0) {
|
|
575
|
+
throw badArity(Array.isArray(args) ? args.length : 1);
|
|
576
|
+
}
|
|
577
|
+
const result = {};
|
|
578
|
+
for (let i = 0; i < args.length; i += 2) result[args[i]] = args[i + 1];
|
|
579
|
+
return result;
|
|
580
|
+
};
|
|
581
|
+
var compileObject = (args, buildState) => {
|
|
582
|
+
if (!Array.isArray(args) || args.length === 0 || args.length % 2 !== 0) return false;
|
|
583
|
+
const strings = ["({["];
|
|
584
|
+
const items = [];
|
|
585
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
586
|
+
items.push(args[i]);
|
|
587
|
+
strings.push("]: ");
|
|
588
|
+
items.push(args[i + 1]);
|
|
589
|
+
strings.push(i + 2 < args.length ? ", [" : "})");
|
|
590
|
+
}
|
|
591
|
+
return buildState.compile(strings, ...items);
|
|
592
|
+
};
|
|
593
|
+
var createValueMethods = () => ({
|
|
594
|
+
obj: { method: objectFrom, compile: compileObject }
|
|
595
|
+
});
|
|
596
|
+
export {
|
|
597
|
+
FAIL,
|
|
598
|
+
LexError,
|
|
599
|
+
ParseError,
|
|
600
|
+
buildParseError,
|
|
601
|
+
createLexer,
|
|
602
|
+
createState,
|
|
603
|
+
createValueMethods,
|
|
604
|
+
expect
|
|
605
|
+
};
|