@sap/cds-compiler 3.1.2 → 3.4.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 (117) hide show
  1. package/CHANGELOG.md +101 -3
  2. package/bin/cdsc.js +4 -2
  3. package/doc/CHANGELOG_BETA.md +35 -0
  4. package/lib/api/main.js +153 -29
  5. package/lib/api/validate.js +8 -3
  6. package/lib/base/dictionaries.js +6 -6
  7. package/lib/base/error.js +2 -2
  8. package/lib/base/keywords.js +106 -24
  9. package/lib/base/message-registry.js +177 -79
  10. package/lib/base/messages.js +78 -57
  11. package/lib/base/model.js +2 -1
  12. package/lib/checks/actionsFunctions.js +1 -1
  13. package/lib/checks/annotationsOData.js +2 -2
  14. package/lib/checks/arrayOfs.js +15 -7
  15. package/lib/checks/cdsPersistence.js +1 -1
  16. package/lib/checks/checkForTypes.js +53 -0
  17. package/lib/checks/defaultValues.js +4 -2
  18. package/lib/checks/elements.js +81 -6
  19. package/lib/checks/foreignKeys.js +12 -13
  20. package/lib/checks/invalidTarget.js +10 -11
  21. package/lib/checks/managedInType.js +21 -15
  22. package/lib/checks/nullableKeys.js +1 -1
  23. package/lib/checks/onConditions.js +9 -9
  24. package/lib/checks/parameters.js +23 -0
  25. package/lib/checks/queryNoDbArtifacts.js +1 -1
  26. package/lib/checks/selectItems.js +1 -1
  27. package/lib/checks/sql-snippets.js +12 -10
  28. package/lib/checks/types.js +2 -2
  29. package/lib/checks/utils.js +17 -7
  30. package/lib/checks/validator.js +36 -14
  31. package/lib/compiler/assert-consistency.js +21 -13
  32. package/lib/compiler/builtins.js +8 -0
  33. package/lib/compiler/checks.js +57 -40
  34. package/lib/compiler/define.js +139 -69
  35. package/lib/compiler/extend.js +319 -50
  36. package/lib/compiler/finalize-parse-cdl.js +14 -9
  37. package/lib/compiler/kick-start.js +2 -35
  38. package/lib/compiler/populate.js +111 -68
  39. package/lib/compiler/propagator.js +5 -3
  40. package/lib/compiler/resolve.js +71 -108
  41. package/lib/compiler/shared.js +82 -54
  42. package/lib/compiler/tweak-assocs.js +26 -14
  43. package/lib/compiler/utils.js +13 -2
  44. package/lib/edm/annotations/genericTranslation.js +10 -7
  45. package/lib/edm/csn2edm.js +11 -11
  46. package/lib/edm/edm.js +17 -9
  47. package/lib/edm/edmPreprocessor.js +53 -30
  48. package/lib/edm/edmUtils.js +7 -2
  49. package/lib/gen/Dictionary.json +14 -0
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +3 -2
  52. package/lib/gen/languageParser.js +4312 -4186
  53. package/lib/inspect/inspectModelStatistics.js +1 -1
  54. package/lib/inspect/inspectPropagation.js +23 -9
  55. package/lib/json/csnVersion.js +13 -13
  56. package/lib/json/from-csn.js +161 -172
  57. package/lib/json/to-csn.js +70 -10
  58. package/lib/language/.eslintrc.json +4 -0
  59. package/lib/language/antlrParser.js +8 -11
  60. package/lib/language/docCommentParser.js +1 -2
  61. package/lib/language/errorStrategy.js +54 -27
  62. package/lib/language/genericAntlrParser.js +140 -93
  63. package/lib/language/language.g4 +57 -33
  64. package/lib/language/multiLineStringParser.js +75 -63
  65. package/lib/main.d.ts +3 -6
  66. package/lib/main.js +1 -0
  67. package/lib/model/.eslintrc.json +13 -0
  68. package/lib/model/api.js +4 -2
  69. package/lib/model/csnRefs.js +78 -50
  70. package/lib/model/csnUtils.js +272 -222
  71. package/lib/model/enrichCsn.js +41 -31
  72. package/lib/model/revealInternalProperties.js +61 -57
  73. package/lib/model/sortViews.js +35 -31
  74. package/lib/modelCompare/compare.js +52 -18
  75. package/lib/modelCompare/filter.js +83 -0
  76. package/lib/optionProcessor.js +10 -1
  77. package/lib/render/manageConstraints.js +11 -7
  78. package/lib/render/toCdl.js +151 -106
  79. package/lib/render/toHdbcds.js +8 -6
  80. package/lib/render/toRename.js +4 -4
  81. package/lib/render/toSql.js +17 -7
  82. package/lib/render/utils/common.js +27 -9
  83. package/lib/render/utils/sql.js +5 -5
  84. package/lib/sql-identifier.js +7 -0
  85. package/lib/transform/db/applyTransformations.js +32 -3
  86. package/lib/transform/db/assertUnique.js +27 -38
  87. package/lib/transform/db/expansion.js +92 -41
  88. package/lib/transform/db/flattening.js +1 -1
  89. package/lib/transform/db/temporal.js +3 -1
  90. package/lib/transform/db/transformExists.js +8 -2
  91. package/lib/transform/db/views.js +42 -13
  92. package/lib/transform/draft/db.js +2 -2
  93. package/lib/transform/forOdataNew.js +10 -7
  94. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
  95. package/lib/transform/localized.js +29 -20
  96. package/lib/transform/odata/toFinalBaseType.js +8 -11
  97. package/lib/transform/odata/typesExposure.js +2 -1
  98. package/lib/transform/parseExpr.js +245 -0
  99. package/lib/transform/transformUtilsNew.js +122 -51
  100. package/lib/transform/translateAssocsToJoins.js +17 -16
  101. package/lib/utils/moduleResolve.js +5 -5
  102. package/lib/utils/objectUtils.js +3 -3
  103. package/lib/utils/term.js +5 -5
  104. package/package.json +2 -2
  105. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  106. package/share/messages/check-proper-type-of.md +4 -4
  107. package/share/messages/check-proper-type.md +2 -2
  108. package/share/messages/duplicate-autoexposed.md +4 -4
  109. package/share/messages/extend-repeated-intralayer.md +4 -5
  110. package/share/messages/extend-unrelated-layer.md +4 -4
  111. package/share/messages/message-explanations.json +3 -1
  112. package/share/messages/redirected-to-ambiguous.md +7 -6
  113. package/share/messages/redirected-to-complex.md +63 -0
  114. package/share/messages/redirected-to-unrelated.md +6 -5
  115. package/share/messages/rewrite-not-supported.md +4 -4
  116. package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
  117. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -721,16 +721,21 @@ aspectDef[ art, outer ] locals[ name = {} ]
721
721
  )*
722
722
  )?
723
723
  )?
724
- '{' { $art.elements = this.createDict(); }
725
- ( elementDef[ $art ]* )
726
- '}' { this.finalizeDictOrArray( $art.elements ); }
727
- // TODO: action definitions in a specific section?
728
- (
729
- ACTIONS '{' { $art.actions = this.createDict(); }
730
- actionFunctionDef[ $art ]*
731
- '}' { this.finalizeDictOrArray( $art.actions ); }
732
- )?
733
- optionalSemi
724
+ ( // `aspect MyAspect {};`
725
+ '{' { $art.elements = this.createDict(); }
726
+ ( elementDef[ $art ]* )
727
+ '}' { this.finalizeDictOrArray( $art.elements ); }
728
+ // TODO: action definitions in a specific section?
729
+ (
730
+ ACTIONS '{' { $art.actions = this.createDict(); }
731
+ actionFunctionDef[ $art ]*
732
+ '}' { this.finalizeDictOrArray( $art.actions ); }
733
+ )?
734
+ optionalSemi
735
+ | // `aspect MyAspect;`, e.g. for annotation aspects.
736
+ { this.aspectWithoutElements( $art ); }
737
+ requiredSemi
738
+ )
734
739
  ;
735
740
 
736
741
  typeDef[ art, outer ] locals[ name = {} ]
@@ -751,7 +756,7 @@ extendType[ art, outer ] locals[ name = {} ]
751
756
  { $art.expectedKind = 'type'; $art.name = $name;
752
757
  this.addItem( $art, $outer, 'extensions', 'extend' );
753
758
  }
754
- extendWithOptElements[ $art, $art ]
759
+ extendWithOptElementsOrType[ $art, $art ]
755
760
  ;
756
761
 
757
762
  annotationDef[ art, outer ] locals[ name = {} ]
@@ -840,11 +845,16 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
840
845
  enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
841
846
  '}' { this.finalizeDictOrArray( $art.enum ); }
842
847
  optionalSemi
848
+ |
849
+ // extend Art with (length: 10);
850
+ // `with` is required, or we could have `extend String(length:10);`.
851
+ typeNamedArgList[ $art ]
852
+ requiredSemi
843
853
  )
844
854
  )
845
855
  ;
846
856
 
847
- extendWithOptElements[ art ]
857
+ extendWithOptElementsOrType[ art ]
848
858
  :
849
859
  WITH { this.noSemicolonHere(); this.docComment( $art ); }
850
860
  annotationAssignment_ll1[ $art ]*
@@ -857,6 +867,10 @@ extendWithOptElements[ art ]
857
867
  '}' { this.finalizeDictOrArray( $art.elements ); }
858
868
  { this.checkExtensionDict( $art.elements ); }
859
869
  optionalSemi
870
+ |
871
+ // extend type|element Art with (length: 10);
872
+ typeNamedArgList[ $art ]
873
+ requiredSemi
860
874
  |
861
875
  requiredSemi
862
876
  )
@@ -1066,12 +1080,12 @@ mixinElementDef[ outer ] locals[ art = {} ]
1066
1080
  |
1067
1081
  typeRefOptArgs[ $art ]
1068
1082
  ( as='=' expression
1069
- { this.notSupportedYet( 'Calculated fields are not supported yet', $as ); }
1083
+ { this.error( 'syntax-unsupported-field', $as ); }
1070
1084
  )?
1071
1085
  )
1072
1086
  |
1073
1087
  as='=' expression
1074
- { this.notSupportedYet( 'Calculated fields are not supported yet', $as ); }
1088
+ { this.error( 'syntax-unsupported-field', $as ); }
1075
1089
  )
1076
1090
  requiredSemi
1077
1091
  ;
@@ -1090,8 +1104,7 @@ elementDefInner[ art, outer, allowEq ]
1090
1104
  ( masked=MASKED
1091
1105
  {
1092
1106
  $art.masked = this.valueWithTokenLocation( true, $masked ) ;
1093
- this.message( 'syntax-invalid-masked', $masked, { keyword: 'masked' },
1094
- 'Keyword $(KEYWORD) not supported' );
1107
+ this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
1095
1108
  }
1096
1109
  )?
1097
1110
  // TODO: order?
@@ -1203,7 +1216,7 @@ elementDefInner[ art, outer, allowEq ]
1203
1216
  eq='=' e=expression // never introduce AS as syntax variant of '='
1204
1217
  {
1205
1218
  if (!$allowEq || $e.expr && !$e.expr.literal )
1206
- this.notSupportedYet( 'Calculated fields are not supported yet', $eq );
1219
+ this.error( 'syntax-unsupported-field', $eq );
1207
1220
  else if ($e.expr)
1208
1221
  $art.value = $e.expr;
1209
1222
  }
@@ -1220,7 +1233,7 @@ extendElement[ art, outer ]
1220
1233
  ( expected=ELEMENT { $art.expectedKind = 'element'; } )?
1221
1234
  name=ident['Element']
1222
1235
  { this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
1223
- extendWithOptElements[ $art, $art ]
1236
+ extendWithOptElementsOrType[ $art, $art ]
1224
1237
  ;
1225
1238
 
1226
1239
  selectItemDef[ outer ] locals[ art ]
@@ -1246,19 +1259,20 @@ selectItemDefBody[ art, outer ]
1246
1259
  e=expression { $art.value = $e.expr; }
1247
1260
  // we cannot use 'condition' instead, as long as we allow aliases without
1248
1261
  // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1249
- ( AS n1=ident['Item'] { $art.name = $n1.id }
1262
+ ( as=AS n1=ident['Item'] { $art.name = $n1.id }
1250
1263
  | n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
1251
1264
  | { if (this.getCurrentToken().text !== '.') this.classifyImplicitName( 'Item', $e.expr ); }
1252
1265
  )
1253
- { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] ); }
1266
+ { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
1267
+ else if ($art.name) this.excludeExpected( ["'.'"] );
1268
+ }
1254
1269
  (
1255
- { if ($art.value && !$art.value.path) this.reportExpandInline( 'expand' ); }
1270
+ { this.reportExpandInline( $art, false ); }
1256
1271
  selectItemInlineList[ $art, 'expand' ]
1257
1272
  excludingClause[ $art ]?
1258
1273
  // TODO: we might alternatively allow AS here
1259
1274
  |
1260
- // TODO: complain if AS has been used - or in definer?
1261
- { if ($art.value && !$art.value.path) this.reportExpandInline( 'inline' ); }
1275
+ { this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
1262
1276
  DOTbeforeBRACE // ...orASTERISK
1263
1277
  (
1264
1278
  selectItemInlineList[ $art, 'inline' ]
@@ -1384,7 +1398,7 @@ elementProperties[ elem ]
1384
1398
  nullability[ $elem ]?
1385
1399
  |
1386
1400
  eq='='
1387
- { this.notSupportedYet( 'Calculated fields are not supported yet', $eq ); }
1401
+ { this.error( 'syntax-unsupported-field', $eq ); }
1388
1402
  expression
1389
1403
  ;
1390
1404
 
@@ -1799,6 +1813,16 @@ typeRefArgs[ art ]
1799
1813
  ')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
1800
1814
  ;
1801
1815
 
1816
+ typeNamedArgList[ art ]
1817
+ :
1818
+ paren='('
1819
+ typeNamedArg[ $art ]
1820
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1821
+ typeNamedArg[ $art ]
1822
+ )*
1823
+ ')'
1824
+ ;
1825
+
1802
1826
  typeNamedArg[ art ] locals[ arg = '' ]
1803
1827
  :
1804
1828
  name=ident['paramname']
@@ -2206,7 +2230,7 @@ conditionTerm returns [ cond ]
2206
2230
  { $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [
2207
2231
  { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' }
2208
2232
  ] };
2209
- this.csnParseOnly( 'Dynamic parameter "?" is not supported', $qm );
2233
+ this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', name: '?' } );
2210
2234
  }
2211
2235
  |
2212
2236
  ep=valuePath[ 'ref' ]
@@ -2330,7 +2354,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2330
2354
  ne=NEW nqp=valuePath[ 'ref', null] // token rewrite for NEW
2331
2355
  // please note: there will be no compiler-supported code completion after NEW
2332
2356
  { $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
2333
- this.notSupportedYet( $ne ); }
2357
+ this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
2334
2358
  |
2335
2359
  vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
2336
2360
  { this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
@@ -2343,7 +2367,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2343
2367
  { $expr = $vp.qp;; $expr.scope = 'param'; }
2344
2368
  | pp=Number
2345
2369
  { $expr = { param: this.numberLiteral( $pp ), scope: 'param' };
2346
- this.csnParseOnly( 'Positional parameter ":' + $pp.text + '" is not supported', $pp );
2370
+ this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', name: ':' + $pp.text } );
2347
2371
  }
2348
2372
  )
2349
2373
  |
@@ -2351,7 +2375,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2351
2375
  // if we have an HideAlternatives here, we would block it to use it in
2352
2376
  // parallel to an expression (would produce adaptivePredict() otherwise)
2353
2377
  { $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
2354
- this.csnParseOnly( 'Dynamic parameter "?" is not supported', $qm );
2378
+ this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', name: '?' } );
2355
2379
  }
2356
2380
  |
2357
2381
  open='('
@@ -2641,8 +2665,9 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
2641
2665
  const item = { literal: 'token', val: '...', location: this.tokenLocation($e) };
2642
2666
  if ($ctx.upTo) item.upTo = $upTo.val;
2643
2667
  $assignment.val.push( item );
2644
- if ($seenEllipsis === true) // TODO: adapt msg to UP TO
2645
- this.error( 'syntax-unexpected-ellipsis', $e, { '#': 'std', code: '...' } );
2668
+ if ($seenEllipsis === true)
2669
+ this.error( 'syntax-unexpected-ellipsis', $e,
2670
+ { '#': 'duplicate', code: '...', keyword: 'up to' } );
2646
2671
  else
2647
2672
  $seenEllipsis = !$ctx.upTo || 'upTo';
2648
2673
  }}
@@ -2652,9 +2677,8 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
2652
2677
  cb=']'
2653
2678
  {
2654
2679
  if ($seenEllipsis === 'upTo')
2655
- this.error( 'syntax-expecting-ellipsis', $cb, // at closing bracket
2656
- { code: '... up to', newcode: '...' },
2657
- 'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
2680
+ this.error( 'syntax-missing-ellipsis', $cb, // at closing bracket
2681
+ { code: '... up to', newcode: '...' } );
2658
2682
  }
2659
2683
  |
2660
2684
  v1=literalValue { Object.assign( $assignment, $v1.val ); }
@@ -25,9 +25,9 @@ const {
25
25
  * of whitespace characters removed.
26
26
  */
27
27
  function stripIndentation(str) {
28
- if (str === '') {
29
- return ['', 0];
30
- }
28
+ if (str === '')
29
+ return [ '', 0 ];
30
+
31
31
 
32
32
  // Note: We have to check all newline characters, as the string is not normalized, yet.
33
33
  const lines = str.split(cdlNewLineRegEx);
@@ -39,20 +39,19 @@ function stripIndentation(str) {
39
39
  // If there is a trailing line break, it means that ``` is on newline and
40
40
  // therefore the indentation to remove is 0.
41
41
  // Remove the last newline, which may be CRLF.
42
- return [lines.slice(0, -1).join('\n'), 0];
42
+ return [ lines.slice(0, -1).join('\n'), 0 ];
43
43
  }
44
44
 
45
45
  const minIndent = lines.reduce((min, line, index) => {
46
46
  // Note: Last line is the line containing ```. There, we always count the indentation,
47
47
  // even if blank. For all other lines, blank lines are ignored.
48
- if (isWhitespaceOrNewLineOnly(line) && index !== (n-1))
48
+ if (isWhitespaceOrNewLineOnly(line) && index !== (n - 1))
49
49
  return min;
50
50
 
51
51
  let count = 0;
52
52
  const length = Math.min(min, line.length);
53
- while (count < length && isWhitespaceCharacterNoNewline(line[count])) {
53
+ while (count < length && isWhitespaceCharacterNoNewline(line[count]))
54
54
  count++;
55
- }
56
55
  return Math.min(min, count);
57
56
  }, Number.MAX_SAFE_INTEGER);
58
57
 
@@ -63,10 +62,10 @@ function stripIndentation(str) {
63
62
  }
64
63
 
65
64
  // Remove trailing last line, if there was nothing else in that line.
66
- if (lines[n-1] === '')
65
+ if (lines[n - 1] === '')
67
66
  lines.pop();
68
67
 
69
- return [lines.join('\n'), minIndent];
68
+ return [ lines.join('\n'), minIndent ];
70
69
  }
71
70
 
72
71
  class MultiLineStringParser {
@@ -75,9 +74,9 @@ class MultiLineStringParser {
75
74
  this.token = token;
76
75
  this.str = token.text; // Copy because .text is a getter
77
76
 
78
- if (this.str[0] !== '`' || this.str[this.str.length-1] !== '`') {
77
+ if (this.str[0] !== '`' || this.str[this.str.length - 1] !== '`')
78
+ // eslint-disable-next-line max-len
79
79
  throw new Error('Invalid multi-line string sequence: Require string to be surrounded by back-ticks!');
80
- }
81
80
 
82
81
  this.output = [];
83
82
  this.isTextBlock = this.str.startsWith('```');
@@ -90,7 +89,8 @@ class MultiLineStringParser {
90
89
  if (this.isTextBlock) {
91
90
  this.i = 3;
92
91
  this.end = this.str.length - 3;
93
- } else {
92
+ }
93
+ else {
94
94
  this.i = 1;
95
95
  this.end = this.str.length - 1;
96
96
  }
@@ -102,9 +102,8 @@ class MultiLineStringParser {
102
102
  * @return {string}
103
103
  */
104
104
  parse() {
105
- if (this.str.length === 2) {
105
+ if (this.str.length === 2)
106
106
  return ''; // Nothing to do: ``
107
- }
108
107
 
109
108
  if (this.isTextBlock) {
110
109
  // If there are no line breaks, emit an error as normal single-back-tick
@@ -112,7 +111,7 @@ class MultiLineStringParser {
112
111
  // there is no text without at least one line break.
113
112
  if (!cdlNewLineRegEx.test(this.str)) {
114
113
  const loc = this._locationForCharacters(this.end, 1);
115
- this.parser.message('syntax-invalid-text-block', loc);
114
+ this.parser.error('syntax-invalid-text-block', loc);
116
115
  return '';
117
116
  }
118
117
  this._skipOptionalLanguageIdentifierLine();
@@ -135,7 +134,7 @@ class MultiLineStringParser {
135
134
  // Note: Index is at first character of string
136
135
 
137
136
  do {
138
- switch(this._current()) {
137
+ switch (this._current()) {
139
138
  case this._matchLineBreakAtCurrentChar():
140
139
  this.output.push('\n');
141
140
  break;
@@ -146,8 +145,8 @@ class MultiLineStringParser {
146
145
  case '$':
147
146
  if (this._lookahead() === '{') {
148
147
  const loc = this._locationForCharacters(this.i, 2);
149
- this.parser.message('syntax-missing-escape', loc,
150
- { '#': 'placeholder', code: '${', newcode: '\\${' });
148
+ this.parser.error('syntax-missing-escape', loc,
149
+ { '#': 'placeholder', code: '${', newcode: '\\${' });
151
150
  }
152
151
  this.output.push(this.str[this.i]);
153
152
  break;
@@ -155,7 +154,7 @@ class MultiLineStringParser {
155
154
  this.output.push(this.str[this.i]);
156
155
  break;
157
156
  }
158
- } while(this._move());
157
+ } while (this._move());
159
158
 
160
159
  return this.output.join('');
161
160
  }
@@ -166,27 +165,27 @@ class MultiLineStringParser {
166
165
  * @private
167
166
  */
168
167
  _innerEscape() {
169
- switch(this._current()) {
168
+ switch (this._current()) {
170
169
  case this._matchLineBreakAtCurrentChar():
171
170
  // Don't add to output -> line break is escaped
172
171
  break;
173
172
  case 'b': // backspace
174
- this.output.push(`\b`);
173
+ this.output.push('\b');
175
174
  break;
176
175
  case 'f': // form feed
177
- this.output.push(`\f`);
176
+ this.output.push('\f');
178
177
  break;
179
178
  case 'v': // vertical tabulator
180
- this.output.push(`\v`);
179
+ this.output.push('\v');
181
180
  break;
182
181
  case 'r': // carriage return
183
- this.output.push(`\r`);
182
+ this.output.push('\r');
184
183
  break;
185
184
  case 'n': // line feed
186
- this.output.push(`\n`);
185
+ this.output.push('\n');
187
186
  break;
188
187
  case 't': // tab
189
- this.output.push(`\t`);
188
+ this.output.push('\t');
190
189
  break;
191
190
  case '\\':
192
191
  case '"':
@@ -206,21 +205,23 @@ class MultiLineStringParser {
206
205
  break;
207
206
  case '0': // null terminator
208
207
  if (!/^\d$/.test(this._lookahead())) {
209
- this.output.push(`\0`);
208
+ this.output.push('\0');
210
209
  break;
211
210
  }
212
211
  // Let the default case handle octal representation.
213
212
  // fallthrough
214
213
  default: {
215
214
  this.output.push(this._current());
216
- const loc = this._locationForCharacters(this.i-1, 2);
215
+ const loc = this._locationForCharacters(this.i - 1, 2);
217
216
  if (/\s/.test(this._current())) {
218
- this.parser.message('syntax-invalid-escape', loc, { '#': 'whitespace' });
219
- } else if (/\d/.test(this._current())) {
220
- this.parser.message('syntax-invalid-escape', loc, { '#': 'octal' });
221
- } else {
222
- const code = this._makeCode('\\' + this._current());
223
- this.parser.message('syntax-unknown-escape', loc, { '#': 'std', code });
217
+ this.parser.error('syntax-invalid-escape', loc, { '#': 'whitespace' });
218
+ }
219
+ else if (/\d/.test(this._current())) {
220
+ this.parser.error('syntax-invalid-escape', loc, { '#': 'octal' });
221
+ }
222
+ else {
223
+ const code = this._makeCode(`\\${ this._current() }`);
224
+ this.parser.message('syntax-unknown-escape', loc, { '#': 'std', code });
224
225
  }
225
226
  break;
226
227
  }
@@ -239,9 +240,9 @@ class MultiLineStringParser {
239
240
  // syntax variant as the first invalid code point is \u{110000}
240
241
  // and an empty `codePoint` is only possible with the braced variant.
241
242
  const reportInvalidCodePoint = () => {
242
- const code = this._makeCode(`\\u{${codePoint}}`);
243
- const loc = this._locationForCharacters(this.i-codePoint.length, codePoint.length);
244
- this.parser.message('syntax-invalid-escape', loc, { '#': 'codepoint', code });
243
+ const code = this._makeCode(`\\u{${ codePoint }}`);
244
+ const loc = this._locationForCharacters(this.i - codePoint.length, codePoint.length);
245
+ this.parser.error('syntax-invalid-escape', loc, { '#': 'codepoint', code });
245
246
  };
246
247
 
247
248
  const n = Number.parseInt(codePoint, 16);
@@ -252,7 +253,8 @@ class MultiLineStringParser {
252
253
 
253
254
  try {
254
255
  this.output.push(String.fromCodePoint(n));
255
- } catch (e) {
256
+ }
257
+ catch (e) {
256
258
  // RangeError is thrown if number isn't a valid code point
257
259
  reportInvalidCodePoint();
258
260
  }
@@ -275,18 +277,20 @@ class MultiLineStringParser {
275
277
  if (!this._eos() && /^[\dA-Fa-f]$/.test(this._lookahead())) {
276
278
  this._move();
277
279
  codePoint += this._current();
278
- } else {
280
+ }
281
+ else {
279
282
  break;
280
283
  }
281
284
  }
282
285
 
283
286
  if (codePoint.length === count) {
284
287
  this._parseHexCodePoint(codePoint);
285
- } else {
286
- const loc = this._locationForCharacters(this.i+1, 1);
287
- const code = this._eos(this.i+1) ? `\\${mode}${codePoint}` : `\\${mode}${codePoint}${this._lookahead()}`;
288
- this.parser.message('syntax-invalid-escape', loc,
289
- { '#': 'hex-count', number: count, code: this._makeCode(code) });
288
+ }
289
+ else {
290
+ const loc = this._locationForCharacters(this.i + 1, 1);
291
+ const code = this._eos(this.i + 1) ? `\\${ mode }${ codePoint }` : `\\${ mode }${ codePoint }${ this._lookahead() }`;
292
+ this.parser.error('syntax-invalid-escape', loc,
293
+ { '#': 'hex-count', number: count, code: this._makeCode(code) });
290
294
  }
291
295
  }
292
296
 
@@ -305,14 +309,17 @@ class MultiLineStringParser {
305
309
  if (/^[\dA-Fa-f]$/.test(this._lookahead())) {
306
310
  this._move();
307
311
  codePoint += this._current();
308
- } else if (this._lookahead() === '}') {
312
+ }
313
+ else if (this._lookahead() === '}') {
309
314
  break;
310
- } else if (!this._eos(this.i+1)) {
311
- const loc = this._locationForCharacters(this.i+1, 1); // Point to the exact character
312
- const code = this._makeCode(`\\u{${codePoint}${this._lookahead()}…}`);
313
- this.parser.message('syntax-invalid-escape', loc, { '#': 'unicode-hex', code });
315
+ }
316
+ else if (!this._eos(this.i + 1)) {
317
+ const loc = this._locationForCharacters(this.i + 1, 1); // Point to the exact character
318
+ const code = this._makeCode(`\\u{${ codePoint }${ this._lookahead() }…}`);
319
+ this.parser.error('syntax-invalid-escape', loc, { '#': 'unicode-hex', code });
314
320
  return;
315
- } else {
321
+ }
322
+ else {
316
323
  break;
317
324
  }
318
325
  }
@@ -320,9 +327,10 @@ class MultiLineStringParser {
320
327
  if (this._lookahead() === '}') {
321
328
  this._move();
322
329
  this._parseHexCodePoint(codePoint);
323
- } else {
330
+ }
331
+ else {
324
332
  const loc = this._locationForCharacters(this.i, 1);
325
- this.parser.message('syntax-invalid-escape', loc, { '#': 'unicode-brace' });
333
+ this.parser.error('syntax-invalid-escape', loc, { '#': 'unicode-brace' });
326
334
  }
327
335
  }
328
336
 
@@ -333,7 +341,7 @@ class MultiLineStringParser {
333
341
  * @private
334
342
  */
335
343
  _skipOptionalLanguageIdentifierLine() {
336
- while(!this._eos()) {
344
+ while (!this._eos()) {
337
345
  switch (this._current()) {
338
346
  case this._matchLineBreakAtCurrentChar():
339
347
  this._move();
@@ -342,7 +350,7 @@ class MultiLineStringParser {
342
350
  // Do not allow an escape in the language identifier. If at the line's end, users
343
351
  // may expect the identifier to span more than the first line, which is _not_ the case.
344
352
  const loc = this._locationForCharacters(this.i, 1);
345
- this.parser.message('syntax-invalid-escape', loc, { '#': 'language-identifier' });
353
+ this.parser.error('syntax-invalid-escape', loc, { '#': 'language-identifier' });
346
354
  this._move();
347
355
  break;
348
356
  }
@@ -367,7 +375,7 @@ class MultiLineStringParser {
367
375
  */
368
376
  _matchLineBreakAtCurrentChar() {
369
377
  // Only increase line number for \n, because ANTLR does the same
370
- switch(this._current()) {
378
+ switch (this._current()) {
371
379
  case '\r':
372
380
  if (this._lookahead() === '\n') {
373
381
  this._move(); // \r\n is normalized
@@ -382,6 +390,7 @@ class MultiLineStringParser {
382
390
  case '\u2028': // LS
383
391
  case '\u2029': // PS
384
392
  return this._current();
393
+ default: break;
385
394
  }
386
395
  return null;
387
396
  }
@@ -419,7 +428,7 @@ class MultiLineStringParser {
419
428
  * @returns {string}
420
429
  */
421
430
  _lookahead() {
422
- return this.str[this.i+1];
431
+ return this.str[this.i + 1];
423
432
  }
424
433
 
425
434
  /**
@@ -439,7 +448,7 @@ class MultiLineStringParser {
439
448
  * @returns {string}
440
449
  */
441
450
  _previous() {
442
- return this.str[this.i-1];
451
+ return this.str[this.i - 1];
443
452
  }
444
453
 
445
454
  /**
@@ -455,8 +464,12 @@ class MultiLineStringParser {
455
464
  file: this.parser.filename,
456
465
  line: this.token.line + this._lineInString,
457
466
  endLine: this.token.line + this._lineInString,
458
- col: this._lineInString > 0 ? i - this._currentLineBreakIndex + this._indentation : this.token.column + i + 1,
459
- endCol: this._lineInString > 0 ? i - this._currentLineBreakIndex + width + this._indentation : this.token.column + i + width + 1,
467
+ col: this._lineInString > 0
468
+ ? i - this._currentLineBreakIndex + this._indentation
469
+ : this.token.column + i + 1,
470
+ endCol: this._lineInString > 0
471
+ ? i - this._currentLineBreakIndex + width + this._indentation
472
+ : this.token.column + i + width + 1,
460
473
  };
461
474
  }
462
475
 
@@ -468,7 +481,7 @@ class MultiLineStringParser {
468
481
  * @param {string} code
469
482
  * @private
470
483
  */
471
- _makeCode(code) {
484
+ _makeCode(code) { // eslint-disable-line class-methods-use-this
472
485
  // For characters that may be rendered as newline,
473
486
  // see <https://www.unicode.org/reports/tr14/tr14-32.html>.
474
487
  //
@@ -484,9 +497,8 @@ class MultiLineStringParser {
484
497
  //
485
498
  // For Visualization, see <https://en.wikipedia.org/wiki/Newline#Unicode>
486
499
  // U+23CE: ⏎
487
-
488
- const allPossibleNewLineCharacters = /[\u{000A}\u{000B}\u{000C}\u{000D}\u{0085}\u{2028}\u{2029}]/ug;
489
- return code.replace(allPossibleNewLineCharacters, '\u{23CE}');
500
+ const allNewLineCharacters = /[\u{000A}\u{000B}\u{000C}\u{000D}\u{0085}\u{2028}\u{2029}]/ug;
501
+ return code.replace(allNewLineCharacters, '\u{23CE}');
490
502
  }
491
503
  }
492
504
 
package/lib/main.d.ts CHANGED
@@ -786,15 +786,12 @@ declare namespace compiler {
786
786
  */
787
787
  export type CdlResult = {
788
788
  /**
789
- * Rendered model, excluding not-applied extensions.
789
+ * Rendered model, including extensions.
790
790
  */
791
791
  model?: string
792
792
  /**
793
- * Rendered extend/annotate statements of `csn.extensions`
794
- */
795
- unappliedExtensions?: string
796
- /**
797
- * Rendered csn.namespace property + using directives.
793
+ * Rendered `csn.namespace` property + using directives.
794
+ * Useful to keep the `csn.namespace` property when re-compiling the to.cdl() result.
798
795
  */
799
796
  namespace?: string
800
797
  }
package/lib/main.js CHANGED
@@ -115,6 +115,7 @@ module.exports = {
115
115
  functions: Object.freeze([ ...keywords.cdl_functions ] ),
116
116
  }),
117
117
  sql: Object.assign((...args) => snapi.sql(...args), {
118
+ migration: (...args) => snapi.sql.migration(...args),
118
119
  sqlite: {
119
120
  keywords: Object.freeze([ ...keywords.sqlite ] )
120
121
  },
@@ -0,0 +1,13 @@
1
+ {
2
+ "root": true,
3
+ "extends": "../../.eslintrc-ydkjsi.json",
4
+ "rules": {
5
+ "max-len": [ "error", {
6
+ "code": 180, // TODO: Remove
7
+ "tabWidth": 2,
8
+ "ignoreRegExpLiterals": false,
9
+ "ignoreStrings": false,
10
+ "ignoreTemplateLiterals": true
11
+ }]
12
+ }
13
+ }
package/lib/model/api.js CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Do not change at will - they are in the compiler API!
4
4
 
5
+ 'use strict';
6
+
5
7
  /**
6
8
  * Dictionary of default traversal functions for function `traverseCsn`.
7
9
  * It maps CSN property names to functions which are used by default
@@ -23,8 +25,8 @@ const defaultFunctions = {
23
25
  actions: dictionary,
24
26
  mixin: dictionary,
25
27
  definitions: dictionary,
26
- '$': () => { /* do not traverse properties starting with '$' */},
27
- }
28
+ $: () => { /* do not traverse properties starting with '$' */ },
29
+ };
28
30
 
29
31
  /**
30
32
  * Traverse the CSN node `csn`.