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.
Files changed (55) hide show
  1. package/README.md +144 -89
  2. package/dist/exports/Exports.d.ts +2 -0
  3. package/dist/exports/Exports.d.ts.map +1 -1
  4. package/dist/exports/Exports.js +2 -0
  5. package/dist/exports/Exports.js.map +1 -1
  6. package/dist/parser-generator/Grammar.d.ts +8 -1
  7. package/dist/parser-generator/Grammar.d.ts.map +1 -1
  8. package/dist/parser-generator/Grammar.js +30 -225
  9. package/dist/parser-generator/Grammar.js.map +1 -1
  10. package/dist/parser-generator/ParseError.d.ts +37 -0
  11. package/dist/parser-generator/ParseError.d.ts.map +1 -0
  12. package/dist/parser-generator/ParseError.js +226 -0
  13. package/dist/parser-generator/ParseError.js.map +1 -0
  14. package/dist/parser-generator/StaticAnalysis.d.ts +6 -0
  15. package/dist/parser-generator/StaticAnalysis.d.ts.map +1 -0
  16. package/dist/parser-generator/StaticAnalysis.js +283 -0
  17. package/dist/parser-generator/StaticAnalysis.js.map +1 -0
  18. package/dist/parser-generator/TerminalToText.d.ts +15 -0
  19. package/dist/parser-generator/TerminalToText.d.ts.map +1 -0
  20. package/dist/parser-generator/TerminalToText.js +158 -0
  21. package/dist/parser-generator/TerminalToText.js.map +1 -0
  22. package/dist/parser-generator/TopDownParser.d.ts +4 -2
  23. package/dist/parser-generator/TopDownParser.d.ts.map +1 -1
  24. package/dist/parser-generator/TopDownParser.js +44 -42
  25. package/dist/parser-generator/TopDownParser.js.map +1 -1
  26. package/dist/tests/Test.js +23 -23
  27. package/dist/tests/Test.js.map +1 -1
  28. package/dist/tests/test-grammars/JsonGrammar.d.ts +12 -11
  29. package/dist/tests/test-grammars/JsonGrammar.d.ts.map +1 -1
  30. package/dist/tests/test-grammars/JsonGrammar.js +25 -10
  31. package/dist/tests/test-grammars/JsonGrammar.js.map +1 -1
  32. package/dist/tests/test-grammars/RegExpGrammar.d.ts +25 -25
  33. package/dist/tests/test-grammars/RegExpGrammar.d.ts.map +1 -1
  34. package/dist/tests/test-grammars/RegExpGrammar.js +48 -31
  35. package/dist/tests/test-grammars/RegExpGrammar.js.map +1 -1
  36. package/dist/tests/test-grammars/XmlGrammar.d.ts +11 -10
  37. package/dist/tests/test-grammars/XmlGrammar.d.ts.map +1 -1
  38. package/dist/tests/test-grammars/XmlGrammar.js +19 -8
  39. package/dist/tests/test-grammars/XmlGrammar.js.map +1 -1
  40. package/dist/utilities/LineAndColumn.d.ts +7 -0
  41. package/dist/utilities/LineAndColumn.d.ts.map +1 -0
  42. package/dist/utilities/LineAndColumn.js +52 -0
  43. package/dist/utilities/LineAndColumn.js.map +1 -0
  44. package/package.json +1 -1
  45. package/src/exports/Exports.ts +2 -0
  46. package/src/parser-generator/Grammar.ts +59 -265
  47. package/src/parser-generator/ParseError.ts +337 -0
  48. package/src/parser-generator/StaticAnalysis.ts +340 -0
  49. package/src/parser-generator/TerminalToText.ts +196 -0
  50. package/src/parser-generator/TopDownParser.ts +67 -49
  51. package/src/tests/Test.ts +26 -24
  52. package/src/tests/test-grammars/JsonGrammar.ts +28 -10
  53. package/src/tests/test-grammars/RegExpGrammar.ts +65 -46
  54. package/src/tests/test-grammars/XmlGrammar.ts +20 -8
  55. package/src/utilities/LineAndColumn.ts +61 -0
@@ -5,7 +5,14 @@ export class RegExpGrammar {
5
5
  //////////////////////////////////////////////////////////////////////////////////////////////
6
6
  // High-level productions
7
7
  //////////////////////////////////////////////////////////////////////////////////////////////
8
- root = () => this.sequenceOrDisjunction
8
+ root = () => this.disjunctionOrSequence
9
+
10
+ disjunctionOrSequence = () => [
11
+ G.anyOf(
12
+ this.disjunction,
13
+ this.sequence,
14
+ )
15
+ ]
9
16
 
10
17
  disjunction = () => [
11
18
  this.sequence,
@@ -20,11 +27,27 @@ export class RegExpGrammar {
20
27
  G.zeroOrMore(this.sequenceElement)
21
28
  )
22
29
 
23
- sequenceOrDisjunction = [
30
+ sequenceElement = () => G.anyOf(
31
+ this.anchor,
32
+ this.lookaround,
33
+ this.quantifiedExpression,
34
+ this.quantifiableExpression,
35
+ )
36
+
37
+ //////////////////////////////////////////////////////////////////////////////////////////////
38
+ // Quantifiable expression
39
+ //////////////////////////////////////////////////////////////////////////////////////////////
40
+ quantifiableExpression = () => G.cached(
24
41
  G.anyOf(
25
- this.disjunction,
26
- this.sequence,
42
+ this.group,
43
+ this.backReference,
44
+ this.singleCharExpression
27
45
  )
46
+ )
47
+
48
+ quantifiedExpression = () => [
49
+ this.quantifiableExpression,
50
+ this.quantifier,
28
51
  ]
29
52
 
30
53
  //////////////////////////////////////////////////////////////////////////////////////////////
@@ -86,7 +109,7 @@ export class RegExpGrammar {
86
109
  //////////////////////////////////////////////////////////////////////////////////////////////
87
110
  uncapturedGroup = () => [
88
111
  '(?:',
89
- this.sequenceOrDisjunction,
112
+ this.disjunctionOrSequence,
90
113
  ')',
91
114
  ]
92
115
 
@@ -96,17 +119,17 @@ export class RegExpGrammar {
96
119
  R.oneOrMore(identifierChar))
97
120
  ),
98
121
  '>',
99
- this.sequenceOrDisjunction,
122
+ this.disjunctionOrSequence,
100
123
  ')',
101
124
  ]
102
125
 
103
126
  unnamedCaptureGroup = () => [
104
127
  '(',
105
- this.sequenceOrDisjunction,
128
+ this.disjunctionOrSequence,
106
129
  ')',
107
130
  ]
108
131
 
109
- group = G.anyOf(
132
+ group = () => G.anyOf(
110
133
  this.uncapturedGroup,
111
134
  this.namedCaptureGroup,
112
135
  this.unnamedCaptureGroup,
@@ -132,7 +155,7 @@ export class RegExpGrammar {
132
155
  '>',
133
156
  ])
134
157
 
135
- backReference = G.anyOf(
158
+ backReference = () => G.anyOf(
136
159
  this.unnamedBackreference,
137
160
  this.namedBackreference,
138
161
  )
@@ -142,29 +165,29 @@ export class RegExpGrammar {
142
165
  //////////////////////////////////////////////////////////////////////////////////////////////
143
166
  positiveLookahead = () => [
144
167
  '(?=',
145
- this.sequenceOrDisjunction,
168
+ this.disjunctionOrSequence,
146
169
  ')',
147
170
  ]
148
171
 
149
172
  negativeLookahead = () => [
150
173
  '(?!',
151
- this.sequenceOrDisjunction,
174
+ this.disjunctionOrSequence,
152
175
  ')',
153
176
  ]
154
177
 
155
178
  positiveLookbehind = () => [
156
179
  '(?<=',
157
- this.sequenceOrDisjunction,
180
+ this.disjunctionOrSequence,
158
181
  ')',
159
182
  ]
160
183
 
161
184
  negativeLookbehind = () => [
162
185
  '(?<!',
163
- this.sequenceOrDisjunction,
186
+ this.disjunctionOrSequence,
164
187
  ')',
165
188
  ]
166
189
 
167
- lookaround = G.anyOf(
190
+ lookaround = () => G.anyOf(
168
191
  this.positiveLookahead,
169
192
  this.negativeLookahead,
170
193
  this.positiveLookbehind,
@@ -244,17 +267,17 @@ export class RegExpGrammar {
244
267
  '}',
245
268
  ])
246
269
 
247
- unicodeProperty = () => G.pattern([
270
+ unicodeProperty = () => [
248
271
  '\\p',
249
272
  this.unicodePropertyBodyPattern,
250
- ])
273
+ ]
251
274
 
252
- notUnicodeProperty = () => G.pattern([
275
+ notUnicodeProperty = () => [
253
276
  '\\P',
254
277
  this.unicodePropertyBodyPattern,
255
- ])
278
+ ]
256
279
 
257
- unicodePropertyBodyPattern: R.Pattern = [
280
+ unicodePropertyBodyPattern = () => G.pattern([
258
281
  '{',
259
282
  R.captureAs('property',
260
283
  R.oneOrMore(R.anyOf(letter, digit, '_', '-'))
@@ -266,7 +289,7 @@ export class RegExpGrammar {
266
289
  ),
267
290
  ]),
268
291
  '}',
269
- ]
292
+ ])
270
293
 
271
294
  charClass = () => [
272
295
  '[',
@@ -283,7 +306,7 @@ export class RegExpGrammar {
283
306
  ']',
284
307
  ]
285
308
 
286
- charcodeOrEscapedChar = G.anyOf(
309
+ charcodeOrEscapedChar = () => G.anyOf(
287
310
  this.codepoint,
288
311
  this.hexCharcode,
289
312
  this.controlCharcode,
@@ -297,7 +320,7 @@ export class RegExpGrammar {
297
320
  )
298
321
  )
299
322
 
300
- charRangeElement = G.anyOf(
323
+ charRangeElement = () => G.anyOf(
301
324
  this.charcodeOrEscapedChar,
302
325
  this.charClassLiteral,
303
326
  )
@@ -321,7 +344,8 @@ export class RegExpGrammar {
321
344
 
322
345
  anyCharWildcard = () => '.'
323
346
 
324
- anchor = G.anyOf(this.inputStartAnchor, this.inputEndAnchor)
347
+ anchor = () =>
348
+ G.anyOf(this.inputStartAnchor, this.inputEndAnchor)
325
349
 
326
350
  escapedCharacterClass = () => G.pattern(R.anyOf(
327
351
  R.captureAs('whitespace', escapedChars.whitespace),
@@ -339,29 +363,6 @@ export class RegExpGrammar {
339
363
  R.captureAs('verticalTab', escapedChars.verticalTab),
340
364
  R.captureAs('backwardSlash', escapedChars.backwardSlash),
341
365
  ))
342
-
343
- //////////////////////////////////////////////////////////////////////////////////////////////
344
- // Sequence elements (positioned here due to TypeScript class member ordering requirements)
345
- //////////////////////////////////////////////////////////////////////////////////////////////
346
- quantifiableExpression = G.cached(
347
- G.anyOf(
348
- this.group,
349
- this.backReference,
350
- this.singleCharExpression
351
- )
352
- )
353
-
354
- quantifiedExpression = () => [
355
- this.quantifiableExpression,
356
- this.quantifier,
357
- ]
358
-
359
- sequenceElement = G.anyOf(
360
- this.anchor,
361
- this.lookaround,
362
- this.quantifiedExpression,
363
- this.quantifiableExpression,
364
- )
365
366
  }
366
367
 
367
368
  //////////////////////////////////////////////////////////////////////////////////////////////
@@ -391,3 +392,21 @@ const escapedChars = {
391
392
  verticalTab: '\\v',
392
393
  backwardSlash: '\\\\'
393
394
  }
395
+
396
+ //////////////////////////////////////////////////////////////////////////////////////////////
397
+ // Wrapped nonterminal names
398
+ //////////////////////////////////////////////////////////////////////////////////////////////
399
+ export const regExpGrammarUnwrappedNonterminalNames: G.GrammarNonterminalNames<RegExpGrammar> = [
400
+ 'disjunctionOrSequence',
401
+ 'group',
402
+ 'backReference',
403
+ 'lookaround',
404
+ 'unicodeProperty',
405
+ 'notUnicodeProperty',
406
+ 'unicodePropertyBodyPattern',
407
+ 'charcodeOrEscapedChar',
408
+ 'charRangeElement',
409
+ 'anchor',
410
+ 'quantifiableExpression',
411
+ 'sequenceElement',
412
+ ]
@@ -14,7 +14,7 @@ export class XmlGrammar {
14
14
  )
15
15
  ]
16
16
 
17
- textFragment = G.pattern([
17
+ textFragment = () => G.pattern([
18
18
  R.oneOrMore(R.notAnyOfChars('<'))
19
19
  ])
20
20
 
@@ -26,7 +26,7 @@ export class XmlGrammar {
26
26
  this.tagEnd
27
27
  ]
28
28
 
29
- openingTagStart = G.pattern([
29
+ openingTagStart = () => G.pattern([
30
30
  '<',
31
31
 
32
32
  R.possibly('?'),
@@ -38,7 +38,7 @@ export class XmlGrammar {
38
38
  R.zeroOrMore(R.whitespace),
39
39
  ])
40
40
 
41
- tagEnd = G.pattern([
41
+ tagEnd = () => G.pattern([
42
42
  R.zeroOrMore(R.whitespace),
43
43
 
44
44
  R.possibly(R.anyOf('/', '?')),
@@ -46,7 +46,7 @@ export class XmlGrammar {
46
46
  '>'
47
47
  ])
48
48
 
49
- attribute = G.pattern([
49
+ attribute = () => G.pattern([
50
50
  R.zeroOrMore(R.whitespace),
51
51
 
52
52
  R.captureAs('attributeName',
@@ -66,7 +66,7 @@ export class XmlGrammar {
66
66
  ])
67
67
  ])
68
68
 
69
- closingTag = G.pattern([
69
+ closingTag = () => G.pattern([
70
70
  '</',
71
71
 
72
72
  R.zeroOrMore(R.whitespace),
@@ -88,7 +88,7 @@ export class XmlGrammar {
88
88
  this.tagEnd
89
89
  ]
90
90
 
91
- declarationTagOpening = G.pattern([
91
+ declarationTagOpening = () => G.pattern([
92
92
  '<!',
93
93
 
94
94
  R.captureAs('tagName',
@@ -98,7 +98,7 @@ export class XmlGrammar {
98
98
  R.zeroOrMore(R.whitespace)
99
99
  ])
100
100
 
101
- declarationTagAttribute = G.pattern([
101
+ declarationTagAttribute = () => G.pattern([
102
102
  R.zeroOrMore(R.whitespace),
103
103
 
104
104
  R.anyOf(
@@ -112,7 +112,7 @@ export class XmlGrammar {
112
112
  R.zeroOrMore(R.whitespace),
113
113
  ])
114
114
 
115
- comment = G.pattern([
115
+ comment = () => G.pattern([
116
116
  '<!--',
117
117
 
118
118
  R.captureAs('commentBody',
@@ -123,6 +123,9 @@ export class XmlGrammar {
123
123
  ])
124
124
  }
125
125
 
126
+ //////////////////////////////////////////////////////////////////////////////////////////////
127
+ // Helper regular expressions
128
+ //////////////////////////////////////////////////////////////////////////////////////////////
126
129
  const quotedString = R.anyOf(
127
130
  [
128
131
  '"',
@@ -139,3 +142,12 @@ const quotedString = R.anyOf(
139
142
  "'"
140
143
  ],
141
144
  )
145
+
146
+ //////////////////////////////////////////////////////////////////////////////////////////////
147
+ // Wrapped nonterminal names
148
+ //////////////////////////////////////////////////////////////////////////////////////////////
149
+ export const xmlGrammarUnwrappedNonterminalNames: G.GrammarNonterminalNames<XmlGrammar> = [
150
+ 'openingTagStart',
151
+ 'tagEnd',
152
+ 'declarationTagOpening',
153
+ ]
@@ -0,0 +1,61 @@
1
+ /////////////////////////////////////////////////////////////////////////////////////////////////
2
+ // Position helpers
3
+ /////////////////////////////////////////////////////////////////////////////////////////////////
4
+ export function getLineAndColumn(input: string, offset: number): { line: number, column: number } {
5
+ let line = 1
6
+ let lineStartOffset = 0
7
+
8
+ for (let i = 0; i < offset && i < input.length; i++) {
9
+ if (input[i] === '\n') {
10
+ line += 1
11
+ lineStartOffset = i + 1
12
+ }
13
+ }
14
+
15
+ return {
16
+ line,
17
+ column: offset - lineStartOffset + 1
18
+ }
19
+ }
20
+
21
+ const tabSize = 4
22
+
23
+ export function expandTabs(text: string): string {
24
+ let result = ''
25
+ let column = 0
26
+
27
+ for (const char of text) {
28
+ if (char === '\t') {
29
+ const spacesToNextTabStop = tabSize - (column % tabSize)
30
+
31
+ result += ' '.repeat(spacesToNextTabStop)
32
+ column += spacesToNextTabStop
33
+ } else {
34
+ result += char
35
+ column += 1
36
+ }
37
+ }
38
+
39
+ return result
40
+ }
41
+
42
+ export function buildCaretSpacing(text: string, caretColumn: number): string {
43
+ let spacing = ''
44
+ let column = 0
45
+
46
+ for (let i = 0; i < caretColumn - 1 && i < text.length; i++) {
47
+ const char = text[i]
48
+
49
+ if (char === '\t') {
50
+ const spacesToNextTabStop = tabSize - (column % tabSize)
51
+
52
+ spacing += ' '.repeat(spacesToNextTabStop)
53
+ column += spacesToNextTabStop
54
+ } else {
55
+ spacing += ' '
56
+ column += 1
57
+ }
58
+ }
59
+
60
+ return spacing
61
+ }