grammar-composer 0.3.1 → 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 (64) 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 +12 -5
  7. package/dist/parser-generator/Grammar.d.ts.map +1 -1
  8. package/dist/parser-generator/Grammar.js +81 -263
  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 +52 -52
  25. package/dist/parser-generator/TopDownParser.js.map +1 -1
  26. package/dist/tests/Test.js +36 -26
  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 +34 -28
  33. package/dist/tests/test-grammars/RegExpGrammar.d.ts.map +1 -1
  34. package/dist/tests/test-grammars/RegExpGrammar.js +104 -60
  35. package/dist/tests/test-grammars/RegExpGrammar.js.map +1 -1
  36. package/dist/tests/test-grammars/SimpleTestGrammar1.d.ts +5 -0
  37. package/dist/tests/test-grammars/SimpleTestGrammar1.d.ts.map +1 -0
  38. package/dist/tests/test-grammars/SimpleTestGrammar1.js +7 -0
  39. package/dist/tests/test-grammars/SimpleTestGrammar1.js.map +1 -0
  40. package/dist/tests/test-grammars/XmlGrammar.d.ts +11 -10
  41. package/dist/tests/test-grammars/XmlGrammar.d.ts.map +1 -1
  42. package/dist/tests/test-grammars/XmlGrammar.js +19 -8
  43. package/dist/tests/test-grammars/XmlGrammar.js.map +1 -1
  44. package/dist/utilities/LineAndColumn.d.ts +7 -0
  45. package/dist/utilities/LineAndColumn.d.ts.map +1 -0
  46. package/dist/utilities/LineAndColumn.js +52 -0
  47. package/dist/utilities/LineAndColumn.js.map +1 -0
  48. package/dist/utilities/Utilities.d.ts +7 -7
  49. package/dist/utilities/Utilities.d.ts.map +1 -1
  50. package/dist/utilities/Utilities.js.map +1 -1
  51. package/package.json +2 -2
  52. package/src/exports/Exports.ts +2 -0
  53. package/src/parser-generator/Grammar.ts +120 -308
  54. package/src/parser-generator/ParseError.ts +337 -0
  55. package/src/parser-generator/StaticAnalysis.ts +340 -0
  56. package/src/parser-generator/TerminalToText.ts +196 -0
  57. package/src/parser-generator/TopDownParser.ts +76 -64
  58. package/src/tests/Test.ts +44 -28
  59. package/src/tests/test-grammars/JsonGrammar.ts +28 -10
  60. package/src/tests/test-grammars/RegExpGrammar.ts +193 -96
  61. package/src/tests/test-grammars/SimpleTestGrammar1.ts +10 -0
  62. package/src/tests/test-grammars/XmlGrammar.ts +20 -8
  63. package/src/utilities/LineAndColumn.ts +61 -0
  64. package/src/utilities/Utilities.ts +7 -7
@@ -5,103 +5,157 @@ export class RegExpGrammar {
5
5
  //////////////////////////////////////////////////////////////////////////////////////////////
6
6
  // High-level productions
7
7
  //////////////////////////////////////////////////////////////////////////////////////////////
8
+ root = () => this.disjunctionOrSequence
9
+
10
+ disjunctionOrSequence = () => [
11
+ G.anyOf(
12
+ this.disjunction,
13
+ this.sequence,
14
+ )
15
+ ]
16
+
8
17
  disjunction = () => [
9
18
  this.sequence,
10
19
 
11
- G.possibly([
12
- this.disjunctionSeparator,
13
- this.disjunction,
20
+ G.oneOrMore([
21
+ '|',
22
+ this.sequence,
14
23
  ])
15
24
  ]
16
25
 
17
- sequence = () => G.zeroOrMore(this.element)
26
+ sequence = () => G.cached(
27
+ G.zeroOrMore(this.sequenceElement)
28
+ )
18
29
 
19
- element = () => G.anyOf(
30
+ sequenceElement = () => G.anyOf(
20
31
  this.anchor,
21
32
  this.lookaround,
22
- this.possiblyQuantifiedExpression,
33
+ this.quantifiedExpression,
34
+ this.quantifiableExpression,
23
35
  )
24
36
 
25
37
  //////////////////////////////////////////////////////////////////////////////////////////////
26
- // Quantifed expressions
38
+ // Quantifiable expression
27
39
  //////////////////////////////////////////////////////////////////////////////////////////////
28
- possiblyQuantifiedExpression = () => [
40
+ quantifiableExpression = () => G.cached(
29
41
  G.anyOf(
30
42
  this.group,
31
43
  this.backReference,
32
44
  this.singleCharExpression
33
- ),
45
+ )
46
+ )
34
47
 
35
- G.possibly(G.anyOf(
36
- this.starQuantifier,
37
- this.plusQuantifier,
38
- this.exactCountQuantifier,
39
- this.countRangeQuantifier,
40
- )),
48
+ quantifiedExpression = () => [
49
+ this.quantifiableExpression,
50
+ this.quantifier,
51
+ ]
41
52
 
42
- G.possibly(this.nongreedyQuantifier), // Nongreedy quantifier
53
+ //////////////////////////////////////////////////////////////////////////////////////////////
54
+ // Quantifier expressions
55
+ //////////////////////////////////////////////////////////////////////////////////////////////
56
+ starQuantifier = () => [
57
+ '*',
58
+ G.possibly(this.nongreedyQuantifier)
43
59
  ]
44
60
 
45
- exactCountQuantifier = () => G.pattern([
46
- '{',
47
- R.captureAs('count', R.oneOrMore(digit)),
48
- '}',
49
- ])
61
+ plusQuantifier = () => [
62
+ '+',
63
+ G.possibly(this.nongreedyQuantifier)
64
+ ]
50
65
 
51
- countRangeQuantifier = () => G.pattern([
52
- '{',
53
- R.captureAs('start', R.oneOrMore(digit)),
54
- ',',
55
- R.possibly(R.captureAs('end', R.oneOrMore(digit))),
56
- '}',
57
- ])
66
+ optionalQuantifier = () => [
67
+ '?',
68
+ G.possibly(this.nongreedyQuantifier)
69
+ ]
70
+
71
+ exactCountQuantifier = () => [
72
+ G.pattern([
73
+ '{',
74
+ R.captureAs('count',
75
+ R.oneOrMore(digit)
76
+ ),
77
+ '}',
78
+ ]),
79
+ G.possibly(this.nongreedyQuantifier)
80
+ ]
81
+
82
+ countRangeQuantifier = () => [
83
+ G.pattern([
84
+ '{',
85
+ R.captureAs('start',
86
+ R.oneOrMore(digit)
87
+ ),
88
+ ',',
89
+ R.possibly(R.captureAs('end',
90
+ R.oneOrMore(digit))
91
+ ),
92
+ '}',
93
+ ]),
94
+ G.possibly(this.nongreedyQuantifier)
95
+ ]
96
+
97
+ nongreedyQuantifier = () => '?'
98
+
99
+ quantifier = () => G.anyOf(
100
+ this.starQuantifier,
101
+ this.plusQuantifier,
102
+ this.optionalQuantifier,
103
+ this.exactCountQuantifier,
104
+ this.countRangeQuantifier,
105
+ )
58
106
 
59
107
  //////////////////////////////////////////////////////////////////////////////////////////////
60
108
  // Group expressions
61
109
  //////////////////////////////////////////////////////////////////////////////////////////////
62
- group = () => G.anyOf(
63
- this.uncapturedGroup,
64
- this.namedCaptureGroup,
65
- this.unnamedCaptureGroup,
66
- )
67
-
68
110
  uncapturedGroup = () => [
69
111
  '(?:',
70
- this.disjunction,
112
+ this.disjunctionOrSequence,
71
113
  ')',
72
114
  ]
73
115
 
74
116
  namedCaptureGroup = () => [
75
117
  '(?<',
76
- G.pattern(R.captureAs('name', R.oneOrMore(identifierChar))),
118
+ G.pattern(R.captureAs('name',
119
+ R.oneOrMore(identifierChar))
120
+ ),
77
121
  '>',
78
- this.disjunction,
122
+ this.disjunctionOrSequence,
79
123
  ')',
80
124
  ]
81
125
 
82
126
  unnamedCaptureGroup = () => [
83
127
  '(',
84
- this.disjunction,
128
+ this.disjunctionOrSequence,
85
129
  ')',
86
130
  ]
87
131
 
132
+ group = () => G.anyOf(
133
+ this.uncapturedGroup,
134
+ this.namedCaptureGroup,
135
+ this.unnamedCaptureGroup,
136
+ )
137
+
88
138
  //////////////////////////////////////////////////////////////////////////////////////////////
89
139
  // Backreferences
90
140
  //////////////////////////////////////////////////////////////////////////////////////////////
91
141
  unnamedBackreference = () =>
92
142
  G.pattern([
93
143
  '\\',
94
- R.captureAs('index', digit),
144
+ R.captureAs('index',
145
+ digit
146
+ ),
95
147
  ])
96
148
 
97
149
  namedBackreference = () =>
98
150
  G.pattern([
99
151
  '\\k<',
100
- R.captureAs('name', R.oneOrMore(identifierChar)),
152
+ R.captureAs('name',
153
+ R.oneOrMore(identifierChar)
154
+ ),
101
155
  '>',
102
156
  ])
103
157
 
104
- backReference = G.anyOf(
158
+ backReference = () => G.anyOf(
105
159
  this.unnamedBackreference,
106
160
  this.namedBackreference,
107
161
  )
@@ -111,29 +165,29 @@ export class RegExpGrammar {
111
165
  //////////////////////////////////////////////////////////////////////////////////////////////
112
166
  positiveLookahead = () => [
113
167
  '(?=',
114
- this.disjunction,
168
+ this.disjunctionOrSequence,
115
169
  ')',
116
170
  ]
117
171
 
118
172
  negativeLookahead = () => [
119
173
  '(?!',
120
- this.disjunction,
174
+ this.disjunctionOrSequence,
121
175
  ')',
122
176
  ]
123
177
 
124
178
  positiveLookbehind = () => [
125
179
  '(?<=',
126
- this.disjunction,
180
+ this.disjunctionOrSequence,
127
181
  ')',
128
182
  ]
129
183
 
130
184
  negativeLookbehind = () => [
131
185
  '(?<!',
132
- this.disjunction,
186
+ this.disjunctionOrSequence,
133
187
  ')',
134
188
  ]
135
189
 
136
- lookaround = G.anyOf(
190
+ lookaround = () => G.anyOf(
137
191
  this.positiveLookahead,
138
192
  this.negativeLookahead,
139
193
  this.positiveLookbehind,
@@ -154,24 +208,31 @@ export class RegExpGrammar {
154
208
  )
155
209
 
156
210
  unreservedCharLiteral = () => G.pattern(
157
- R.notAnyOfChars('[', '.', '*', '+', '?', '^', '$', '{', '}', '(', ')', '|', ']', '\\')
211
+ R.captureAs('char',
212
+ R.notAnyOfChars('[', '.', '*', '+', '?', '^', '$', '{', '}', '(', ')', '|', ']', '\\')
213
+ )
158
214
  )
159
215
 
160
216
  escapedCharLiteral = () => G.pattern([
161
217
  '\\',
162
- R.anyChar
218
+ R.captureAs('char',
219
+ R.anyChar
220
+ )
163
221
  ])
164
222
 
165
223
  hexCharcode = () =>
166
224
  G.pattern([
167
225
  '\\x',
168
- hexDigit,
169
- hexDigit
226
+ R.captureAs('value',
227
+ R.repeated(2, hexDigit)
228
+ )
170
229
  ])
171
230
 
172
231
  controlCharcode = () => G.pattern([
173
232
  '\\c',
174
- R.anyOf(R.charRange('A', 'Z'), R.charRange('a', 'z')),
233
+ R.captureAs('value',
234
+ R.anyOf(R.charRange('A', 'Z'), R.charRange('a', 'z'))
235
+ )
175
236
  ])
176
237
 
177
238
  nullCharcode = () => G.pattern('\\0')
@@ -180,48 +241,55 @@ export class RegExpGrammar {
180
241
  G.pattern([
181
242
  '\\u',
182
243
  '{',
183
- R.captureAs('value', R.repeated([1, 6], hexDigit)),
244
+ R.captureAs('value',
245
+ R.repeated([1, 6], hexDigit)
246
+ ),
184
247
  '}',
185
248
  ]),
186
249
  G.pattern([
187
250
  '\\u',
188
- hexDigit,
189
- hexDigit,
190
- hexDigit,
191
- hexDigit
251
+ R.captureAs('value',
252
+ R.repeated(4, hexDigit)
253
+ )
192
254
  ])
193
255
  )
194
256
 
195
257
  codepointRange = () => G.pattern([
196
258
  '\\u',
197
259
  '{',
198
- R.captureAs('start', R.oneOrMore(hexDigit)),
260
+ R.captureAs('start',
261
+ R.oneOrMore(hexDigit)
262
+ ),
199
263
  '-',
200
- R.captureAs('end', R.oneOrMore(hexDigit)),
264
+ R.captureAs('end',
265
+ R.oneOrMore(hexDigit)
266
+ ),
201
267
  '}',
202
268
  ])
203
269
 
204
- unicodeProperty = () => G.pattern([
270
+ unicodeProperty = () => [
205
271
  '\\p',
206
272
  this.unicodePropertyBodyPattern,
207
- ])
273
+ ]
208
274
 
209
- notUnicodeProperty = () => G.pattern([
275
+ notUnicodeProperty = () => [
210
276
  '\\P',
211
277
  this.unicodePropertyBodyPattern,
212
- ])
278
+ ]
213
279
 
214
- unicodePropertyBodyPattern: R.Pattern = [
280
+ unicodePropertyBodyPattern = () => G.pattern([
215
281
  '{',
216
- R.captureAs('property', R.oneOrMore(R.anyOf(letter, digit, '_', '-'))),
282
+ R.captureAs('property',
283
+ R.oneOrMore(R.anyOf(letter, digit, '_', '-'))
284
+ ),
217
285
  R.possibly([
218
286
  '=',
219
- R.captureAs('value', R.oneOrMore(R.anyOf(letter, digit, '_', '-'))),
287
+ R.captureAs('value',
288
+ R.oneOrMore(R.anyOf(letter, digit, '_', '-'))
289
+ ),
220
290
  ]),
221
291
  '}',
222
- ]
223
-
224
- charClassChar = () => G.pattern(R.notAnyOfChars(']'))
292
+ ])
225
293
 
226
294
  charClass = () => [
227
295
  '[',
@@ -233,36 +301,42 @@ export class RegExpGrammar {
233
301
  this.notUnicodeProperty,
234
302
  this.escapedCharacterClass,
235
303
  this.charcodeOrEscapedChar,
236
- this.charClassChar
304
+ this.charClassLiteral
237
305
  )),
238
306
  ']',
239
307
  ]
240
308
 
241
- charcodeOrEscapedChar = G.anyOf(
309
+ charcodeOrEscapedChar = () => G.anyOf(
242
310
  this.codepoint,
243
311
  this.hexCharcode,
244
312
  this.controlCharcode,
313
+ this.nullCharcode,
245
314
  this.escapedCharLiteral,
246
315
  )
247
316
 
248
- charRangeElement = G.anyOf(
317
+ charClassLiteral = () => G.pattern(
318
+ R.captureAs('char',
319
+ R.notAnyOfChars(']')
320
+ )
321
+ )
322
+
323
+ charRangeElement = () => G.anyOf(
249
324
  this.charcodeOrEscapedChar,
250
- G.pattern(R.notAnyOfChars(']')),
325
+ this.charClassLiteral,
251
326
  )
252
327
 
328
+ charRangeStart = () => this.charRangeElement
329
+ charRangeEnd = () => this.charRangeElement
330
+
253
331
  charRange = () => [
254
- this.charRangeElement,
332
+ this.charRangeStart,
255
333
  '-',
256
- this.charRangeElement,
334
+ this.charRangeEnd,
257
335
  ]
258
336
 
259
337
  //////////////////////////////////////////////////////////////////////////////////////////////
260
338
  // Special symbols
261
339
  //////////////////////////////////////////////////////////////////////////////////////////////
262
- starQuantifier = () => '*'
263
- plusQuantifier = () => '+'
264
- nongreedyQuantifier = () => '?'
265
-
266
340
  charClassNegator = () => '^'
267
341
 
268
342
  inputStartAnchor = () => '^'
@@ -270,34 +344,39 @@ export class RegExpGrammar {
270
344
 
271
345
  anyCharWildcard = () => '.'
272
346
 
273
- disjunctionSeparator = () => '|'
274
-
275
- anchor = G.anyOf(this.inputStartAnchor, this.inputEndAnchor)
347
+ anchor = () =>
348
+ G.anyOf(this.inputStartAnchor, this.inputEndAnchor)
276
349
 
277
350
  escapedCharacterClass = () => G.pattern(R.anyOf(
278
- R.captureAs('whitespace', escapedCharString.whitespace),
279
- R.captureAs('nonWhitespace', escapedCharString.nonWhitespace),
280
- R.captureAs('digit', escapedCharString.digit),
281
- R.captureAs('nonDigit', escapedCharString.nonDigit),
282
- R.captureAs('word', escapedCharString.word),
283
- R.captureAs('nonWord', escapedCharString.nonWord),
284
- R.captureAs('wordBoundary', escapedCharString.wordBoundary),
285
- R.captureAs('nonWordBoundary', escapedCharString.nonWordBoundary),
286
- R.captureAs('formFeed', escapedCharString.formFeed),
287
- R.captureAs('carriageReturn', escapedCharString.carriageReturn),
288
- R.captureAs('lineFeed', escapedCharString.lineFeed),
289
- R.captureAs('tab', escapedCharString.tab),
290
- R.captureAs('verticalTab', escapedCharString.verticalTab),
291
- R.captureAs('backwardSlash', escapedCharString.backwardSlash),
351
+ R.captureAs('whitespace', escapedChars.whitespace),
352
+ R.captureAs('nonWhitespace', escapedChars.nonWhitespace),
353
+ R.captureAs('digit', escapedChars.digit),
354
+ R.captureAs('nonDigit', escapedChars.nonDigit),
355
+ R.captureAs('word', escapedChars.word),
356
+ R.captureAs('nonWord', escapedChars.nonWord),
357
+ R.captureAs('wordBoundary', escapedChars.wordBoundary),
358
+ R.captureAs('nonWordBoundary', escapedChars.nonWordBoundary),
359
+ R.captureAs('formFeed', escapedChars.formFeed),
360
+ R.captureAs('carriageReturn', escapedChars.carriageReturn),
361
+ R.captureAs('lineFeed', escapedChars.lineFeed),
362
+ R.captureAs('tab', escapedChars.tab),
363
+ R.captureAs('verticalTab', escapedChars.verticalTab),
364
+ R.captureAs('backwardSlash', escapedChars.backwardSlash),
292
365
  ))
293
366
  }
294
367
 
368
+ //////////////////////////////////////////////////////////////////////////////////////////////
369
+ // Shared regular expressions
370
+ //////////////////////////////////////////////////////////////////////////////////////////////
295
371
  const digit = R.charRange('0', '9')
296
372
  const letter = R.anyOf(R.charRange('a', 'z'), R.charRange('A', 'Z'))
297
373
  const hexDigit = R.anyOf(digit, R.charRange('a', 'f'), R.charRange('A', 'F'))
298
374
  const identifierChar = R.anyOf(letter, digit, '_', '$')
299
375
 
300
- const escapedCharString = {
376
+ //////////////////////////////////////////////////////////////////////////////////////////////
377
+ // Escaped characters lookup
378
+ //////////////////////////////////////////////////////////////////////////////////////////////
379
+ const escapedChars = {
301
380
  whitespace: '\\s',
302
381
  nonWhitespace: '\\S',
303
382
  digit: '\\d',
@@ -313,3 +392,21 @@ const escapedCharString = {
313
392
  verticalTab: '\\v',
314
393
  backwardSlash: '\\\\'
315
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
+ ]
@@ -0,0 +1,10 @@
1
+ import * as G from '../../exports/Exports.js'
2
+
3
+ export class SimpleTestGrammar1 {
4
+ root = () => [
5
+ G.anyOf(
6
+ 'abc',
7
+ 'abcdefg',
8
+ )
9
+ ]
10
+ }
@@ -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
+ }