@sap/cds-compiler 2.15.2 → 3.0.2

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 (111) hide show
  1. package/CHANGELOG.md +66 -1590
  2. package/bin/cdsc.js +42 -46
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +312 -143
  10. package/lib/api/options.js +15 -85
  11. package/lib/api/validate.js +6 -10
  12. package/lib/base/keywords.js +280 -110
  13. package/lib/base/message-registry.js +80 -24
  14. package/lib/base/messages.js +103 -52
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +53 -21
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +1 -1
  19. package/lib/checks/cdsPersistence.js +1 -0
  20. package/lib/checks/elements.js +6 -6
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/nonexpandableStructured.js +1 -1
  23. package/lib/checks/queryNoDbArtifacts.js +2 -1
  24. package/lib/checks/selectItems.js +5 -1
  25. package/lib/checks/types.js +4 -2
  26. package/lib/checks/utils.js +2 -2
  27. package/lib/checks/validator.js +2 -1
  28. package/lib/compiler/assert-consistency.js +15 -10
  29. package/lib/compiler/builtins.js +127 -10
  30. package/lib/compiler/define.js +6 -4
  31. package/lib/compiler/extend.js +63 -12
  32. package/lib/compiler/finalize-parse-cdl.js +20 -9
  33. package/lib/compiler/index.js +25 -11
  34. package/lib/compiler/moduleLayers.js +7 -0
  35. package/lib/compiler/populate.js +16 -14
  36. package/lib/compiler/propagator.js +3 -3
  37. package/lib/compiler/resolve.js +194 -222
  38. package/lib/compiler/shared.js +56 -76
  39. package/lib/compiler/tweak-assocs.js +9 -10
  40. package/lib/compiler/utils.js +7 -2
  41. package/lib/edm/annotations/genericTranslation.js +60 -6
  42. package/lib/edm/annotations/preprocessAnnotations.js +10 -11
  43. package/lib/edm/csn2edm.js +39 -41
  44. package/lib/edm/edm.js +22 -15
  45. package/lib/edm/edmPreprocessor.js +66 -69
  46. package/lib/edm/edmUtils.js +12 -62
  47. package/lib/gen/Dictionary.json +8 -6
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +8 -30
  50. package/lib/gen/language.tokens +105 -114
  51. package/lib/gen/languageLexer.interp +1 -34
  52. package/lib/gen/languageLexer.js +889 -1007
  53. package/lib/gen/languageLexer.tokens +95 -106
  54. package/lib/gen/languageParser.js +20717 -22376
  55. package/lib/json/from-csn.js +73 -68
  56. package/lib/json/to-csn.js +13 -10
  57. package/lib/language/antlrParser.js +2 -2
  58. package/lib/language/docCommentParser.js +61 -38
  59. package/lib/language/errorStrategy.js +52 -40
  60. package/lib/language/genericAntlrParser.js +333 -259
  61. package/lib/language/language.g4 +600 -645
  62. package/lib/language/multiLineStringParser.js +14 -42
  63. package/lib/language/textUtils.js +44 -0
  64. package/lib/main.d.ts +27 -42
  65. package/lib/main.js +104 -81
  66. package/lib/model/csnRefs.js +2 -1
  67. package/lib/model/csnUtils.js +183 -285
  68. package/lib/model/revealInternalProperties.js +32 -9
  69. package/lib/model/sortViews.js +32 -31
  70. package/lib/optionProcessor.js +64 -57
  71. package/lib/render/.eslintrc.json +1 -1
  72. package/lib/render/DuplicateChecker.js +4 -7
  73. package/lib/render/manageConstraints.js +70 -2
  74. package/lib/render/toCdl.js +334 -339
  75. package/lib/render/toHdbcds.js +20 -16
  76. package/lib/render/toRename.js +44 -22
  77. package/lib/render/toSql.js +60 -54
  78. package/lib/render/utils/common.js +15 -1
  79. package/lib/render/utils/sql.js +20 -19
  80. package/lib/sql-identifier.js +6 -0
  81. package/lib/transform/db/.eslintrc.json +3 -2
  82. package/lib/transform/db/cdsPersistence.js +5 -15
  83. package/lib/transform/db/constraints.js +1 -1
  84. package/lib/transform/db/expansion.js +7 -6
  85. package/lib/transform/db/flattening.js +18 -19
  86. package/lib/transform/db/views.js +3 -3
  87. package/lib/transform/draft/.eslintrc.json +2 -2
  88. package/lib/transform/draft/db.js +6 -6
  89. package/lib/transform/draft/odata.js +6 -7
  90. package/lib/transform/forHanaNew.js +19 -22
  91. package/lib/transform/forOdataNew.js +13 -15
  92. package/lib/transform/localized.js +35 -25
  93. package/lib/transform/odata/toFinalBaseType.js +11 -9
  94. package/lib/transform/odata/typesExposure.js +3 -3
  95. package/lib/transform/odata/utils.js +1 -38
  96. package/lib/transform/transformUtilsNew.js +63 -77
  97. package/lib/transform/translateAssocsToJoins.js +6 -2
  98. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  99. package/lib/transform/universalCsn/coreComputed.js +11 -6
  100. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  101. package/lib/utils/file.js +31 -21
  102. package/lib/utils/timetrace.js +20 -21
  103. package/package.json +34 -4
  104. package/share/messages/syntax-expected-integer.md +9 -8
  105. package/doc/ApiMigration.md +0 -237
  106. package/doc/CommandLineMigration.md +0 -58
  107. package/doc/ErrorMessages.md +0 -175
  108. package/doc/FioriAnnotations.md +0 -94
  109. package/doc/ODataTransformation.md +0 -273
  110. package/lib/backends.js +0 -529
  111. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -1,6 +1,10 @@
1
1
  'use strict';
2
2
 
3
- const { splitLines } = require('../utils/file');
3
+ const {
4
+ isWhitespaceOrNewLineOnly,
5
+ isWhitespaceCharacterNoNewline,
6
+ cdlNewLineRegEx,
7
+ } = require('./textUtils');
4
8
 
5
9
  /**
6
10
  * Get the content of a JSDoc-like comment and remove all surrounding asterisks, etc.
@@ -18,39 +22,40 @@ function parseDocComment(comment) {
18
22
  if (comment.length <= 5) // at least "/***/"
19
23
  return null;
20
24
 
21
- let lines = splitLines(comment);
25
+ let lines = comment.split(cdlNewLineRegEx);
22
26
 
23
27
  if (lines.length === 1) {
24
- // special case for one-liners
25
- // remove "/***/" and trim white space
26
- const content = lines[0].replace(/^\/[*]{2,}/, '').replace(/\*\/$/, '').trim();
27
- return isWhiteSpaceOnly(content) ? null : content;
28
+ // Special case for one-liners.
29
+ // Remove "/***/" and trim white space and asterisks.
30
+ const content = lines[0]
31
+ .replace(/^\/[*]{2,}/, '')
32
+ .replace(/\*+\/$/, '')
33
+ .trim();
34
+ return isWhitespaceOrNewLineOnly(content) ? null : content;
28
35
  }
29
36
 
30
- lines[0] = removeHeaderFence(lines[0]);
37
+ // If the comment already has content on the first line, i.e. after `/**`,
38
+ // its leading whitespace is ignored for whitespace trimming.
39
+ const hasContentOnFirstLine = /\/\*+\s*\S/.test(lines[0]);
40
+
41
+ // First line, i.e. header, is always trimmed from left.
42
+ lines[0] = removeHeaderFence(lines[0]).trimStart();
31
43
  lines[lines.length - 1] = removeFooterFence(lines[lines.length - 1]);
32
44
 
33
- if (isFencedComment(lines)) {
34
- lines = lines.map((line, index) => ((index === 0) ? line : removeFence(line)));
35
- }
36
- else if (lines.length === 2) {
45
+ if (lines.length === 2) {
37
46
  // Comment that is essentially just a header + footer.
38
- // First line, i.e. header, is always trimmed from left.
39
- lines[0] = lines[0].trimStart();
40
-
41
47
  // If the second line starts with an asterisk then remove it.
42
- // Otherwise trim all whitespace.
48
+ // Otherwise, trim all left whitespace.
43
49
  if ((/^\s*[*]/.test(lines[1])))
44
50
  lines[1] = removeFence(lines[1]);
45
51
  else
46
52
  lines[1] = lines[1].trimStart();
47
53
  }
54
+ else if (isFencedComment(lines)) {
55
+ lines = lines.map((line, index) => ((index === 0) ? line : removeFence(line)));
56
+ }
48
57
  else {
49
- const firstNonEmptyLine = lines.find((line, index) => index !== 0 && /[^\s]/.test(line)) || '';
50
- // Tabs are regarded as one space.
51
- const spacesAtBeginning = firstNonEmptyLine.match(/^\s*/)[0].length;
52
- if (spacesAtBeginning > 0)
53
- lines = lines.map(line => removeWhitespace(line, spacesAtBeginning));
58
+ stripCommentIndentation(lines, hasContentOnFirstLine);
54
59
  }
55
60
 
56
61
  // Remove empty header and footer.
@@ -58,18 +63,47 @@ function parseDocComment(comment) {
58
63
  const endIndex = (lines[lines.length - 1] === '') ? lines.length - 1 : lines.length;
59
64
 
60
65
  const content = lines.slice(startIndex, endIndex).join('\n');
61
-
62
- return isWhiteSpaceOnly(content) ? null : content;
66
+ return isWhitespaceOrNewLineOnly(content) ? null : content;
63
67
  }
64
68
 
65
69
  /**
66
- * Checks whether the given string is whitespace only, i.e. newline
67
- * spaces, tabs.
70
+ * Strips and counts the indentation from the given comment string.
71
+ * This function is similar to the one in multiLineStringParser.js, but does not
72
+ * have special handling for the first and last line of the string.
73
+ *
74
+ * @example
75
+ * | hello
76
+ * | world
77
+ * | foo bar
78
+ * becomes
79
+ * | hello
80
+ * | world
81
+ * | foo bar
68
82
  *
69
- * @param {string} content
83
+ * @param {string[]} lines String split into lines.
84
+ * @param {boolean} ignoreFirstLine Whether to ignore the first line for indentation counting.
70
85
  */
71
- function isWhiteSpaceOnly(content) {
72
- return /^\s*$/.test(content);
86
+ function stripCommentIndentation(lines, ignoreFirstLine) {
87
+ const n = lines.length;
88
+
89
+ const minIndent = lines.reduce((min, line, index) => {
90
+ // Blank lines are ignored.
91
+ if (isWhitespaceOrNewLineOnly(line) || (index === 0 && ignoreFirstLine))
92
+ return min;
93
+
94
+ let count = 0;
95
+ const length = Math.min(min, line.length);
96
+ while (count < length && isWhitespaceCharacterNoNewline(line[count])) {
97
+ count++;
98
+ }
99
+ return Math.min(min, count);
100
+ }, Number.MAX_SAFE_INTEGER);
101
+
102
+ for (let i = (ignoreFirstLine ? 1 : 0); i < n; ++i) {
103
+ // Note: Line may be empty and have fewer characters than `min`.
104
+ // In that case, slice() returns an empty string.
105
+ lines[i] = lines[i].slice(minIndent);
106
+ }
73
107
  }
74
108
 
75
109
  /**
@@ -85,17 +119,6 @@ function removeFence(line) {
85
119
  return line.replace(/^\s*[*]\s?/, '');
86
120
  }
87
121
 
88
- /**
89
- * Remove the TODO
90
- *
91
- * @param {string} line
92
- * @param {number} spaces Number of whitespace to remove at the beginning of the line
93
- * @returns {string} line without fence
94
- */
95
- function removeWhitespace(line, spaces) {
96
- return line.replace(new RegExp(`^\\s{0,${ spaces }}`), ''); // Trailing spaces with '*'? => .replace(/\s+[*]$/, '');
97
- }
98
-
99
122
  /**
100
123
  * Removes a header fence, i.e. '/**'.
101
124
  * May remove more than two asterisks e.g. '/*******'
@@ -29,12 +29,12 @@
29
29
  'use strict';
30
30
 
31
31
  const antlr4 = require('antlr4');
32
- const IntervalSet = require('antlr4/IntervalSet');
33
- const antlr4_error = require('antlr4/error/ErrorStrategy');
34
- const antlr4_LL1Analyzer = require('antlr4/LL1Analyzer.js').LL1Analyzer;
35
- const predictionContext = require('antlr4/PredictionContext').predictionContextFromRuleContext;
36
- const { ATNState } = require('antlr4/atn/ATNState');
37
- const { InputMismatchException } = antlr4.error;
32
+ const antlr4_LL1Analyzer = 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 { predictionContextFromRuleContext: predictionContext } = require('antlr4/src/antlr4/PredictionContext');
36
+ const { ATNState } = require('antlr4/src/antlr4/atn/ATNState');
37
+ const { IntervalSet, Interval } = require('antlr4/src/antlr4/IntervalSet');
38
38
 
39
39
  const keywordRegexp = /^[a-zA-Z]+$/; // we don't have keywords with underscore
40
40
 
@@ -65,30 +65,35 @@ function match( ttype ) {
65
65
  //
66
66
  // An instance of this class should be set as property `_errHandler` to the
67
67
  // parser (prototype).
68
- function KeywordErrorStrategy( ...args ) {
69
- antlr4_error.DefaultErrorStrategy.call( this, ...args );
70
- }
71
- const super1 = antlr4_error.DefaultErrorStrategy.prototype;
72
-
73
- KeywordErrorStrategy.prototype = Object.assign(
74
- Object.create( super1 ), {
75
- sync,
76
- reportNoViableAlternative,
77
- reportInputMismatch,
78
- reportUnwantedToken,
79
- reportMissingToken,
80
- reportIgnoredWith,
81
- // getErrorRecoverySet,
82
- consumeUntil,
83
- recoverInline,
84
- getMissingSymbol,
85
- getExpectedTokensForMessage,
86
- getTokenDisplay,
87
- constructor: KeywordErrorStrategy,
68
+ class KeywordErrorStrategy extends DefaultErrorStrategy {
69
+ constructor( ...args ) {
70
+ super( ...args );
71
+
72
+ this._super = {
73
+ consumeUntil: super.consumeUntil,
74
+ recoverInline: super.recoverInline,
75
+ getExpectedTokens: super.getExpectedTokens,
76
+ };
88
77
  }
89
- );
78
+ }
90
79
 
91
- // Attemp to recover from problems in subrules, except if rule has defined a
80
+ // TODO: Use actual methods
81
+ Object.assign( KeywordErrorStrategy.prototype, {
82
+ sync,
83
+ reportNoViableAlternative,
84
+ reportInputMismatch,
85
+ reportUnwantedToken,
86
+ reportMissingToken,
87
+ reportIgnoredWith,
88
+ // getErrorRecoverySet,
89
+ consumeUntil,
90
+ recoverInline,
91
+ getMissingSymbol,
92
+ getExpectedTokensForMessage,
93
+ getTokenDisplay,
94
+ });
95
+
96
+ // Attempt to recover from problems in subrules, except if rule has defined a
92
97
  // local variable `_sync` with value 'nop'
93
98
  function sync( recognizer ) {
94
99
  // If already recovering, don't try to sync
@@ -166,7 +171,7 @@ function sync( recognizer ) {
166
171
  case ATNState.STAR_LOOP_BACK: { // 9
167
172
  // TODO: do not delete a '}'
168
173
  this.reportUnwantedToken(recognizer);
169
- const expecting = new IntervalSet.IntervalSet();
174
+ const expecting = new IntervalSet();
170
175
  expecting.addSet(recognizer.getExpectedTokens());
171
176
  const whatFollowsLoopIterationOrRule = expecting.addSet(this.getErrorRecoverySet(recognizer));
172
177
  this.consumeUntil(recognizer, whatFollowsLoopIterationOrRule);
@@ -270,21 +275,21 @@ function consumeUntil( recognizer, set ) {
270
275
 
271
276
  // let s=this.getTokenDisplay( recognizer.getCurrentToken(), recognizer );
272
277
  if (SEMI < 1 || RBRACE < 1) {
273
- super1.consumeUntil.call( this, recognizer, set );
278
+ this._super.consumeUntil.call( this, recognizer, set );
274
279
  }
275
280
  else if (set.contains(SEMI)) { // do not check for RBRACE here!
276
- super1.consumeUntil.call( this, recognizer, set );
281
+ this._super.consumeUntil.call( this, recognizer, set );
277
282
  // console.log('CONSUMED-ORIG:',s,this.getTokenDisplay( recognizer.getCurrentToken(), recognizer ),recognizer.getCurrentToken().line,intervalSetToArray( recognizer, set ));
278
283
  }
279
284
  else {
280
285
  // DO NOT modify input param `set`, as the set might be cached in the ATN
281
- const stop = new IntervalSet.IntervalSet();
286
+ const stop = new IntervalSet();
282
287
  stop.addSet( set );
283
288
  stop.removeOne( recognizer.constructor.Identifier );
284
289
  stop.addOne( SEMI );
285
290
  // I am not that sure whether to add RBRACE...
286
291
  stop.addOne( RBRACE );
287
- super1.consumeUntil.call( this, recognizer, stop );
292
+ this._super.consumeUntil.call( this, recognizer, stop );
288
293
  if (recognizer.getTokenStream().LA(1) === SEMI ||
289
294
  recognizer.getTokenStream().LA(1) === RBRACE && !set.contains(RBRACE)) {
290
295
  recognizer.consume();
@@ -310,11 +315,11 @@ function consumeUntil( recognizer, set ) {
310
315
  function recoverInline( recognizer ) {
311
316
  const identType = recognizer.constructor.Identifier;
312
317
  if (!identType || !recognizer.isExpectedToken( identType ))
313
- return super1.recoverInline.call( this, recognizer );
318
+ return this._super.recoverInline.call( this, recognizer );
314
319
 
315
320
  const token = recognizer.getCurrentToken();
316
321
  if (!keywordRegexp.test( token.text ))
317
- return super1.recoverInline.call( this, recognizer );
322
+ return this._super.recoverInline.call( this, recognizer );
318
323
 
319
324
  recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text },
320
325
  '$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
@@ -344,8 +349,12 @@ function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
344
349
  for (let j = v.start; j < v.stop; j++) {
345
350
  // a generic keyword as such does not appear in messages, only its replacements,
346
351
  // which are function name and argument position dependent:
347
- if (j === pc.GenericArgFull)
348
- names.push( ...recognizer.$genericKeywords.argFull );
352
+ if (j === pc.GenericExpr)
353
+ names.push( ...recognizer.$genericKeywords.expr );
354
+ else if (j === pc.GenericSeparator)
355
+ names.push( ...recognizer.$genericKeywords.separator );
356
+ else if (j === pc.GenericIntro)
357
+ names.push( ...recognizer.$genericKeywords.introMsg );
349
358
  // other expected tokens usually appear in messages, except the helper tokens
350
359
  // which are used to solve ambiguities via the parser method setLocalToken():
351
360
  else if (j !== pc.HelperToken1 && j !== pc.HelperToken2)
@@ -362,6 +371,9 @@ function intervalSetToArray( recognizer, expected, excludesForNextToken ) {
362
371
  else if (names.includes("';'")) {
363
372
  names = names.filter( n => n !== "'}'" );
364
373
  }
374
+ else if (names.includes("'?'")) {
375
+ names = names.filter( n => n !== "'?'" );
376
+ }
365
377
  names.sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
366
378
  return names;
367
379
  }
@@ -421,10 +433,10 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
421
433
  const hideAltsType = recognizer.constructor.HideAlternatives;
422
434
  const beforeUnreserved = recognizer.constructor.Number;
423
435
  if (!identType || !beforeUnreserved || beforeUnreserved + 2 > identType)
424
- return intervalSetToArray( recognizer, super1.getExpectedTokens.call( this, recognizer ) );
436
+ return intervalSetToArray( recognizer, this._super.getExpectedTokens.call( this, recognizer ) );
425
437
 
426
438
  const ll1 = new antlr4_LL1Analyzer(atn);
427
- const expected = new IntervalSet.IntervalSet();
439
+ const expected = new IntervalSet();
428
440
  const orig_addInterval = expected.addInterval;
429
441
  const orig_addSet = expected.addSet;
430
442
  expected.addInterval = addInterval;
@@ -485,7 +497,7 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
485
497
  }
486
498
 
487
499
  function addRange( interval, start, stop ) {
488
- orig_addInterval.call( interval, new IntervalSet.Interval( start, stop || start + 1 ) );
500
+ orig_addInterval.call( interval, new Interval( start, stop || start + 1 ) );
489
501
  }
490
502
  }
491
503