@sap/cds-compiler 5.9.4 → 6.0.12

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 (114) hide show
  1. package/CHANGELOG.md +117 -319
  2. package/README.md +1 -1
  3. package/bin/cds_update_identifiers.js +3 -5
  4. package/bin/cdsc.js +24 -9
  5. package/bin/cdshi.js +1 -1
  6. package/bin/cdsse.js +4 -4
  7. package/doc/CHANGELOG_BETA.md +11 -0
  8. package/doc/CHANGELOG_DEPRECATED.md +29 -0
  9. package/lib/api/main.js +8 -5
  10. package/lib/api/options.js +12 -10
  11. package/lib/base/builtins.js +1 -0
  12. package/lib/base/message-registry.js +191 -99
  13. package/lib/base/messages.js +35 -21
  14. package/lib/base/model.js +14 -24
  15. package/lib/checks/actionsFunctions.js +10 -20
  16. package/lib/checks/annotationsOData.js +1 -1
  17. package/lib/checks/elements.js +35 -10
  18. package/lib/checks/enums.js +31 -0
  19. package/lib/checks/foreignKeys.js +2 -2
  20. package/lib/checks/hasPersistedElements.js +5 -0
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedWithoutKeys.js +5 -4
  23. package/lib/checks/queryNoDbArtifacts.js +10 -8
  24. package/lib/checks/types.js +5 -5
  25. package/lib/checks/validator.js +6 -4
  26. package/lib/compiler/assert-consistency.js +13 -9
  27. package/lib/compiler/checks.js +20 -52
  28. package/lib/compiler/define.js +31 -6
  29. package/lib/compiler/extend.js +5 -1
  30. package/lib/compiler/generate.js +14 -17
  31. package/lib/compiler/populate.js +8 -31
  32. package/lib/compiler/propagator.js +21 -35
  33. package/lib/compiler/resolve.js +64 -29
  34. package/lib/compiler/shared.js +16 -4
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +1 -1
  37. package/lib/edm/annotations/edmJson.js +23 -20
  38. package/lib/edm/annotations/genericTranslation.js +12 -10
  39. package/lib/edm/csn2edm.js +50 -56
  40. package/lib/edm/edm.js +33 -28
  41. package/lib/edm/edmInboundChecks.js +2 -2
  42. package/lib/edm/edmPreprocessor.js +54 -88
  43. package/lib/edm/edmUtils.js +9 -12
  44. package/lib/gen/BaseParser.js +63 -52
  45. package/lib/gen/CdlGrammar.checksum +1 -1
  46. package/lib/gen/CdlParser.js +1153 -1165
  47. package/lib/gen/Dictionary.json +21 -1
  48. package/lib/json/from-csn.js +70 -43
  49. package/lib/json/to-csn.js +6 -8
  50. package/lib/language/multiLineStringParser.js +3 -2
  51. package/lib/main.d.ts +58 -24
  52. package/lib/model/cloneCsn.js +3 -0
  53. package/lib/model/csnUtils.js +28 -39
  54. package/lib/model/xprAsTree.js +23 -9
  55. package/lib/modelCompare/compare.js +5 -4
  56. package/lib/optionProcessor.js +24 -17
  57. package/lib/parsers/AstBuildingParser.js +81 -25
  58. package/lib/parsers/XprTree.js +57 -3
  59. package/lib/parsers/identifiers.js +1 -1
  60. package/lib/parsers/index.js +0 -3
  61. package/lib/render/manageConstraints.js +25 -25
  62. package/lib/render/toCdl.js +173 -170
  63. package/lib/render/toHdbcds.js +126 -128
  64. package/lib/render/toRename.js +7 -7
  65. package/lib/render/toSql.js +128 -125
  66. package/lib/render/utils/common.js +47 -22
  67. package/lib/render/utils/delta.js +25 -25
  68. package/lib/render/utils/operators.js +2 -2
  69. package/lib/render/utils/pretty.js +5 -5
  70. package/lib/render/utils/sql.js +13 -13
  71. package/lib/render/utils/standardDatabaseFunctions.js +115 -103
  72. package/lib/render/utils/unique.js +4 -4
  73. package/lib/transform/db/applyTransformations.js +1 -1
  74. package/lib/transform/db/assertUnique.js +2 -2
  75. package/lib/transform/db/associations.js +6 -7
  76. package/lib/transform/db/assocsToQueries/utils.js +4 -5
  77. package/lib/transform/db/backlinks.js +12 -9
  78. package/lib/transform/db/cdsPersistence.js +8 -7
  79. package/lib/transform/db/constraints.js +13 -10
  80. package/lib/transform/db/expansion.js +7 -3
  81. package/lib/transform/db/flattening.js +4 -14
  82. package/lib/transform/db/processSqlServices.js +2 -1
  83. package/lib/transform/db/temporal.js +5 -7
  84. package/lib/transform/db/views.js +2 -4
  85. package/lib/transform/draft/db.js +8 -8
  86. package/lib/transform/draft/odata.js +10 -7
  87. package/lib/transform/forOdata.js +10 -5
  88. package/lib/transform/forRelationalDB.js +5 -75
  89. package/lib/transform/localized.js +1 -1
  90. package/lib/transform/odata/createForeignKeys.js +11 -10
  91. package/lib/transform/odata/flattening.js +8 -4
  92. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
  93. package/lib/transform/odata/typesExposure.js +3 -3
  94. package/lib/transform/transformUtils.js +4 -8
  95. package/lib/transform/translateAssocsToJoins.js +14 -7
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
  97. package/lib/utils/objectUtils.js +0 -17
  98. package/package.json +10 -13
  99. package/share/messages/def-upcoming-virtual-change.md +1 -1
  100. package/LICENSE +0 -37
  101. package/bin/cds_remove_invalid_whitespace.js +0 -138
  102. package/doc/CHANGELOG_ARCHIVE.md +0 -3604
  103. package/lib/gen/genericAntlrParser.js +0 -3
  104. package/lib/gen/language.checksum +0 -1
  105. package/lib/gen/language.interp +0 -456
  106. package/lib/gen/language.tokens +0 -180
  107. package/lib/gen/languageLexer.interp +0 -439
  108. package/lib/gen/languageLexer.js +0 -1483
  109. package/lib/gen/languageLexer.tokens +0 -167
  110. package/lib/gen/languageParser.js +0 -24941
  111. package/lib/language/antlrParser.js +0 -205
  112. package/lib/language/errorStrategy.js +0 -646
  113. package/lib/language/genericAntlrParser.js +0 -1572
  114. package/lib/parsers/CdlGrammar.g4 +0 -2070
@@ -1,646 +0,0 @@
1
- // Error strategy with special handling for (non-reserved) keywords
2
-
3
- // If a language has non-reserved keywords, any such keyword can be used at
4
- // places where just a identifier is expected. For doing so, we define a rule
5
- // ident : Identifier | NONRESERVED_1 | ... NONRESERVED_n ;
6
- //
7
- // Now consider another rule:
8
- // expected : RESERVED_j | NONRESERVED_k | ident ;
9
- // If parsing fails at this place, you expect to see an message like
10
- // Mismatched input '?', expecting RESERVED_j, NONRESERVED_k, or Identifier
11
- // With ANTLR's default error strategy, you unfortunately also see all other
12
- // n-1 non-reserved keyword after "expecting"...
13
- //
14
- // The error strategy provided by this file gives you the expected message.
15
- // The example above shows that it is not enough to just remove all
16
- // non-reserved keywords from the expected-set. The error strategy also allows
17
- // you to match reserved keywords as identifiers at certain places (when there
18
- // are no alternatives).
19
-
20
- // For using this error strategy, the grammar for the parser/lexer must have a
21
- // lexer rule `Number`, then rules for unreserved keywords, and finally a rule
22
- // `Identifier`. No tokens (which are used in parser rules) must be defined
23
- // after that, no other rules must be defined in between those rules.
24
-
25
- // This file is actually very ANTLR4 specific and should be checked against
26
- // future versions of the ANTLR4-js runtime. There is no need to look at this
27
- // file if you just want to understand the rest of this compiler project.
28
-
29
- 'use strict';
30
-
31
- const antlr4 = require('antlr4');
32
- const Antlr4LL1Analyzer = require('antlr4/src/antlr4/LL1Analyzer');
33
- const { DefaultErrorStrategy } = require('antlr4/src/antlr4/error/ErrorStrategy');
34
- const { InputMismatchException } = require('antlr4/src/antlr4/error/Errors');
35
- const {
36
- predictionContextFromRuleContext: predictionContext,
37
- } = require('antlr4/src/antlr4/PredictionContext');
38
- const { ATNState } = require('antlr4/src/antlr4/atn/ATNState');
39
- const { IntervalSet, Interval } = require('antlr4/src/antlr4/IntervalSet');
40
- const { CompilerAssertion } = require('../base/error');
41
-
42
- const keywordRegexp = /^[a-zA-Z]+$/; // we don't have keywords with underscore
43
-
44
- let SEMI = null;
45
- let RBRACE = null;
46
-
47
- // Class which adapts ANTLR4s standard error strategy: do something special
48
- // with (non-reserved) keywords.
49
- //
50
- // An instance of this class should be set as property `_errHandler` to the
51
- // parser (prototype).
52
- class KeywordErrorStrategy extends DefaultErrorStrategy {
53
- constructor( ...args ) {
54
- super( ...args );
55
-
56
- this._super = {
57
- recoverInline: super.recoverInline,
58
- getExpectedTokens: super.getExpectedTokens,
59
- };
60
- }
61
- }
62
-
63
- // TODO: Use actual methods
64
- Object.assign( KeywordErrorStrategy.prototype, {
65
- sync,
66
- singleTokenDeletion,
67
- reportNoViableAlternative,
68
- reportInputMismatch,
69
- reportUnwantedToken,
70
- reportMissingToken,
71
- reportIgnoredWith,
72
- // getErrorRecoverySet,
73
- consumeUntil,
74
- consumeAndMarkUntil,
75
- recoverInline,
76
- getMissingSymbol,
77
- getExpectedTokensForMessage,
78
- getTokenDisplay,
79
- });
80
-
81
- // Attempt to recover from problems in subrules, except if rule has defined a
82
- // local variable `_sync` with value 'nop'
83
- // TODO: consider performance - see #8800
84
- // See DefaultErrorStrategy#sync
85
- function sync( recognizer ) {
86
- // If already recovering, don't try to sync
87
- if (this.inErrorRecoveryMode(recognizer))
88
- return;
89
-
90
- const token = recognizer.getCurrentToken();
91
- if (!token)
92
- return;
93
-
94
- const s = recognizer._interp.atn.states[recognizer.state];
95
- // try cheaper subset first; might get lucky. seems to shave a wee bit off
96
- const nextTokens = recognizer.atn.nextTokens(s);
97
- // console.log('SYNC:', recognizer._ctx._sync, s.stateType, token.text,
98
- // intervalSetToArray( recognizer, nextTokens ))
99
-
100
- if (nextTokens.contains(token.type)) { // we are sure the token matches
101
- if (token.text === '}' && recognizer.$nextTokensToken !== token &&
102
- nextTokens.contains(SEMI)) {
103
- // if the '}' could be matched alternative to ';', we had an opt ';' (rule requiredSemi)
104
- recognizer.$nextTokensToken = token;
105
- recognizer.$nextTokensState = recognizer.state;
106
- recognizer.$nextTokensContext = recognizer._ctx;
107
- }
108
- return;
109
- }
110
-
111
- if (nextTokens.contains(antlr4.Token.EPSILON)) {
112
- // when exiting a (innermost) rule, remember the state to make
113
- // getExpectedTokensForMessage() calculate the full "expected set"
114
- if (recognizer.$nextTokensToken !== token) {
115
- // console.log('SET:',token.type,recognizer.state,recognizer.$nextTokensToken &&
116
- // recognizer.$nextTokensToken.type)
117
- recognizer.$nextTokensToken = token;
118
- recognizer.$nextTokensState = recognizer.state;
119
- recognizer.$nextTokensContext = recognizer._ctx;
120
- }
121
- return;
122
- }
123
-
124
- // Expected token is identifier, current is (reserved) KEYWORD:
125
- // TODO: do not use this if "close enough" (1 char diff or prefix)
126
- // to a keyword in nextTokens
127
- //
128
- // NOTE: it is important to do this only if EPSILON is not in `nextTokens`,
129
- // which means that we cannot bring the better special syntax-unexpected-reserved
130
- // in all cases. Reason: high performance impact of the alternative,
131
- // i.e. calling method Parser#isExpectedToken() = invoking the ATN
132
- // interpreter to see behind EPSILON.
133
- const identType = recognizer.constructor.Identifier;
134
- if (keywordRegexp.test( token.text ) && nextTokens.contains( identType )) {
135
- recognizer.message( 'syntax-unexpected-reserved-word', token,
136
- { code: token.text, delimited: token.text } );
137
- // TODO: attach tokens like for 'syntax-unexpected-token'
138
- token.type = identType; // make next ANTLR decision assume identifier
139
- return;
140
- }
141
-
142
- if (recognizer._ctx._sync === 'nop')
143
- return;
144
- switch (s.stateType) {
145
- case ATNState.BLOCK_START: // 3
146
- case ATNState.STAR_BLOCK_START: // 5
147
- case ATNState.PLUS_BLOCK_START: // 4
148
- case ATNState.STAR_LOOP_ENTRY: // 10
149
- // report error and recover if possible
150
- if ( token.text !== '}' && // do not just delete a '}'
151
- this.singleTokenDeletion(recognizer) !== null) { // also calls reportUnwantedToken
152
- return;
153
- }
154
- else if (recognizer._ctx._sync === 'recover') {
155
- this.reportInputMismatch( recognizer, new InputMismatchException(recognizer) );
156
- this.consumeUntil( recognizer, nextTokens );
157
- return;
158
- }
159
- // TODO: at least with STAR_LOOP_ENTRY, we might want to do s/th similar as
160
- // with LOOP_BACK (syncing to “expected tokens” -> the separator)
161
- throw new InputMismatchException(recognizer);
162
-
163
- case ATNState.PLUS_LOOP_BACK: // 11
164
- case ATNState.STAR_LOOP_BACK: { // 9
165
- // TODO: do not delete a '}', ')', ',', ';'
166
- this.reportUnwantedToken(recognizer);
167
- const expecting = new IntervalSet();
168
- expecting.addSet(recognizer.getExpectedTokens());
169
-
170
- // First try some ',' insertion (TODO does not work yet):
171
- if (trySeparatorInsertion( recognizer, expecting, "','" ))
172
- return;
173
-
174
- // We then try syncing only to the loop-cont (`,`) / loop-end (`}`) token set,
175
- // but only for the current or next line (and not consuming `;`s):
176
- const prevToken = recognizer.getTokenStream().LT(-1);
177
- if (token.line <= prevToken.line + 1 && // in same or next line
178
- this.consumeAndMarkUntil( recognizer, expecting, true ))
179
- break;
180
- // console.log(token.text,JSON.stringify(intervalSetToArray(recognizer,expecting)))
181
-
182
- // If that fails, we also sync to all tokens which are in the follow set of
183
- // the current rule and all outer rules
184
- const whatFollowsLoopIterationOrRule = expecting.addSet(this.getErrorRecoverySet(recognizer));
185
- this.consumeUntil(recognizer, whatFollowsLoopIterationOrRule);
186
- // console.log(JSON.stringify(intervalSetToArray(recognizer,expecting)))
187
- if (recognizer._ctx._sync === 'recover' || // in start rule: no exception
188
- nextTokens.contains( recognizer.getTokenStream().LA(1) ))
189
- return;
190
- throw new InputMismatchException(recognizer);
191
- }
192
- default:
193
- // do nothing if we can't identify the exact kind of ATN state
194
- }
195
- }
196
-
197
-
198
- function trySeparatorInsertion( recognizer, expecting, separatorName ) {
199
- // Remark: this function does not really work, because it is based on
200
- // singleTokenInsertion, which also does not really work… (see below).
201
- // But we might improve it in the future…
202
- const separator = recognizer.literalNames.indexOf( separatorName );
203
- if (!expecting.contains( separator ))
204
- return false;
205
-
206
- const currentSymbolType = recognizer.getTokenStream().LA(1);
207
- // if current token is consistent with what could come after current
208
- // ATN state, then we know we're missing a token; error recovery
209
- // is free to conjure up and insert the missing token
210
- const { atn } = recognizer._interp;
211
- const currentState = atn.states[recognizer.state];
212
- const next = separatorTransition( currentState.transitions, separator ).target;
213
- // While this is an improvement to the default ANTLR code for
214
- // singleTokenInsertion(), it still does not help, as we navigate along an
215
- // epsilon transition, i.e. we still see ',', etc
216
- const expectingAtLL2 = atn.nextTokens(next, recognizer._ctx);
217
- if (!expectingAtLL2.contains(currentSymbolType))
218
- return false;
219
-
220
- this.reportMissingToken(recognizer);
221
- return getMissingSymbol( recognizer, separator );
222
- }
223
-
224
- function separatorTransition( transitions, separator ) {
225
- for (const tr of transitions) {
226
- if (tr.matches( separator ))
227
- return tr;
228
- }
229
- return transitions[0];
230
- }
231
-
232
- function singleTokenDeletion( recognizer ) {
233
- const token = recognizer.getCurrentToken();
234
- if (!token || token.text === '}')
235
- return null;
236
-
237
- const nextTokenType = recognizer.getTokenStream().LA(2);
238
- const { Number: num } = recognizer.constructor;
239
- if (nextTokenType > num && // next token is Id|Unreserved|IllegalToken
240
- token.type <= num) // current token is not
241
- return null;
242
-
243
- const expecting = this.getExpectedTokens(recognizer);
244
- if (!expecting.contains(nextTokenType))
245
- return null;
246
-
247
- this.reportUnwantedToken(recognizer);
248
- recognizer.consume(); // simply delete extra token
249
- // we want to return the token we're actually matching
250
- const matchedSymbol = recognizer.getCurrentToken();
251
- this.reportMatch( recognizer ); // we know current token is correct
252
- return matchedSymbol;
253
- }
254
-
255
-
256
- // singleTokenInsertion called by recoverInline (called by match / in else),
257
- // calls reportMissingToken
258
-
259
- // Report `NoViableAltException e` signalled by parser `recognizer`
260
- function reportNoViableAlternative( recognizer, e ) {
261
- // console.log('NOV:',this.getTokenErrorDisplay(e.startToken),
262
- // this.getTokenErrorDisplay(e.offendingToken))
263
- if (e.startToken === e.offendingToken) { // mismatch at LA(1)
264
- this.reportInputMismatch( recognizer, e );
265
- }
266
- else {
267
- this.reportInputMismatch( recognizer, e, !e.deadEndConfigs || e.deadEndConfigs.configs );
268
- do {
269
- // console.log('CONSUME-NOVIA:',this.getTokenErrorDisplay(recognizer.getCurrentToken()));
270
- recognizer.consume();
271
- } while (recognizer.getCurrentToken() !== e.offendingToken);
272
- // this.lastErrorIndex = e.startToken.tokenIndex; // avoid another consume()
273
- }
274
- }
275
-
276
- // Report `InputMismatchException e` signalled by parser `recognizer``
277
- function reportInputMismatch( recognizer, e, deadEnds ) {
278
- const expecting = deadEnds !== true && // true: cannot compute expecting
279
- this.getExpectedTokensForMessage( recognizer, e.offendingToken, deadEnds );
280
- const offending = this.getTokenDisplay( e.offendingToken, recognizer );
281
- e.offendingToken.$isSkipped = 'offending';
282
- let err;
283
- if (expecting && expecting.length) {
284
- err = recognizer.error( 'syntax-unexpected-token', e.offendingToken,
285
- { offending, expecting } );
286
- err.expectedTokens = expecting;
287
- }
288
- else { // should not really happen anymore... -> no messageId !
289
- err = recognizer.error( null, e.offendingToken, { offending },
290
- 'Mismatched $(OFFENDING)' );
291
- }
292
- if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
293
- recognizer.notifyErrorListeners( err.message, e.offendingToken, err );
294
- }
295
-
296
- // Report unwanted token when the parser `recognizer` tries to recover/sync
297
- function reportUnwantedToken( recognizer, expecting ) {
298
- if (this.inErrorRecoveryMode(recognizer))
299
- return;
300
- this.beginErrorCondition(recognizer);
301
-
302
- const token = recognizer.getCurrentToken();
303
- token.$isSkipped = 'offending';
304
- expecting ??= this.getExpectedTokensForMessage( recognizer, token );
305
- const offending = this.getTokenDisplay( token, recognizer );
306
- // Just text variant, no other message id! Would depend on ANTLR-internals
307
- const err = recognizer.error( 'syntax-unexpected-token', token,
308
- { '#': 'unwanted', offending, expecting } );
309
- err.expectedTokens = expecting; // TODO: remove next token?
310
- if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
311
- recognizer.notifyErrorListeners( err.message, token, err );
312
- }
313
-
314
- // Report missing token when the parser `recognizer` tries to recover/sync
315
- function reportMissingToken( recognizer ) {
316
- if ( this.inErrorRecoveryMode(recognizer))
317
- return;
318
- this.beginErrorCondition(recognizer);
319
-
320
- const token = recognizer.getCurrentToken();
321
- token.$isSkipped = 'offending';
322
- const expecting = this.getExpectedTokensForMessage( recognizer, token );
323
- const offending = this.getTokenDisplay( token, recognizer );
324
- // TODO: if non-reserved keyword will not been parsed as keyword, use Identifier for offending
325
- // Hopefully not too ANTLR-specific, so extra message id is ok:
326
- const err = recognizer.error( 'syntax-missing-token', token,
327
- { offending, expecting },
328
- 'Missing $(EXPECTING) before $(OFFENDING)' );
329
- err.expectedTokens = expecting;
330
- if (!recognizer.avoidErrorListeners) // with --trace-parser or --trace-parser-ambig
331
- recognizer.notifyErrorListeners( err.message, token, err );
332
- }
333
-
334
- function reportIgnoredWith( recognizer, t ) {
335
- const next = recognizer._interp.atn.states[recognizer.state].transitions[0].target;
336
- recognizer.state = next.stateNumber; // previous match() does not set the state
337
- const expecting = this.getExpectedTokensForMessage( recognizer, t );
338
- const m = recognizer.warning( 'syntax-unexpected-semicolon', t,
339
- { offending: "';'", expecting, keyword: 'with' },
340
- // eslint-disable-next-line @stylistic/js/max-len
341
- 'Unexpected $(OFFENDING), expecting $(EXPECTING) - ignored previous $(KEYWORD)' );
342
- m.expectedTokens = expecting;
343
- }
344
-
345
- function consumeUntil( recognizer, set ) {
346
- // TODO: add trace
347
- if (SEMI == null)
348
- SEMI = recognizer.literalNames.indexOf( "';'" );
349
- if (RBRACE == null)
350
- RBRACE = recognizer.literalNames.indexOf( "'}'" );
351
-
352
- // let s=this.getTokenDisplay( recognizer.getCurrentToken(), recognizer );
353
- if (SEMI < 1 || RBRACE < 1) {
354
- this.consumeAndMarkUntil( recognizer, set );
355
- }
356
- else if (set.contains(SEMI)) { // do not check for RBRACE here!
357
- this.consumeAndMarkUntil( recognizer, set );
358
- // console.log('CONSUMED-ORIG:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
359
- // recognizer ),recognizer.getCurrentToken().line,intervalSetToArray( recognizer, set ));
360
- }
361
- else {
362
- // DO NOT modify input param `set`, as the set might be cached in the ATN
363
- const stop = new IntervalSet();
364
- stop.addSet( set );
365
- stop.removeOne( recognizer.constructor.Identifier );
366
- stop.addOne( SEMI );
367
- // I am not that sure whether to add RBRACE...
368
- stop.addOne( RBRACE );
369
- this.consumeAndMarkUntil( recognizer, stop );
370
- const ttype = recognizer.getTokenStream().LA(1);
371
- if (ttype === SEMI || ttype === RBRACE && !set.contains(RBRACE)) {
372
- recognizer.consume();
373
- this.reportMatch(recognizer); // we know current token is correct
374
- }
375
- // if matched '}', also try to match next ';' (also matches double ';')
376
- if (recognizer.getTokenStream().LA(1) === SEMI) {
377
- recognizer.consume();
378
- this.reportMatch(recognizer); // we know current token is correct
379
- }
380
- // console.log('CONSUMED:',s,this.getTokenDisplay( recognizer.getCurrentToken(),
381
- // recognizer ),recognizer.getCurrentToken().line);
382
- // throw new CompilerAssertion('Sync')
383
- }
384
- }
385
-
386
- function consumeAndMarkUntil( recognizer, set, onlyInSameLine ) {
387
- const stream = recognizer.getTokenStream();
388
- let t = stream.LT(1);
389
- const { line } = t;
390
- while (t.type !== antlr4.Token.EOF && !set.contains( t.type )) {
391
- if (onlyInSameLine && (t.line !== line || t.text === ';' || t.text === '}' ))
392
- return false; // early exit
393
- if (!t.$isSkipped)
394
- t.$isSkipped = true;
395
- recognizer.consume();
396
- t = stream.LT(1);
397
- }
398
- return true;
399
- }
400
-
401
- // As the `match` function of the parser `recognizer` does not allow to check
402
- // against a set of token types, the generated parser code checks against that
403
- // set itself and calls this function if not successful.
404
- // We now also allow keywords if the Identifier is expected.
405
- // Called by match() and in generated parser in "else part" before consume()
406
- // for ( TOKEN1 | TOKEN2 )
407
- function recoverInline( recognizer ) {
408
- const identType = recognizer.constructor.Identifier;
409
- if (!identType || !recognizer.isExpectedToken( identType ))
410
- return this._super.recoverInline.call( this, recognizer );
411
-
412
- const token = recognizer.getCurrentToken();
413
- // TODO: do not delete `)`, `}`,
414
-
415
- // TODO: overwrite singleTokenDeletion do not delete parens etc for identifier
416
- // or non-reserved keywords
417
- if (!keywordRegexp.test( token.text ))
418
- return this._super.recoverInline.call( this, recognizer );
419
-
420
- // TODO: attach `Identifier` as valid name to message?
421
- recognizer.message( 'syntax-unexpected-reserved-word', token,
422
- { code: token.text, delimited: token.text } );
423
- this.reportMatch(recognizer); // we know current token is correct
424
- recognizer.consume();
425
- return token;
426
- }
427
-
428
- // Conjure up a missing token during error recovery in parser `recognizer`. If
429
- // an identifier is expected, create one.
430
- // Think about: we might want to prefer one of '}]);,'.
431
- function getMissingSymbol( recognizer, expectedTokenType ) {
432
- expectedTokenType ??= this.getExpectedTokens(recognizer).first(); // get any element
433
- const current = recognizer.getCurrentToken();
434
- return recognizer.getTokenFactory().create(
435
- current.source, // do s/th special if EOF like in DefaultErrorStrategy ?
436
- expectedTokenType, '', antlr4.Token.DEFAULT_CHANNEL, // empty string as token text
437
- -1, -1, current.line, current.column
438
- );
439
- }
440
-
441
- function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
442
- // similar to `IntervalSet#toTokenString`
443
- let names = [];
444
- const pc = recognizer.constructor;
445
- for (const v of expected.intervals) {
446
- for (let j = v.start; j < v.stop; j++) {
447
- // a generic keyword as such does not appear in messages, only its replacements,
448
- // which are function name and argument position dependent:
449
- if (j === pc.GenericExpr) {
450
- names.push( ...recognizer.$genericKeywords.expr );
451
- }
452
- else if (j === pc.GenericSeparator) {
453
- names.push( ...recognizer.$genericKeywords.separator );
454
- }
455
- else if (j === pc.GenericIntro) {
456
- names.push( ...recognizer.$genericKeywords.introMsg );
457
- }
458
- else if (j === pc.SemicolonTopLevel) {
459
- // We only insert a semikolon (i.e. make it optional) after a closing brace.
460
- // If the previous token is not `}`, don't propose these keywords, as ';' is required.
461
- if (recognizer._input.LA(-1) === recognizer._input.BRACE_CLOSE) {
462
- const name = recognizer.topLevelKeywords.map(i => expected
463
- .elementName(recognizer.literalNames, recognizer.symbolicNames, i));
464
- names.push(...name);
465
- if (recognizer._ctx.outer?.kind !== 'source') {
466
- if (names.includes('<EOF>'))
467
- names.splice(names.indexOf('<EOF>'), 1);
468
- }
469
- }
470
- }
471
- // other expected tokens usually appear in messages, except the helper tokens
472
- // which are used to solve ambiguities via the parser method setLocalToken():
473
- else if (j !== pc.HelperToken1 && j !== pc.HelperToken2) {
474
- names.push( expected.elementName(recognizer.literalNames, recognizer.symbolicNames, j ) );
475
- }
476
- }
477
- }
478
- // The parser method excludeExpected() additionally removes some tokens from the message:
479
- if (recognizer.$adaptExpectedToken &&
480
- recognizer.$nextTokensToken === recognizer.$adaptExpectedToken) {
481
- const excludes = (excludesForNextToken && Array.isArray(recognizer.$adaptExpectedExcludes[0]))
482
- ? recognizer.$adaptExpectedExcludes[0]
483
- : recognizer.$adaptExpectedExcludes;
484
- names = names.filter( n => !excludes.includes( n ) );
485
- }
486
- else if (names.includes("';'")) {
487
- names = names.filter( n => n !== "'}'" );
488
- }
489
- else if (names.includes("'?'")) {
490
- names = names.filter( n => n !== "'?'" );
491
- }
492
- names.sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
493
- return names;
494
- }
495
-
496
- // Used for sorting in messages
497
- const token1sort = {
498
- // 0: Identifier, Number, ...
499
- // 1: separators:
500
- ',': 1,
501
- '.': 1,
502
- ':': 1,
503
- ';': 1,
504
- // 2: parentheses:
505
- '(': 2,
506
- ')': 2,
507
- '[': 2,
508
- ']': 2,
509
- '{': 2,
510
- '}': 2,
511
- // 3: special:
512
- '!': 3,
513
- '#': 3,
514
- $: 3,
515
- '?': 3,
516
- '@': 3,
517
- // 4: operators:
518
- '*': 4,
519
- '+': 4,
520
- '-': 4,
521
- '/': 4,
522
- '<': 4,
523
- '=': 4,
524
- '>': 4,
525
- '|': 4,
526
- // 8: KEYWORD
527
- // 9: <EOF>
528
- };
529
-
530
- function tokenPrecedence( name ) {
531
- if (name.length < 2 || name === '<EOF>')
532
- return `9${ name }`;
533
- const prec = token1sort[name.charAt(1)];
534
- if (prec)
535
- return `${ prec }${ name }`;
536
- return (name.charAt(1) < 'a' ? '8' : '0') + name;
537
- }
538
-
539
- function getTokenDisplay( token, recognizer ) {
540
- if (!token)
541
- return '<EOF>';
542
- const t = token.type;
543
- if (t === antlr4.Token.EOF || t === antlr4.Token.EPSILON ) {
544
- return '<EOF>';
545
- }
546
- else if (t === recognizer.constructor.DOTbeforeBRACE) {
547
- if (recognizer.getTokenStream().LT(2).text === '{')
548
- return "'.{'";
549
- return "'.*'";
550
- }
551
- return recognizer.literalNames[t] || recognizer.symbolicNames[t];
552
- }
553
-
554
- // Return an IntervalSet of token types which the parser had expected. Do not
555
- // include non-reserved keywords if not mentioned explicitly (i.e. other than
556
- // from rule `ident`).
557
- //
558
- // We actually define something like a corrected version of function
559
- // `LL1Analyzer.prototype.getDecisionLookahead`. We cannot just redefine
560
- // `getExpectedTokens`, because that function is also used to decide whether
561
- // to consume in `DefaultErrorStrategy#singleTokenDeletion`.
562
- function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
563
- const { atn } = recognizer._interp;
564
- if (recognizer.state < 0)
565
- return [];
566
- if (recognizer.state >= atn.states.length) {
567
- throw new CompilerAssertion( `Invalid state number ${ recognizer.state } for ${
568
- this.getTokenErrorDisplay( offendingToken ) }`);
569
- }
570
-
571
- const identType = recognizer.constructor.Identifier;
572
- const hideAltsType = recognizer.constructor.HideAlternatives;
573
- const beforeUnreserved = recognizer.constructor.Number;
574
- if (!identType || !beforeUnreserved || beforeUnreserved + 2 > identType)
575
- return intervalSetToArray( recognizer, this._super.getExpectedTokens.call( this, recognizer ) );
576
-
577
- const ll1 = new Antlr4LL1Analyzer(atn);
578
- const expected = new IntervalSet();
579
- const origAddInterval = expected.addInterval;
580
- const origAddSet = expected.addSet;
581
- expected.addInterval = addInterval;
582
- expected.addSet = addSet;
583
- const lookBusy = new antlr4.Utils.Set();
584
- const calledRules = new antlr4.Utils.BitSet();
585
-
586
- if (deadEnds) {
587
- // "No viable alternative" by adaptivePredict() not on first token
588
- for (const trans of deadEnds) {
589
- ll1._LOOK( trans.state, null, predictionContext( atn, recognizer._ctx ),
590
- expected, lookBusy, calledRules, true, true );
591
- }
592
- return intervalSetToArray( recognizer, expected, true );
593
- }
594
- else if (offendingToken && recognizer.$nextTokensContext &&
595
- offendingToken === recognizer.$nextTokensToken) {
596
- // Before exiting a rule, we had a state (via sync()) with a bigger
597
- // "expecting set" for the same token
598
- ll1._LOOK( atn.states[recognizer.$nextTokensState], null,
599
- predictionContext( atn, recognizer.$nextTokensContext ),
600
- expected, lookBusy, calledRules, true, true );
601
- }
602
- else {
603
- // Use current state to compute "expecting"
604
- ll1._LOOK( atn.states[recognizer.state], null,
605
- predictionContext( atn, recognizer._ctx ),
606
- expected, lookBusy, calledRules, true, true );
607
- }
608
- // console.log(state, recognizer.$nextTokensState,
609
- // expected.toString(recognizer.literalNames, recognizer.symbolicNames));
610
- return intervalSetToArray( recognizer, expected );
611
-
612
- function addSet( other ) {
613
- if (!other.contains( hideAltsType ))
614
- origAddSet.call( this, other );
615
- }
616
-
617
- // Add an interval `v` to the IntervalSet `this`. If `v` contains the token
618
- // type `Identifier`, do not add non-reserved keywords in `v`.
619
- function addInterval( v ) {
620
- if (v.stop <= identType) {
621
- origAddInterval.call(this, v);
622
- }
623
- else if (v.start >= identType) {
624
- if (v.stop === identType + 1 || !recognizer.tokenRewrite) {
625
- origAddInterval.call(this, v);
626
- }
627
- else {
628
- for (let j = v.start; j < v.stop; j++)
629
- addRange( this, recognizer.tokenRewrite[j - identType] || j );
630
- }
631
- }
632
- else {
633
- if (v.start <= beforeUnreserved)
634
- addRange( this, v.start, beforeUnreserved + 1 );
635
- addRange( this, identType );
636
- }
637
- }
638
-
639
- function addRange( interval, start, stop ) {
640
- origAddInterval.call( interval, new Interval( start, stop || start + 1 ) );
641
- }
642
- }
643
-
644
- module.exports = {
645
- KeywordErrorStrategy,
646
- };