grammar-composer 0.4.0 → 0.5.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/README.md +144 -89
- package/dist/exports/Exports.d.ts +2 -0
- package/dist/exports/Exports.d.ts.map +1 -1
- package/dist/exports/Exports.js +2 -0
- package/dist/exports/Exports.js.map +1 -1
- package/dist/parser-generator/Grammar.d.ts +8 -1
- package/dist/parser-generator/Grammar.d.ts.map +1 -1
- package/dist/parser-generator/Grammar.js +30 -225
- package/dist/parser-generator/Grammar.js.map +1 -1
- package/dist/parser-generator/ParseError.d.ts +37 -0
- package/dist/parser-generator/ParseError.d.ts.map +1 -0
- package/dist/parser-generator/ParseError.js +226 -0
- package/dist/parser-generator/ParseError.js.map +1 -0
- package/dist/parser-generator/StaticAnalysis.d.ts +6 -0
- package/dist/parser-generator/StaticAnalysis.d.ts.map +1 -0
- package/dist/parser-generator/StaticAnalysis.js +283 -0
- package/dist/parser-generator/StaticAnalysis.js.map +1 -0
- package/dist/parser-generator/TerminalToText.d.ts +15 -0
- package/dist/parser-generator/TerminalToText.d.ts.map +1 -0
- package/dist/parser-generator/TerminalToText.js +158 -0
- package/dist/parser-generator/TerminalToText.js.map +1 -0
- package/dist/parser-generator/TopDownParser.d.ts +4 -2
- package/dist/parser-generator/TopDownParser.d.ts.map +1 -1
- package/dist/parser-generator/TopDownParser.js +44 -42
- package/dist/parser-generator/TopDownParser.js.map +1 -1
- package/dist/tests/Test.js +23 -23
- package/dist/tests/Test.js.map +1 -1
- package/dist/tests/test-grammars/JsonGrammar.d.ts +12 -11
- package/dist/tests/test-grammars/JsonGrammar.d.ts.map +1 -1
- package/dist/tests/test-grammars/JsonGrammar.js +25 -10
- package/dist/tests/test-grammars/JsonGrammar.js.map +1 -1
- package/dist/tests/test-grammars/RegExpGrammar.d.ts +25 -25
- package/dist/tests/test-grammars/RegExpGrammar.d.ts.map +1 -1
- package/dist/tests/test-grammars/RegExpGrammar.js +48 -31
- package/dist/tests/test-grammars/RegExpGrammar.js.map +1 -1
- package/dist/tests/test-grammars/XmlGrammar.d.ts +11 -10
- package/dist/tests/test-grammars/XmlGrammar.d.ts.map +1 -1
- package/dist/tests/test-grammars/XmlGrammar.js +19 -8
- package/dist/tests/test-grammars/XmlGrammar.js.map +1 -1
- package/dist/utilities/LineAndColumn.d.ts +7 -0
- package/dist/utilities/LineAndColumn.d.ts.map +1 -0
- package/dist/utilities/LineAndColumn.js +52 -0
- package/dist/utilities/LineAndColumn.js.map +1 -0
- package/package.json +1 -1
- package/src/exports/Exports.ts +2 -0
- package/src/parser-generator/Grammar.ts +59 -265
- package/src/parser-generator/ParseError.ts +337 -0
- package/src/parser-generator/StaticAnalysis.ts +340 -0
- package/src/parser-generator/TerminalToText.ts +196 -0
- package/src/parser-generator/TopDownParser.ts +67 -49
- package/src/tests/Test.ts +26 -24
- package/src/tests/test-grammars/JsonGrammar.ts +28 -10
- package/src/tests/test-grammars/RegExpGrammar.ts +65 -46
- package/src/tests/test-grammars/XmlGrammar.ts +20 -8
- package/src/utilities/LineAndColumn.ts +61 -0
|
@@ -2,33 +2,48 @@ import { Pattern, buildRegExp, inputStart, isPatternOptional } from 'regexp-comp
|
|
|
2
2
|
import { isArray, isBoolean, isFunction, isString } from '../utilities/Utilities.js'
|
|
3
3
|
|
|
4
4
|
import { parse } from './TopDownParser.js'
|
|
5
|
+
import { detectAndAnnotateOptionalNodes, detectAndErrorOnLeftRecursion, validatePatternCaptureGroups } from './StaticAnalysis.js'
|
|
5
6
|
|
|
6
7
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
7
8
|
// Grammar builder method
|
|
8
9
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
9
|
-
export function buildGrammar<T extends { [key: string]: any }>(
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
export function buildGrammar<T extends { [key: string]: any }>(
|
|
11
|
+
definitionObject: T | (new () => T),
|
|
12
|
+
startProductionName: keyof T,
|
|
13
|
+
options?: GrammarBuilderOptions<T>): Grammar<T> {
|
|
14
|
+
|
|
15
|
+
options = {
|
|
16
|
+
unwrappedNonterminalNames: [],
|
|
17
|
+
...options
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isFunction(definitionObject)) {
|
|
21
|
+
definitionObject = new definitionObject()
|
|
12
22
|
}
|
|
13
23
|
|
|
14
24
|
const nameLookup = new Map<any, keyof T>()
|
|
15
25
|
const nonterminalLookup = new Map<Function, Nonterminal>()
|
|
16
26
|
const optionalNonterminalLookup = new Map<Function, Nonterminal>()
|
|
17
27
|
|
|
18
|
-
for (const key in
|
|
19
|
-
const objectProperty =
|
|
28
|
+
for (const key in definitionObject) {
|
|
29
|
+
const objectProperty = definitionObject[key]
|
|
20
30
|
|
|
21
31
|
nameLookup.set(objectProperty, key)
|
|
22
32
|
|
|
23
33
|
if (!isFunction(objectProperty)) {
|
|
24
|
-
|
|
34
|
+
throw new Error(`Grammar definition contains a property '${key}', which is not a function. All object properties must be productions, declared as functions.`)
|
|
25
35
|
}
|
|
26
36
|
|
|
27
|
-
const productionContent = objectProperty.call(
|
|
37
|
+
const productionContent = objectProperty.call(definitionObject)
|
|
28
38
|
const normalizedProduction = productionToGrammarElement(productionContent)
|
|
39
|
+
const isUnwrappedNonterminal = options.unwrappedNonterminalNames?.includes(key) === true
|
|
29
40
|
|
|
30
|
-
const newNonterminal = nonterminal(key, normalizedProduction)
|
|
31
|
-
const newOptionalNonterminal = {
|
|
41
|
+
const newNonterminal = nonterminal(key, normalizedProduction, isUnwrappedNonterminal)
|
|
42
|
+
const newOptionalNonterminal = {
|
|
43
|
+
...newNonterminal,
|
|
44
|
+
optional: true,
|
|
45
|
+
grammarNonterminal: newNonterminal,
|
|
46
|
+
}
|
|
32
47
|
|
|
33
48
|
nonterminalLookup.set(objectProperty, newNonterminal)
|
|
34
49
|
optionalNonterminalLookup.set(objectProperty, newOptionalNonterminal)
|
|
@@ -55,7 +70,7 @@ export function buildGrammar<T extends { [key: string]: any }>(obj: T | (new ()
|
|
|
55
70
|
optionalNonterminal.content = preparedContent
|
|
56
71
|
}
|
|
57
72
|
|
|
58
|
-
const startNonterminal = nonterminalLookup.get(
|
|
73
|
+
const startNonterminal = nonterminalLookup.get(definitionObject[startProductionName] as Function)
|
|
59
74
|
|
|
60
75
|
if (!startNonterminal) {
|
|
61
76
|
throw new Error(`Couldn't find a start production named '${startProductionName as string}'.`)
|
|
@@ -142,6 +157,8 @@ function prepareGrammarElement(
|
|
|
142
157
|
|
|
143
158
|
cached: true,
|
|
144
159
|
cacheId: getNewCacheId(),
|
|
160
|
+
|
|
161
|
+
grammarNonterminal: nonterminal.grammarNonterminal ?? nonterminal
|
|
145
162
|
}
|
|
146
163
|
} else {
|
|
147
164
|
return nonterminal
|
|
@@ -154,255 +171,7 @@ function prepareGrammarElement(
|
|
|
154
171
|
}
|
|
155
172
|
|
|
156
173
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
157
|
-
//
|
|
158
|
-
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
159
|
-
function detectAndAnnotateOptionalNodes(rootNode: GrammarElement) {
|
|
160
|
-
const visitedNodes = new Set<GrammarElement>()
|
|
161
|
-
|
|
162
|
-
const resolvedNodes = new Map<GrammarElement, boolean>()
|
|
163
|
-
const unresolvedNodes = new Map<GrammarElement, { dependencies: Set<GrammarElement>, isChoice: boolean }>()
|
|
164
|
-
|
|
165
|
-
function processDepthFirst(node: GrammarElement): boolean | undefined {
|
|
166
|
-
if (visitedNodes.has(node)) {
|
|
167
|
-
return resolvedNodes.get(node)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
visitedNodes.add(node)
|
|
171
|
-
|
|
172
|
-
switch (node.type) {
|
|
173
|
-
case 'StringTerminal':
|
|
174
|
-
case 'PatternTerminal': {
|
|
175
|
-
resolvedNodes.set(node, node.optional)
|
|
176
|
-
|
|
177
|
-
return node.optional
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
case 'Nonterminal':
|
|
181
|
-
case 'Repetition': {
|
|
182
|
-
const result = processDepthFirst(node.content)
|
|
183
|
-
|
|
184
|
-
if (node.optional) {
|
|
185
|
-
resolvedNodes.set(node, true)
|
|
186
|
-
|
|
187
|
-
return true
|
|
188
|
-
} else if (isBoolean(result)) {
|
|
189
|
-
resolvedNodes.set(node, result)
|
|
190
|
-
|
|
191
|
-
return result
|
|
192
|
-
} else {
|
|
193
|
-
unresolvedNodes.set(node, { dependencies: new Set([node.content]), isChoice: false })
|
|
194
|
-
|
|
195
|
-
return undefined
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
case 'Sequence': {
|
|
200
|
-
const dependencies = new Set<GrammarElement>()
|
|
201
|
-
|
|
202
|
-
let hasNonOptionalResolvedMember = false
|
|
203
|
-
|
|
204
|
-
for (const element of node.members) {
|
|
205
|
-
const result = processDepthFirst(element)
|
|
206
|
-
|
|
207
|
-
if (isBoolean(result)) {
|
|
208
|
-
if (result === false) {
|
|
209
|
-
hasNonOptionalResolvedMember = true
|
|
210
|
-
}
|
|
211
|
-
} else {
|
|
212
|
-
dependencies.add(element)
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (node.optional == true) {
|
|
217
|
-
resolvedNodes.set(node, true)
|
|
218
|
-
|
|
219
|
-
return true
|
|
220
|
-
} else if (hasNonOptionalResolvedMember) {
|
|
221
|
-
resolvedNodes.set(node, false)
|
|
222
|
-
|
|
223
|
-
return false
|
|
224
|
-
} else if (dependencies.size == 0) {
|
|
225
|
-
resolvedNodes.set(node, true)
|
|
226
|
-
|
|
227
|
-
return true
|
|
228
|
-
} else {
|
|
229
|
-
unresolvedNodes.set(node, { dependencies, isChoice: false })
|
|
230
|
-
|
|
231
|
-
return undefined
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
case 'Choice': {
|
|
236
|
-
const dependencies = new Set<GrammarElement>()
|
|
237
|
-
let hasOptionalResolvedMember = false
|
|
238
|
-
|
|
239
|
-
for (const element of node.members) {
|
|
240
|
-
const result = processDepthFirst(element)
|
|
241
|
-
|
|
242
|
-
if (isBoolean(result)) {
|
|
243
|
-
if (result === true) {
|
|
244
|
-
hasOptionalResolvedMember = true
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
dependencies.add(element)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (node.optional == true) {
|
|
252
|
-
resolvedNodes.set(node, true)
|
|
253
|
-
|
|
254
|
-
return true
|
|
255
|
-
} else if (hasOptionalResolvedMember) {
|
|
256
|
-
resolvedNodes.set(node, true)
|
|
257
|
-
|
|
258
|
-
return true
|
|
259
|
-
} else if (dependencies.size == 0) {
|
|
260
|
-
resolvedNodes.set(node, false)
|
|
261
|
-
|
|
262
|
-
return false
|
|
263
|
-
} else {
|
|
264
|
-
unresolvedNodes.set(node, { dependencies, isChoice: true })
|
|
265
|
-
|
|
266
|
-
return undefined
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return undefined
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Process depth first to resolve the easy cases, for productions that contain
|
|
275
|
-
// no cyclic references:
|
|
276
|
-
processDepthFirst(rootNode)
|
|
277
|
-
|
|
278
|
-
// Now the remainder consists of nodes containing cyclic references that have not yet been resolved.
|
|
279
|
-
// Use a form of iterative elimination and substitution to resolve them:
|
|
280
|
-
while (unresolvedNodes.size > 0) {
|
|
281
|
-
// This variable tracks whether at least one dependency was resolved, in any node.
|
|
282
|
-
// If it stays false, it means that no improvement was made during the iteration,
|
|
283
|
-
// and we should exit the loop.
|
|
284
|
-
let atLastOneDependencyResolvedInAnyNode = false
|
|
285
|
-
const nodesToDelete: GrammarElement[] = []
|
|
286
|
-
|
|
287
|
-
// Scan the unresolved nodes to locate any new resolved dependencies
|
|
288
|
-
for (const [node, { dependencies, isChoice }] of unresolvedNodes) {
|
|
289
|
-
let nodeResolved = false
|
|
290
|
-
|
|
291
|
-
// Iterate over all unresolved dependencies for the node
|
|
292
|
-
for (const dependency of Array.from(dependencies)) {
|
|
293
|
-
// Check if the dependency has been resolved
|
|
294
|
-
const value = resolvedNodes.get(dependency)
|
|
295
|
-
|
|
296
|
-
if (value !== undefined) {
|
|
297
|
-
// If it did, record that some dependencies were resolved
|
|
298
|
-
atLastOneDependencyResolvedInAnyNode = true
|
|
299
|
-
|
|
300
|
-
if (isChoice) {
|
|
301
|
-
if (value === true) {
|
|
302
|
-
resolvedNodes.set(node, true)
|
|
303
|
-
nodeResolved = true
|
|
304
|
-
break
|
|
305
|
-
}
|
|
306
|
-
} else {
|
|
307
|
-
if (value === false) {
|
|
308
|
-
resolvedNodes.set(node, false)
|
|
309
|
-
nodeResolved = true
|
|
310
|
-
break
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
dependencies.delete(dependency)
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (nodeResolved) {
|
|
319
|
-
nodesToDelete.push(node)
|
|
320
|
-
} else if (dependencies.size === 0) {
|
|
321
|
-
if (isChoice) {
|
|
322
|
-
resolvedNodes.set(node, false)
|
|
323
|
-
} else {
|
|
324
|
-
resolvedNodes.set(node, true)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
nodesToDelete.push(node)
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
for (const node of nodesToDelete) {
|
|
332
|
-
unresolvedNodes.delete(node)
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// If not even one dependency was eliminated for any node,
|
|
336
|
-
// it means that only mutually cyclic nodes are left unresolved, so exit the loop.
|
|
337
|
-
if (!atLastOneDependencyResolvedInAnyNode) {
|
|
338
|
-
break
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// All remaining unresolved nodes must now be optional,
|
|
343
|
-
// since they are all mutually cyclic and all their non-cyclic grammar elements are known to be optional.
|
|
344
|
-
for (const node of unresolvedNodes.keys()) {
|
|
345
|
-
resolvedNodes.set(node, true)
|
|
346
|
-
unresolvedNodes.delete(node)
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Finally set the 'optional' property of all nodes based on the detected values.
|
|
350
|
-
for (const [node, isOptional] of resolvedNodes) {
|
|
351
|
-
node.optional = isOptional
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function detectAndErrorOnLeftRecursion(rootNode: GrammarElement) {
|
|
356
|
-
const currentlyIteratedNodes = new Set<GrammarElement>()
|
|
357
|
-
|
|
358
|
-
function detect(node: GrammarElement) {
|
|
359
|
-
if (currentlyIteratedNodes.has(node)) {
|
|
360
|
-
if (node.type === 'Nonterminal') {
|
|
361
|
-
throw new Error(`Detected left recursion for nonterminal '${node.name}'.`)
|
|
362
|
-
} else {
|
|
363
|
-
throw new Error(`Detected left recursion for node: ${JSON.stringify(node, undefined, 4)}`)
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
currentlyIteratedNodes.add(node)
|
|
368
|
-
|
|
369
|
-
switch (node.type) {
|
|
370
|
-
case 'Nonterminal':
|
|
371
|
-
case 'Repetition': {
|
|
372
|
-
detect(node.content)
|
|
373
|
-
|
|
374
|
-
break
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
case 'Sequence': {
|
|
378
|
-
for (const member of node.members) {
|
|
379
|
-
detect(member)
|
|
380
|
-
|
|
381
|
-
if (!member.optional) {
|
|
382
|
-
break
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
break
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
case 'Choice': {
|
|
390
|
-
for (const member of node.members) {
|
|
391
|
-
detect(member)
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
break
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
currentlyIteratedNodes.delete(node)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
detect(rootNode)
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
405
|
-
// Exported builder methods
|
|
174
|
+
// Exported AST builder functions
|
|
406
175
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
407
176
|
export function zeroOrMore(content: Production): Repetition {
|
|
408
177
|
return {
|
|
@@ -423,7 +192,7 @@ export function oneOrMore(content: Production): Repetition {
|
|
|
423
192
|
}
|
|
424
193
|
|
|
425
194
|
export function anyOf(...members: Production[]): Choice {
|
|
426
|
-
if (members.length
|
|
195
|
+
if (members.length === 0) {
|
|
427
196
|
throw new Error(`'anyOf' requires at least one member.`)
|
|
428
197
|
}
|
|
429
198
|
|
|
@@ -439,7 +208,7 @@ export function anyOf(...members: Production[]): Choice {
|
|
|
439
208
|
}
|
|
440
209
|
|
|
441
210
|
export function bestOf(...members: Production[]): Choice {
|
|
442
|
-
if (members.length
|
|
211
|
+
if (members.length === 0) {
|
|
443
212
|
throw new Error(`'bestOf' requires at least one member.`)
|
|
444
213
|
}
|
|
445
214
|
|
|
@@ -466,6 +235,8 @@ export function pattern(pattern: Pattern): PatternTerminal {
|
|
|
466
235
|
pattern = [inputStart, pattern]
|
|
467
236
|
}
|
|
468
237
|
|
|
238
|
+
validatePatternCaptureGroups(pattern)
|
|
239
|
+
|
|
469
240
|
const regExp = buildRegExp(pattern)
|
|
470
241
|
const optional = isPatternOptional(pattern)
|
|
471
242
|
|
|
@@ -488,7 +259,7 @@ export function uncached<T extends Production>(content: Production): T {
|
|
|
488
259
|
}
|
|
489
260
|
|
|
490
261
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
491
|
-
// Internal builder methods
|
|
262
|
+
// Internal AST builder methods
|
|
492
263
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
493
264
|
function stringTerminal(content: string): StringTerminal {
|
|
494
265
|
if (content.length < 1) {
|
|
@@ -504,7 +275,7 @@ function stringTerminal(content: string): StringTerminal {
|
|
|
504
275
|
}
|
|
505
276
|
}
|
|
506
277
|
|
|
507
|
-
function nonterminal(name: string, content: GrammarElement): Nonterminal {
|
|
278
|
+
function nonterminal(name: string, content: GrammarElement, unwrapped: boolean): Nonterminal {
|
|
508
279
|
if (name.length < 1) {
|
|
509
280
|
throw new Error(`A nonterminal name must include at least 1 character.`)
|
|
510
281
|
}
|
|
@@ -513,9 +284,10 @@ function nonterminal(name: string, content: GrammarElement): Nonterminal {
|
|
|
513
284
|
type: 'Nonterminal',
|
|
514
285
|
name,
|
|
515
286
|
content,
|
|
516
|
-
optional: false,
|
|
517
287
|
|
|
288
|
+
optional: false,
|
|
518
289
|
cached: false,
|
|
290
|
+
unwrapped,
|
|
519
291
|
}
|
|
520
292
|
}
|
|
521
293
|
|
|
@@ -554,7 +326,7 @@ function productionToGrammarElement(production: Production): GrammarElement {
|
|
|
554
326
|
}
|
|
555
327
|
|
|
556
328
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
557
|
-
//
|
|
329
|
+
// Grammar class
|
|
558
330
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
559
331
|
export class Grammar<T> {
|
|
560
332
|
readonly productions: Record<keyof T, any>
|
|
@@ -577,6 +349,9 @@ export class Grammar<T> {
|
|
|
577
349
|
}
|
|
578
350
|
}
|
|
579
351
|
|
|
352
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
353
|
+
// Type definitions
|
|
354
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
580
355
|
export type Production = string | GrammarElement | (() => Production) | Production[]
|
|
581
356
|
|
|
582
357
|
export type GrammarElement =
|
|
@@ -594,6 +369,10 @@ interface GrammarElementBase {
|
|
|
594
369
|
|
|
595
370
|
cached: boolean
|
|
596
371
|
cacheId?: number
|
|
372
|
+
|
|
373
|
+
// The name of the grammar property this element was assigned to, if any.
|
|
374
|
+
// Used by the parser to refer to the element by name in error messages.
|
|
375
|
+
name?: string
|
|
597
376
|
}
|
|
598
377
|
|
|
599
378
|
export type Terminal = StringTerminal | PatternTerminal
|
|
@@ -613,6 +392,12 @@ export interface Nonterminal extends GrammarElementBase {
|
|
|
613
392
|
type: 'Nonterminal'
|
|
614
393
|
name: string
|
|
615
394
|
content: GrammarElement
|
|
395
|
+
unwrapped: boolean
|
|
396
|
+
|
|
397
|
+
// The canonical nonterminal as defined in the grammar. Clones created for
|
|
398
|
+
// optional references and cached references keep a reference to the original,
|
|
399
|
+
// so identity comparisons work across clones.
|
|
400
|
+
grammarNonterminal?: Nonterminal
|
|
616
401
|
}
|
|
617
402
|
|
|
618
403
|
export interface Sequence extends GrammarElementBase {
|
|
@@ -635,3 +420,12 @@ export interface NonterminalReference extends GrammarElementBase {
|
|
|
635
420
|
type: 'NonterminalReference'
|
|
636
421
|
reference: Function
|
|
637
422
|
}
|
|
423
|
+
|
|
424
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
425
|
+
// Builder type definitions
|
|
426
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
427
|
+
export interface GrammarBuilderOptions<T> {
|
|
428
|
+
unwrappedNonterminalNames?: GrammarNonterminalNames<T>
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export type GrammarNonterminalNames<T> = (keyof T)[]
|