@sap/cds-compiler 2.11.4 → 2.13.8

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 (133) hide show
  1. package/CHANGELOG.md +159 -1
  2. package/bin/cds_update_identifiers.js +7 -7
  3. package/bin/cdsc.js +22 -23
  4. package/bin/cdsse.js +2 -2
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +25 -6
  7. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  8. package/doc/NameResolution.md +21 -16
  9. package/lib/api/main.js +30 -63
  10. package/lib/api/options.js +5 -5
  11. package/lib/api/validate.js +0 -5
  12. package/lib/backends.js +15 -23
  13. package/lib/base/dictionaries.js +0 -8
  14. package/lib/base/error.js +26 -0
  15. package/lib/base/keywords.js +7 -17
  16. package/lib/base/location.js +9 -4
  17. package/lib/base/message-registry.js +52 -2
  18. package/lib/base/messages.js +16 -26
  19. package/lib/base/model.js +2 -62
  20. package/lib/base/optionProcessorHelper.js +246 -183
  21. package/lib/checks/.eslintrc.json +2 -0
  22. package/lib/checks/actionsFunctions.js +2 -1
  23. package/lib/checks/annotationsOData.js +1 -1
  24. package/lib/checks/cdsPersistence.js +2 -1
  25. package/lib/checks/enricher.js +17 -1
  26. package/lib/checks/foreignKeys.js +4 -4
  27. package/lib/checks/invalidTarget.js +3 -1
  28. package/lib/checks/managedInType.js +4 -4
  29. package/lib/checks/managedWithoutKeys.js +3 -1
  30. package/lib/checks/queryNoDbArtifacts.js +1 -3
  31. package/lib/checks/selectItems.js +4 -4
  32. package/lib/checks/sql-snippets.js +94 -0
  33. package/lib/checks/types.js +1 -1
  34. package/lib/checks/validator.js +12 -7
  35. package/lib/compiler/assert-consistency.js +10 -6
  36. package/lib/compiler/base.js +0 -1
  37. package/lib/compiler/builtins.js +8 -6
  38. package/lib/compiler/checks.js +46 -12
  39. package/lib/compiler/cycle-detector.js +1 -1
  40. package/lib/compiler/define.js +1103 -0
  41. package/lib/compiler/extend.js +983 -0
  42. package/lib/compiler/finalize-parse-cdl.js +231 -0
  43. package/lib/compiler/index.js +33 -14
  44. package/lib/compiler/kick-start.js +190 -0
  45. package/lib/compiler/moduleLayers.js +4 -4
  46. package/lib/compiler/populate.js +1226 -0
  47. package/lib/compiler/propagator.js +113 -47
  48. package/lib/compiler/resolve.js +1433 -0
  49. package/lib/compiler/shared.js +76 -38
  50. package/lib/compiler/tweak-assocs.js +529 -0
  51. package/lib/compiler/utils.js +204 -33
  52. package/lib/edm/.eslintrc.json +5 -0
  53. package/lib/edm/annotations/genericTranslation.js +38 -25
  54. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  55. package/lib/edm/csn2edm.js +10 -9
  56. package/lib/edm/edm.js +19 -20
  57. package/lib/edm/edmPreprocessor.js +166 -95
  58. package/lib/edm/edmUtils.js +127 -34
  59. package/lib/gen/Dictionary.json +92 -43
  60. package/lib/gen/language.checksum +1 -1
  61. package/lib/gen/language.interp +11 -1
  62. package/lib/gen/language.tokens +86 -82
  63. package/lib/gen/languageLexer.interp +18 -1
  64. package/lib/gen/languageLexer.js +925 -847
  65. package/lib/gen/languageLexer.tokens +78 -74
  66. package/lib/gen/languageParser.js +5434 -4298
  67. package/lib/json/from-csn.js +59 -17
  68. package/lib/json/to-csn.js +143 -71
  69. package/lib/language/antlrParser.js +3 -3
  70. package/lib/language/docCommentParser.js +3 -3
  71. package/lib/language/genericAntlrParser.js +144 -54
  72. package/lib/language/language.g4 +424 -203
  73. package/lib/language/multiLineStringParser.js +536 -0
  74. package/lib/main.d.ts +472 -61
  75. package/lib/main.js +38 -11
  76. package/lib/model/api.js +3 -1
  77. package/lib/model/csnRefs.js +321 -204
  78. package/lib/model/csnUtils.js +224 -263
  79. package/lib/model/enrichCsn.js +97 -40
  80. package/lib/model/revealInternalProperties.js +27 -6
  81. package/lib/model/sortViews.js +2 -1
  82. package/lib/modelCompare/compare.js +17 -12
  83. package/lib/optionProcessor.js +7 -6
  84. package/lib/render/DuplicateChecker.js +1 -1
  85. package/lib/render/manageConstraints.js +36 -33
  86. package/lib/render/toCdl.js +174 -275
  87. package/lib/render/toHdbcds.js +201 -115
  88. package/lib/render/toRename.js +7 -10
  89. package/lib/render/toSql.js +149 -75
  90. package/lib/render/utils/common.js +22 -8
  91. package/lib/render/utils/sql.js +10 -7
  92. package/lib/render/utils/stringEscapes.js +111 -0
  93. package/lib/sql-identifier.js +1 -1
  94. package/lib/transform/.eslintrc.json +5 -0
  95. package/lib/transform/braceExpression.js +4 -2
  96. package/lib/transform/db/.eslintrc.json +2 -0
  97. package/lib/transform/db/applyTransformations.js +35 -12
  98. package/lib/transform/db/assertUnique.js +1 -1
  99. package/lib/transform/db/associations.js +187 -0
  100. package/lib/transform/db/cdsPersistence.js +150 -0
  101. package/lib/transform/db/constraints.js +61 -56
  102. package/lib/transform/db/expansion.js +50 -29
  103. package/lib/transform/db/flattening.js +552 -105
  104. package/lib/transform/db/groupByOrderBy.js +3 -1
  105. package/lib/transform/db/temporal.js +236 -0
  106. package/lib/transform/db/transformExists.js +94 -28
  107. package/lib/transform/db/views.js +5 -4
  108. package/lib/transform/draft/.eslintrc.json +38 -0
  109. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  110. package/lib/transform/draft/odata.js +227 -0
  111. package/lib/transform/forHanaNew.js +94 -801
  112. package/lib/transform/forOdataNew.js +22 -175
  113. package/lib/transform/localized.js +36 -32
  114. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  115. package/lib/transform/odata/referenceFlattener.js +95 -89
  116. package/lib/transform/odata/structureFlattener.js +1 -1
  117. package/lib/transform/odata/toFinalBaseType.js +86 -12
  118. package/lib/transform/odata/typesExposure.js +5 -5
  119. package/lib/transform/odata/utils.js +2 -2
  120. package/lib/transform/transformUtilsNew.js +47 -33
  121. package/lib/transform/translateAssocsToJoins.js +10 -27
  122. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  123. package/lib/transform/universalCsn/coreComputed.js +170 -0
  124. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  125. package/lib/transform/universalCsn/utils.js +63 -0
  126. package/lib/utils/file.js +2 -1
  127. package/lib/utils/objectUtils.js +30 -0
  128. package/lib/utils/timetrace.js +8 -2
  129. package/package.json +1 -1
  130. package/share/messages/README.md +26 -0
  131. package/lib/compiler/definer.js +0 -2340
  132. package/lib/compiler/resolver.js +0 -2988
  133. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -89,6 +89,8 @@
89
89
 
90
90
  const { dictAdd } = require('../base/dictionaries');
91
91
 
92
+ const $location = Symbol.for('cds.$location');
93
+
92
94
  let inExtensions = null;
93
95
 
94
96
  let vocabInDefinitions = null; // must be reset!
@@ -661,6 +663,9 @@ const schema = compileSchema( {
661
663
  ignore: true, type: ignore,
662
664
  },
663
665
  // TODO: should we keep $parens ?
666
+ $generated: {
667
+ type: string,
668
+ },
664
669
  $: { type: ignore, ignore: true }, // including $origin
665
670
  _: { type: ignore, ignore: true },
666
671
  } );
@@ -705,7 +710,7 @@ let csnFilename = '';
705
710
  let virtualLine = 1;
706
711
  /** @type {CSN.Location[]} */
707
712
  let dollarLocations = [];
708
- let arrayLvlCnt = 0;
713
+ let arrayLevelCount = 0;
709
714
 
710
715
  /**
711
716
  * @param {Object.<string, SchemaSpec>} specs
@@ -931,8 +936,10 @@ function definition( def, spec, xsn, csn, name ) {
931
936
  popLocation( def );
932
937
  if (kind !== 'annotation' || prop === 'vocabularies')
933
938
  return r;
934
- if (!vocabInDefinitions)
939
+ if (!vocabInDefinitions) {
935
940
  vocabInDefinitions = Object.create(null);
941
+ vocabInDefinitions[$location] = location();
942
+ }
936
943
  vocabInDefinitions[name] = r; // deprecated: anno def in 'definitions'
937
944
  return undefined;
938
945
 
@@ -957,6 +964,7 @@ function dictionaryOf( elementFct ) {
957
964
  return ignore( dict );
958
965
  }
959
966
  const r = Object.create(null);
967
+ r[$location] = location();
960
968
  const allNames = Object.keys( dict );
961
969
  if (!allNames.length)
962
970
  return r; // {} in one JSON line
@@ -980,6 +988,7 @@ function keys( array, spec, xsn ) {
980
988
  if (!isArray( array, spec ))
981
989
  return;
982
990
  const r = Object.create(null);
991
+ r[$location] = location();
983
992
  ++virtualLine;
984
993
  for (const def of array) {
985
994
  const id = def.as || implicitName( def.ref );
@@ -1164,6 +1173,12 @@ function symbol( id, spec, xsn ) { // for CSN property '#'
1164
1173
  xsn.sym = { id, location: location() };
1165
1174
  }
1166
1175
 
1176
+ // returns: false = no "...", true = "..." without UP TO, 'upTo' = "..." with UP TO
1177
+ function isEllipsis( val ) {
1178
+ return val && typeof val === 'object' && '...' in val && Object.keys(val).length === 1 &&
1179
+ (val['...'] === true || 'upTo');
1180
+ }
1181
+
1167
1182
  function annoValue( val, spec ) {
1168
1183
  if (val == null) // TODO: reject undefined
1169
1184
  return { val, literal: 'null', location: location() };
@@ -1171,22 +1186,40 @@ function annoValue( val, spec ) {
1171
1186
  if (lit !== 'object')
1172
1187
  return { val, literal: lit, location: location() };
1173
1188
  if (Array.isArray( val )) {
1174
- const ec = val.reduce((c, v) => ((v && v['...'] && Object.keys(v).length === 1) ? ++c : c), 0);
1175
- if (arrayLvlCnt === 0 && ec > 1) {
1176
- error( 'syntax-csn-duplicate-ellipsis', location(true), { code: '...' },
1177
- 'Expected no more than one $(CODE)' );
1189
+ /** @type {string|boolean} */
1190
+ let seenEllipsis = false;
1191
+ if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
1192
+ if (val.some( isEllipsis )) {
1193
+ error( 'syntax-csn-unexpected-ellipsis', location(true), { code: '...' },
1194
+ 'Unexpected $(CODE) in nested array' );
1195
+ }
1178
1196
  }
1179
- if (arrayLvlCnt > 0 && ec > 0) {
1180
- error( 'syntax-csn-unexpected-ellipsis', location(true), { code: '...' },
1181
- 'Unexpected $(CODE) in nested array' );
1197
+ else {
1198
+ for (const item of val) {
1199
+ if (seenEllipsis !== true) {
1200
+ seenEllipsis = isEllipsis( item ) || seenEllipsis;
1201
+ }
1202
+ else if (isEllipsis( item )) { // with or without UP TO
1203
+ // error position at the beginning of the array, but that is fine
1204
+ error( 'syntax-csn-duplicate-ellipsis', location(true), { code: '...' },
1205
+ 'Expected no more than one $(CODE)' );
1206
+ break;
1207
+ }
1208
+ }
1182
1209
  }
1183
- arrayLvlCnt++;
1210
+ arrayLevelCount++;
1184
1211
  const retval = {
1185
1212
  location: location(),
1186
1213
  val: arrayOf( annoValue )( val, spec ),
1187
1214
  literal: 'array',
1188
1215
  };
1189
- arrayLvlCnt--;
1216
+ arrayLevelCount--;
1217
+ if (seenEllipsis === 'upTo') {
1218
+ error( 'syntax-csn-expecting-ellipsis', location(true), // at closing bracket
1219
+ { code: '... up to', newcode: '...' },
1220
+ // TODO: should we be more CSN specific in the message?
1221
+ 'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
1222
+ }
1190
1223
  return retval;
1191
1224
  }
1192
1225
  if (typeof val['#'] === 'string') {
@@ -1205,12 +1238,19 @@ function annoValue( val, spec ) {
1205
1238
  return refSplit( val['='], '=' );
1206
1239
  }
1207
1240
  }
1208
- else if (val['...'] && Object.keys(val).length === 1) {
1209
- return {
1241
+ else if (val['...'] && Object.keys( val ).length === 1) {
1242
+ // TODO: only if not nested - see error above
1243
+ ++virtualLine;
1244
+ const ell = val['...'];
1245
+ const r = {
1210
1246
  val: '...',
1211
1247
  literal: 'token',
1212
1248
  location: location(),
1213
1249
  };
1250
+ if (ell !== true)
1251
+ r.upTo = annoValue( ell, schema['@'] );
1252
+ ++virtualLine;
1253
+ return r;
1214
1254
  }
1215
1255
  const struct = Object.create(null);
1216
1256
  ++virtualLine;
@@ -1354,12 +1394,11 @@ function exprArgs( cond, spec, xsn, csn ) {
1354
1394
 
1355
1395
  function condition( cond, spec ) {
1356
1396
  const loc = location();
1357
- const x = {
1397
+ return {
1358
1398
  op: { val: 'xpr', location: loc },
1359
1399
  args: exprArgs( cond, spec ),
1360
1400
  location: loc,
1361
1401
  };
1362
- return x;
1363
1402
  }
1364
1403
 
1365
1404
  function vZeroValue( obj, spec, xsn ) {
@@ -1406,6 +1445,7 @@ function excluding( array, spec, xsn ) {
1406
1445
  if (!isArray( array, spec ))
1407
1446
  return;
1408
1447
  const r = Object.create(null);
1448
+ r[$location] = location();
1409
1449
  ++virtualLine;
1410
1450
  for (const ex of array) {
1411
1451
  const id = string( ex, spec ) || '';
@@ -1666,8 +1706,8 @@ function pushLocation( obj ) {
1666
1706
  error( 'syntax-csn-expected-object', location(true), { prop: '$location' } );
1667
1707
  }
1668
1708
  // hidden feature: string $location
1669
- const m = /:(\d+)(?::(\d+)(?:-[0-9-]+)?)?$/.exec( loc ); // extra - at end for .refloc
1670
- if (!m) {
1709
+ const m = /:(\d+)(?::(\d+))?$/.exec( loc ); // extra '^'s at end deliberately left out
1710
+ if (!m) { // without location or with '^'s: do not use
1671
1711
  dollarLocations.push( null );
1672
1712
  }
1673
1713
  else {
@@ -1710,8 +1750,10 @@ function toXsn( csn, filename, options, messageFunctions ) {
1710
1750
  csnFilename = filename;
1711
1751
  virtualLine = 1;
1712
1752
  dollarLocations = [];
1753
+ arrayLevelCount = 0;
1713
1754
  inExtensions = null;
1714
1755
  vocabInDefinitions = null;
1756
+
1715
1757
  const xsn = { $frontend: 'json' };
1716
1758
 
1717
1759
  // eslint-disable-next-line object-curly-newline
@@ -57,7 +57,7 @@ const transformers = {
57
57
  key: value,
58
58
  unique: value,
59
59
  masked: value,
60
- params: insertOrderDict,
60
+ params,
61
61
  // early expression / query properties -------------------------------------
62
62
  op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
63
63
  from, // before elements!
@@ -100,7 +100,7 @@ const transformers = {
100
100
  offset: expression,
101
101
  on: onCondition,
102
102
  // definitions, extensions, members ----------------------------------------
103
- returns: definition, // storing the return type of actions
103
+ returns, // storing the return type of actions
104
104
  notNull: value,
105
105
  default: expression,
106
106
  // targetElement: ignore, // special display of foreign key, renameTo: select
@@ -185,7 +185,7 @@ const propertyOrder = (function orderPositions() {
185
185
 
186
186
  // sync with definition in from-csn.js:
187
187
  const typeProperties = [
188
- 'target', 'elements', 'enum', 'items',
188
+ 'target', 'elements', 'enum', 'items', // TODO: notNull?
189
189
  'type', 'length', 'precision', 'scale', 'srid', 'localized',
190
190
  'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
191
191
  ];
@@ -204,7 +204,10 @@ const operators = {
204
204
  when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
205
205
  case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
206
206
  over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
207
- orderBy: exprs => [
207
+ orderBy: exprs => [ // ORDER BY in generic functions
208
+ ...exprs[0], ...operators.overOrderBy(exprs.slice(1)),
209
+ ],
210
+ overOrderBy: exprs => [ // ORDER BY in OVER() clause
208
211
  'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
209
212
  ],
210
213
  partitionBy: exprs => [
@@ -277,7 +280,7 @@ function sortCsn( csn, cloneOptions = false ) {
277
280
  }
278
281
 
279
282
  /**
280
- * Check wether the given object has non enumerable property.
283
+ * Check whether the given object has non enumerable property.
281
284
  * Ensure that we don't take it from the prototype, only "directly" - we accidentally
282
285
  * cloned elements with a cds.linked input otherwise.
283
286
  *
@@ -556,7 +559,7 @@ function sources( srcDict, csn ) {
556
559
  }
557
560
  }
558
561
 
559
- function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
562
+ function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
560
563
  const annoDict = Object.create( dictionaryPrototype );
561
564
  for (const name in dict) {
562
565
  const elem = dict[name];
@@ -574,7 +577,7 @@ function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
574
577
  annoDict[name] = sub;
575
578
  }
576
579
  if (Object.keys( annoDict ).length) {
577
- if (returns)
580
+ if (insideReturns)
578
581
  annotate.returns = { elements: annoDict };
579
582
  else
580
583
  annotate[prop] = annoDict;
@@ -621,10 +624,7 @@ function targetAspect( val, csn, node ) {
621
624
  if (val.$inferred)
622
625
  return undefined;
623
626
  if (node.target) {
624
- csn.$origin = { type: 'cds.Composition' };
625
- if (node.cardinality)
626
- csn.$origin.cardinality = standard( node.cardinality );
627
- csn.$origin.target = (val.elements) ? standard( val ) : artifactRef( val, true );
627
+ csn.$origin = { target: (val.elements) ? standard( val ) : artifactRef( val, true ) };
628
628
  return undefined;
629
629
  }
630
630
  }
@@ -643,7 +643,7 @@ function target( val, _csn, node ) {
643
643
  val = node._origin.target;
644
644
  if (val.elements)
645
645
  return standard( val ); // elements in target (parse-cdl)
646
- if (!universalCsn || node.on)
646
+ if (!universalCsn || gensrcFlavor || node.on)
647
647
  return artifactRef( val, true );
648
648
  const tref = artifactRef( val, true );
649
649
  const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
@@ -674,7 +674,8 @@ function enumDict( dict, csn, node ) {
674
674
  // no 'elements' with SELECT or inferred elements with gensrc;
675
675
  // hidden or visible 'elements' will be set in query()
676
676
  return undefined;
677
- if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate')
677
+ if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate' &&
678
+ node.type._artifact && !node.type._artifact.builtin)
678
679
  // derived type of enum type with individual annotations: also set $origin
679
680
  csn.$origin = originRef( node.type._artifact );
680
681
  return insertOrderDict( dict );
@@ -778,7 +779,7 @@ function addLocation( loc, csn ) {
778
779
  // Remove endLine/endCol:
779
780
  // Reasoning: $location is mostly attached to definitions/members but the name
780
781
  // is often not the reason for an error or warning. So we gain little benefit for
781
- // two more properties.
782
+ // two more properties. It is also an indication that the location is not exact.
782
783
  const val = { file: loc.file, line: loc.line, col: loc.col };
783
784
  Object.defineProperty( csn, '$location', {
784
785
  value: val, configurable: true, writable: true, enumerable: withLocations,
@@ -806,6 +807,13 @@ function actions( dict ) {
806
807
  : undefined;
807
808
  }
808
809
 
810
+ function params( dict ) {
811
+ const keys = Object.keys( dict );
812
+ return (keys.length)
813
+ ? insertOrderDict( dict )
814
+ : undefined;
815
+ }
816
+
809
817
  function dictionary( dict, keys, prop ) {
810
818
  const csn = Object.create( dictionaryPrototype );
811
819
  for (const name of keys) {
@@ -834,6 +842,14 @@ function foreignKeys( dict, csn, node ) {
834
842
  csn.keys = keys;
835
843
  }
836
844
 
845
+ function returns( art, csn, _node, prop ) {
846
+ // TODO: currently, the `returns` structure might just have been created by the propagator
847
+ // if that is the case, there should be no reason to store it in universal CSN
848
+ if (universalCsn && art.$inferred === 'proxy')
849
+ return undefined;
850
+ return definition( art, csn, _node, prop );
851
+ }
852
+
837
853
  function definition( art, _csn, _node, prop ) {
838
854
  if (!art || typeof art !== 'object')
839
855
  return undefined; // TODO: complain with strict
@@ -854,7 +870,7 @@ function definition( art, _csn, _node, prop ) {
854
870
  c.returns = { elements: elems };
855
871
  }
856
872
  // precondition already fulfilled: art.kind !== 'key'
857
- addOrigin( c, art, art._origin );
873
+ addOrigin( c, art, art );
858
874
  return c;
859
875
  }
860
876
 
@@ -867,7 +883,8 @@ function includesOrigin( includes, art ) {
867
883
  for (const incl of includes.slice(1)) {
868
884
  const aspect = incl._artifact;
869
885
  for (const prop in aspect) {
870
- if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
886
+ if ((prop.charAt(0) === '@' || prop === 'doc') &&
887
+ (!art[prop] || art[prop].$inferred)) {
871
888
  const anno = aspect[prop];
872
889
  if (anno.val !== null)
873
890
  // matererialize non-null annos (whether direct or inherited)
@@ -878,9 +895,23 @@ function includesOrigin( includes, art ) {
878
895
  return (Object.keys( result ).length === 1) ? $origin : result;
879
896
  }
880
897
 
881
- function addOrigin( csn, xsn, origin ) {
882
- if (!universalCsn || hasExplicitProp( xsn.type ))
898
+ function addOrigin( csn, xsn, node ) {
899
+ if (!universalCsn)
900
+ return;
901
+ if (hasExplicitProp( xsn.type, 'cast' )) {
902
+ const main = xsn._main || xsn;
903
+ let count = 0;
904
+ let source = xsn;
905
+ while (source && source._main === main) {
906
+ source = source.value && source.value._artifact;
907
+ ++count;
908
+ }
909
+ if (count > 0 && source && source.kind !== 'builtin')
910
+ csn.$source = originRef( source, xsn );
911
+ else if (count > 1)
912
+ csn.$source = null;
883
913
  return;
914
+ }
884
915
  if (xsn._from) {
885
916
  const source = xsn._from[0]._origin;
886
917
  csn.$origin = originRef( source );
@@ -894,23 +925,22 @@ function addOrigin( csn, xsn, origin ) {
894
925
  csn.$origin = includesOrigin( xsn.includes, xsn );
895
926
  return;
896
927
  }
897
- else if (!xsn._main || xsn.kind === 'select') {
928
+ let origin = getOrigin( node );
929
+ if (xsn.$inferred === 'composition-entity') {
930
+ csn.$origin = originRef( origin, xsn );
931
+ return;
932
+ }
933
+ else if (!isMember( xsn ) || xsn.kind === 'select') {
898
934
  return;
899
935
  }
936
+ // from here on: member:
900
937
  const parent = getParent( xsn );
901
938
  const parentOrigin = getOrigin( parent );
902
- if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
903
- if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
939
+ if (!origin) {
940
+ if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
904
941
  csn.$origin = null;
905
942
  return;
906
943
  }
907
- // Skip all proxies which do not make it into the CSN, as there are no
908
- // individual annotations or redirection targets on it:
909
- while (origin._parent && origin._parent.$expand === 'origin')
910
- origin = origin._origin || origin.type._artifact;
911
- // The while loop is not only for the else case below: when setting implicit
912
- // prototypes, it is important that we do not have to follow the prototypes of
913
- // other object; we would need to ensure a right order to avoid issues otherwise.
914
944
  if (parentOrigin === getParent( origin )) {
915
945
  // implicit prototype or shortened reference
916
946
  const { id } = origin.name || {};
@@ -919,16 +949,21 @@ function addOrigin( csn, xsn, origin ) {
919
949
  return;
920
950
  }
921
951
  if (origin.kind === 'mixin') {
922
- // currently, target and on are always set - nothing to do here, just set type
923
- csn.type = 'cds.Association';
952
+ set( 'type', csn, origin );
953
+ set( 'cardinality', csn, origin );
954
+ // currently, target and on are always set - nothing to do here
924
955
  return;
925
956
  }
957
+ // Skip all proxies which do not make it into the CSN, as there are no
958
+ // individual annotations or redirection targets on it:
959
+ while (origin._parent && origin._parent.$expand === 'origin')
960
+ origin = origin._origin || origin.type._artifact;
926
961
  const ref = originRef( origin, xsn );
927
962
  if (ref) {
928
963
  csn.$origin = ref;
929
964
  return;
930
965
  }
931
- // An element of a query with a query in FROM:
966
+ // An element of a query with a query in FROM: -----------------------------
932
967
  const anon = definition( origin ); // use $origin: {...} if necessary
933
968
  // as there are no implicit $origin prototypes on sub query elements (yet),
934
969
  // we do not have to care about $origin not being set
@@ -936,15 +971,26 @@ function addOrigin( csn, xsn, origin ) {
936
971
  if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
937
972
  // repeated anon: flatten
938
973
  csn.$origin = Object.assign( $origin, anon );
974
+ return;
975
+ }
976
+ // Annotations and 'doc' must keep the distinction between direct or inherited,
977
+ // other properties can as well be set as direct element properties
978
+ const annos = {};
979
+ for (const prop of Object.keys( anon )) {
980
+ if (prop.charAt(0) === '@' || prop === 'doc')
981
+ annos[prop] = anon[prop];
982
+ else if (prop === '$source')
983
+ csn[prop] = anon[prop]; // overwrite from inner
984
+ else if (prop !== '$location' && prop !== '$origin' && !(prop in csn))
985
+ csn[prop] = anon[prop];
939
986
  }
940
- else if (Object.keys( anon )
941
- // (we can use the properties in `csn`, because addOrigin() is called last)
942
- .every( p => p in csn || p === '$origin' || p === '$location')) {
943
- // nothing new in $origin: {...}
944
- addOrigin( csn, xsn, origin._origin );
987
+ if (Object.keys( annos ).length) {
988
+ if (!csn.type && $origin)
989
+ annos.$origin = $origin;
990
+ csn.$origin = annos;
945
991
  }
946
- else {
947
- csn.$origin = anon;
992
+ else if (!csn.type) {
993
+ addOrigin( csn, xsn, origin );
948
994
  }
949
995
  }
950
996
 
@@ -954,10 +1000,27 @@ function getParent( art ) {
954
1000
  return (main && parent === main._leadingQuery) ? main : parent;
955
1001
  }
956
1002
 
1003
+ function isMember( art ) {
1004
+ // TODO: introduce art.kind = '$aspect' for anonymous aspect (is a member) ?
1005
+ return !!(art._main || art._outer);
1006
+ }
1007
+
1008
+ // function getDefinition( art ) {
1009
+ // let main = art._main || art;
1010
+ // while (main._outer) // anonymous aspect
1011
+ // main = main._outer._main;
1012
+ // return main;
1013
+ // }
1014
+
1015
+ // XSN `_origin` is (currently?) not the same as _origin in Universal CSN...
1016
+ // TODO: at least with expand, set it correctly (alias: keep, assoc: to entity, $builtin: no)
957
1017
  function getOrigin( art ) {
958
- if (art._origin)
959
- return art._origin;
960
- if (hasExplicitProp( art.type ))
1018
+ if (art.$noOrigin)
1019
+ return undefined;
1020
+ const { _origin } = art;
1021
+ if (_origin)
1022
+ return (_origin.kind === 'builtin') ? undefined : _origin; // not $dollarVariable
1023
+ if (hasExplicitProp( art.type, 'cast' ))
961
1024
  return art.type._artifact;
962
1025
  if (art.includes)
963
1026
  return art.includes[0]._artifact;
@@ -966,31 +1029,31 @@ function getOrigin( art ) {
966
1029
  return undefined;
967
1030
  }
968
1031
 
969
- function hasExplicitProp( ref ) {
970
- return ref && !ref.$inferred;
1032
+ function hasExplicitProp( ref, alsoLikeExplicit ) {
1033
+ return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
971
1034
  }
972
1035
 
973
1036
  function originRef( art, user ) {
974
1037
  const r = [];
975
- // do not use name.element, as we allow `.`s in name
1038
+ // do not use name.element, as we might allow `.`s in name
976
1039
  let parent = art;
977
- while (parent._main && parent.kind !== 'select') {
1040
+ if (parent._outer && parent.kind === 'aspect')
1041
+ r.push( { target: true } );
1042
+ while (isMember( parent ) && parent.kind !== 'select') {
978
1043
  const nkind = normalizedKind[parent.kind];
979
- if (parent.name.id || !r.length)
1044
+ const name = parent.name || parent._outer.name;
1045
+ if (name.id || !r.length)
980
1046
  // Return parameter is in XSN - kind: 'param', name.id: ''
981
1047
  // eslint-disable-next-line no-nested-ternary, max-len
982
- r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
1048
+ r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
983
1049
  parent = parent._parent;
984
1050
  }
985
1051
  if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
986
1052
  // well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
987
- return null; // probably use $origin: {...}
1053
+ return false; // do not write, probably use $origin: {...}
988
1054
  // for sub query in FROM in sub query in FROM, we could condense the info
989
1055
 
990
- // Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
991
- if (r.length === 1 && normalizedKind[art.kind] === 'action')
992
- return [ art.name.absolute, art.name.id ];
993
- r.push( art.name.absolute );
1056
+ r.push( parent.name.absolute );
994
1057
  r.reverse();
995
1058
  return r;
996
1059
  }
@@ -1009,6 +1072,9 @@ function kind( k, csn, node ) {
1009
1072
  else if (k === 'action' && node._main && universalCsn && node.$inferred) {
1010
1073
  // Universal CSN: do not mention kind: 'action' on expanded action
1011
1074
  }
1075
+ else if (k === 'aspect' && (node._outer || node.$inferred)) {
1076
+ return; // do not show kind for anonymous aspect
1077
+ }
1012
1078
  else if (![
1013
1079
  'element', 'key', 'param', 'enum', 'select', '$join',
1014
1080
  '$tableAlias', 'annotation', 'mixin',
@@ -1023,26 +1089,20 @@ function kind( k, csn, node ) {
1023
1089
  function type( node, csn, xsn ) {
1024
1090
  if (!universalCsn)
1025
1091
  return artifactRef( node, !node.$extra );
1026
- if (node.$inferred)
1092
+ if (node.$inferred && node.$inferred !== 'cast')
1027
1093
  return undefined;
1028
1094
  if (xsn._origin) {
1029
1095
  if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
1030
1096
  csn.$origin = definition( xsn._origin );
1031
- return undefined;
1032
1097
  }
1033
1098
  }
1034
- else if ( xsn.targetAspect && xsn.target ) {
1035
- // type moved to $origin: { type: … }
1036
- return undefined;
1037
- }
1038
1099
  return artifactRef( node, !node.$extra );
1039
1100
  }
1040
1101
 
1041
- function cardinality( node, csn, xsn ) {
1102
+ function cardinality( node ) {
1042
1103
  if (!universalCsn)
1043
1104
  return standard( node );
1044
- // cardinality might be moved to $origin: { cardinality: … }
1045
- if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
1105
+ if (node.$inferred)
1046
1106
  return undefined;
1047
1107
  return standard( node );
1048
1108
  }
@@ -1150,8 +1210,12 @@ function value( node ) {
1150
1210
  // "Short" value form, e.g. for annotation assignments
1151
1211
  if (!node)
1152
1212
  return true; // `@aBool` short for `@aBool: true`
1153
- if (universalCsn && node.$inferred === 'prop') // via propagator.js
1154
- return undefined;
1213
+ if (universalCsn && node.$inferred) {
1214
+ if (node.$inferred === 'prop' || node.$inferred === '$generated') // via propagator.js
1215
+ return undefined;
1216
+ else if (node.$inferred === 'NULL')
1217
+ return null;
1218
+ }
1155
1219
  if (node.$inferred && gensrcFlavor)
1156
1220
  return undefined;
1157
1221
  if (node.path) {
@@ -1163,7 +1227,7 @@ function value( node ) {
1163
1227
  if (node.literal === 'array')
1164
1228
  return node.val.map( value );
1165
1229
  if (node.literal === 'token' && node.val === '...')
1166
- return extra( { '...': true } );
1230
+ return extra( { '...': !node.upTo || value( node.upTo ) } );
1167
1231
  if (node.literal !== 'struct')
1168
1232
  // no val (undefined) as true only for annotation values (and struct elem values)
1169
1233
  return node.name && !('val' in node) || node.val;
@@ -1320,7 +1384,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1320
1384
  if (node.op.val === 'SELECT') {
1321
1385
  if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
1322
1386
  node.from && node.from.path && !projectionAsQuery) {
1323
- csn.projection = standard( node );
1387
+ csn.projection = addLocation( node.location, standard( node ) );
1324
1388
  return undefined;
1325
1389
  }
1326
1390
  const select = { SELECT: extra( standard( node ), node, expectedParens ) };
@@ -1342,6 +1406,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1342
1406
  gensrcFlavor = gensrcSaved;
1343
1407
  }
1344
1408
  }
1409
+ // the $location is better put inside the SELECT value, not as sibling (but
1410
+ // we keep it as sibling also for compatibility):
1411
+ addLocation( node.location, select.SELECT );
1345
1412
  return addLocation( node.location, select );
1346
1413
  }
1347
1414
  const union = {};
@@ -1390,11 +1457,13 @@ function from( node ) {
1390
1457
  else if (node.query) {
1391
1458
  return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
1392
1459
  }
1393
- else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
1394
- return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
1395
- }
1396
- return extra( addExplicitAs( artifactRef( node, false ), node.name, (id) => {
1397
- const name = node._artifact.name.absolute;
1460
+
1461
+ const ref = artifactRef( node, false );
1462
+ return extra( addExplicitAs( ref, node.name, (id) => {
1463
+ let name = ref.ref ? ref.ref[ref.ref.length - 1] : ref;
1464
+ name = name && name.id || name;
1465
+ if (!name)
1466
+ return false;
1398
1467
  const dot = name.lastIndexOf('.');
1399
1468
  return name.substring( dot + 1 ) !== id;
1400
1469
  }), node );
@@ -1443,7 +1512,7 @@ function addElementAsColumn( elem, cols ) {
1443
1512
  }
1444
1513
 
1445
1514
  function orderBy( node ) {
1446
- const expr = expression( node, 'ignoreExtra' );
1515
+ const expr = expression( node );
1447
1516
  if (node.sort)
1448
1517
  expr.sort = node.sort.val;
1449
1518
  if (node.nulls)
@@ -1516,6 +1585,9 @@ function compactExpr( e ) { // TODO: options
1516
1585
  return e && expression( e, true );
1517
1586
  }
1518
1587
 
1588
+ /**
1589
+ * @param {CSN.Options} options
1590
+ */
1519
1591
  function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
1520
1592
  gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
1521
1593
  options.toCsn && options.toCsn.flavor === 'gensrc';
@@ -112,7 +112,7 @@ const rules = {
112
112
  expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
113
113
  };
114
114
 
115
- function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions, rule = 'cdl' ) {
115
+ function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions = null, rule = 'cdl' ) {
116
116
  const lexer = new Lexer( new antlr4.InputStream(source) );
117
117
  const tokenStream = new RewriteTypeTokenStream(lexer);
118
118
  /** @type {object} */
@@ -166,8 +166,8 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
166
166
  // Do not warn if docComments are explicitly disabled.
167
167
  if (options.docComment !== false) {
168
168
  for (const token of tokenStream.tokens) {
169
- if (token.channel === antlr4.Token.HIDDEN_CHANNEL && token.type === parser.constructor.DocComment && !token.isUsed) {
170
- messageFunctions.info('syntax-ignoring-doc-comment', parser.multiLineTokenLocation(token), {},
169
+ if (token.type === parser.constructor.DocComment && !token.isUsed) {
170
+ messageFunctions.info('syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
171
171
  "Ignoring doc-comment as it does not belong to any artifact");
172
172
  }
173
173
  }
@@ -69,7 +69,7 @@ function parseDocComment(comment) {
69
69
  * @param {string} content
70
70
  */
71
71
  function isWhiteSpaceOnly(content) {
72
- return content.trim().length === 0;
72
+ return /^\s*$/.test(content);
73
73
  }
74
74
 
75
75
  /**
@@ -127,8 +127,8 @@ function removeFooterFence(line) {
127
127
  * @param {string[]} lines
128
128
  */
129
129
  function isFencedComment(lines) {
130
- const index = lines.findIndex((line, index) => {
131
- const exclude = (index === 0 || index === lines.length - 1);
130
+ const index = lines.findIndex((line, i) => {
131
+ const exclude = (i === 0 || i === lines.length - 1);
132
132
  return !exclude && !(/^\s*[*]/.test(line));
133
133
  });
134
134
  return index === -1 && lines.length > 2;