@sap/cds-compiler 2.11.2 → 2.13.6

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 (140) hide show
  1. package/CHANGELOG.md +175 -2
  2. package/bin/.eslintrc.json +1 -2
  3. package/bin/cds_update_identifiers.js +10 -8
  4. package/bin/cdsc.js +23 -17
  5. package/bin/cdsse.js +2 -2
  6. package/bin/cdsv2m.js +3 -2
  7. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  8. package/doc/CHANGELOG_BETA.md +25 -6
  9. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  10. package/doc/NameResolution.md +21 -16
  11. package/lib/api/main.js +32 -79
  12. package/lib/api/options.js +3 -2
  13. package/lib/api/validate.js +2 -1
  14. package/lib/backends.js +16 -26
  15. package/lib/base/dictionaries.js +0 -8
  16. package/lib/base/error.js +26 -0
  17. package/lib/base/keywords.js +10 -19
  18. package/lib/base/location.js +9 -4
  19. package/lib/base/message-registry.js +75 -9
  20. package/lib/base/messages.js +31 -35
  21. package/lib/base/model.js +2 -62
  22. package/lib/base/optionProcessorHelper.js +246 -183
  23. package/lib/checks/.eslintrc.json +2 -0
  24. package/lib/checks/actionsFunctions.js +2 -1
  25. package/lib/checks/annotationsOData.js +1 -1
  26. package/lib/checks/cdsPersistence.js +2 -1
  27. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  28. package/lib/checks/enricher.js +17 -1
  29. package/lib/checks/foreignKeys.js +4 -4
  30. package/lib/checks/invalidTarget.js +3 -1
  31. package/lib/checks/managedInType.js +4 -4
  32. package/lib/checks/managedWithoutKeys.js +3 -1
  33. package/lib/checks/queryNoDbArtifacts.js +1 -3
  34. package/lib/checks/selectItems.js +4 -4
  35. package/lib/checks/sql-snippets.js +94 -0
  36. package/lib/checks/types.js +1 -1
  37. package/lib/checks/unknownMagic.js +1 -1
  38. package/lib/checks/validator.js +12 -7
  39. package/lib/compiler/assert-consistency.js +12 -8
  40. package/lib/compiler/base.js +0 -1
  41. package/lib/compiler/builtins.js +42 -21
  42. package/lib/compiler/checks.js +46 -12
  43. package/lib/compiler/cycle-detector.js +1 -1
  44. package/lib/compiler/define.js +1103 -0
  45. package/lib/compiler/extend.js +983 -0
  46. package/lib/compiler/finalize-parse-cdl.js +231 -0
  47. package/lib/compiler/index.js +46 -39
  48. package/lib/compiler/kick-start.js +190 -0
  49. package/lib/compiler/moduleLayers.js +4 -4
  50. package/lib/compiler/populate.js +1226 -0
  51. package/lib/compiler/propagator.js +113 -47
  52. package/lib/compiler/resolve.js +1433 -0
  53. package/lib/compiler/shared.js +100 -65
  54. package/lib/compiler/tweak-assocs.js +529 -0
  55. package/lib/compiler/utils.js +215 -33
  56. package/lib/edm/.eslintrc.json +5 -0
  57. package/lib/edm/annotations/genericTranslation.js +38 -25
  58. package/lib/edm/annotations/preprocessAnnotations.js +3 -3
  59. package/lib/edm/csn2edm.js +10 -9
  60. package/lib/edm/edm.js +19 -20
  61. package/lib/edm/edmPreprocessor.js +166 -95
  62. package/lib/edm/edmUtils.js +127 -34
  63. package/lib/gen/Dictionary.json +92 -43
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +11 -1
  66. package/lib/gen/language.tokens +86 -82
  67. package/lib/gen/languageLexer.interp +18 -1
  68. package/lib/gen/languageLexer.js +925 -847
  69. package/lib/gen/languageLexer.tokens +78 -74
  70. package/lib/gen/languageParser.js +5434 -4298
  71. package/lib/json/from-csn.js +59 -17
  72. package/lib/json/to-csn.js +189 -71
  73. package/lib/language/antlrParser.js +3 -3
  74. package/lib/language/docCommentParser.js +3 -3
  75. package/lib/language/errorStrategy.js +26 -8
  76. package/lib/language/genericAntlrParser.js +144 -53
  77. package/lib/language/language.g4 +424 -200
  78. package/lib/language/multiLineStringParser.js +536 -0
  79. package/lib/main.d.ts +550 -61
  80. package/lib/main.js +38 -11
  81. package/lib/model/api.js +3 -1
  82. package/lib/model/csnRefs.js +322 -198
  83. package/lib/model/csnUtils.js +226 -370
  84. package/lib/model/enrichCsn.js +124 -69
  85. package/lib/model/revealInternalProperties.js +29 -7
  86. package/lib/model/sortViews.js +10 -2
  87. package/lib/modelCompare/compare.js +17 -12
  88. package/lib/optionProcessor.js +8 -3
  89. package/lib/render/.eslintrc.json +1 -2
  90. package/lib/render/DuplicateChecker.js +1 -1
  91. package/lib/render/manageConstraints.js +36 -33
  92. package/lib/render/toCdl.js +174 -275
  93. package/lib/render/toHdbcds.js +203 -122
  94. package/lib/render/toRename.js +7 -10
  95. package/lib/render/toSql.js +161 -82
  96. package/lib/render/utils/common.js +22 -8
  97. package/lib/render/utils/sql.js +10 -7
  98. package/lib/render/utils/stringEscapes.js +111 -0
  99. package/lib/sql-identifier.js +1 -1
  100. package/lib/transform/.eslintrc.json +5 -0
  101. package/lib/transform/braceExpression.js +4 -2
  102. package/lib/transform/db/.eslintrc.json +2 -0
  103. package/lib/transform/db/applyTransformations.js +212 -0
  104. package/lib/transform/db/assertUnique.js +1 -1
  105. package/lib/transform/db/associations.js +187 -0
  106. package/lib/transform/db/cdsPersistence.js +150 -0
  107. package/lib/transform/db/constraints.js +61 -56
  108. package/lib/transform/db/expansion.js +50 -29
  109. package/lib/transform/db/flattening.js +556 -106
  110. package/lib/transform/db/groupByOrderBy.js +3 -1
  111. package/lib/transform/db/temporal.js +236 -0
  112. package/lib/transform/db/transformExists.js +103 -28
  113. package/lib/transform/db/views.js +92 -44
  114. package/lib/transform/draft/.eslintrc.json +38 -0
  115. package/lib/transform/{db/draft.js → draft/db.js} +9 -7
  116. package/lib/transform/draft/odata.js +227 -0
  117. package/lib/transform/forHanaNew.js +98 -783
  118. package/lib/transform/forOdataNew.js +22 -175
  119. package/lib/transform/localized.js +36 -32
  120. package/lib/transform/odata/generateForeignKeyElements.js +3 -3
  121. package/lib/transform/odata/referenceFlattener.js +95 -89
  122. package/lib/transform/odata/structureFlattener.js +1 -1
  123. package/lib/transform/odata/toFinalBaseType.js +86 -12
  124. package/lib/transform/odata/typesExposure.js +5 -5
  125. package/lib/transform/odata/utils.js +2 -2
  126. package/lib/transform/transformUtilsNew.js +47 -33
  127. package/lib/transform/translateAssocsToJoins.js +13 -30
  128. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  129. package/lib/transform/universalCsn/coreComputed.js +170 -0
  130. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  131. package/lib/transform/universalCsn/utils.js +63 -0
  132. package/lib/utils/file.js +8 -3
  133. package/lib/utils/objectUtils.js +30 -0
  134. package/lib/utils/timetrace.js +8 -2
  135. package/package.json +1 -1
  136. package/share/messages/README.md +26 -0
  137. package/lib/compiler/definer.js +0 -2349
  138. package/lib/compiler/resolver.js +0 -2922
  139. package/lib/transform/db/helpers.js +0 -58
  140. package/lib/transform/universalCsnEnricher.js +0 -67
@@ -37,6 +37,14 @@ let dictionaryPrototype = null;
37
37
  // stored with symbols as keys, as we do not want to disallow any key name:
38
38
  const $inferred = Symbol.for('cds.$inferred');
39
39
 
40
+ // XSN $inferred values mapped to Universal CSN $generated values:
41
+ const inferredAsGenerated = {
42
+ autoexposed: 'exposed',
43
+ 'localized-entity': 'localized',
44
+ localized: 'localized', // on elements (texts, localized)
45
+ 'composition-entity': 'composed', // ('aspect-composition' on element not in CSN)
46
+ };
47
+
40
48
  // IMPORTANT: the order of these properties determine the order of properties
41
49
  // in the resulting CSN !!! Also check const `csnPropertyNames`.
42
50
  const transformers = {
@@ -49,7 +57,7 @@ const transformers = {
49
57
  key: value,
50
58
  unique: value,
51
59
  masked: value,
52
- params: insertOrderDict,
60
+ params,
53
61
  // early expression / query properties -------------------------------------
54
62
  op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
55
63
  from, // before elements!
@@ -66,7 +74,7 @@ const transformers = {
66
74
  precision: value,
67
75
  scale: value,
68
76
  srid: value,
69
- cardinality: standard, // also for pathItem: after 'id', before 'where'
77
+ cardinality, // also in pathItem: after 'id', before 'where'
70
78
  targetAspect,
71
79
  target,
72
80
  foreignKeys,
@@ -92,7 +100,7 @@ const transformers = {
92
100
  offset: expression,
93
101
  on: onCondition,
94
102
  // definitions, extensions, members ----------------------------------------
95
- returns: definition, // storing the return type of actions
103
+ returns, // storing the return type of actions
96
104
  notNull: value,
97
105
  default: expression,
98
106
  // targetElement: ignore, // special display of foreign key, renameTo: select
@@ -177,7 +185,7 @@ const propertyOrder = (function orderPositions() {
177
185
 
178
186
  // sync with definition in from-csn.js:
179
187
  const typeProperties = [
180
- 'target', 'elements', 'enum', 'items',
188
+ 'target', 'elements', 'enum', 'items', // TODO: notNull?
181
189
  'type', 'length', 'precision', 'scale', 'srid', 'localized',
182
190
  'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
183
191
  ];
@@ -196,7 +204,10 @@ const operators = {
196
204
  when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
197
205
  case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
198
206
  over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
199
- 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
200
211
  'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
201
212
  ],
202
213
  partitionBy: exprs => [
@@ -269,7 +280,7 @@ function sortCsn( csn, cloneOptions = false ) {
269
280
  }
270
281
 
271
282
  /**
272
- * Check wether the given object has non enumerable property.
283
+ * Check whether the given object has non enumerable property.
273
284
  * Ensure that we don't take it from the prototype, only "directly" - we accidentally
274
285
  * cloned elements with a cds.linked input otherwise.
275
286
  *
@@ -548,7 +559,7 @@ function sources( srcDict, csn ) {
548
559
  }
549
560
  }
550
561
 
551
- function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
562
+ function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
552
563
  const annoDict = Object.create( dictionaryPrototype );
553
564
  for (const name in dict) {
554
565
  const elem = dict[name];
@@ -566,7 +577,7 @@ function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
566
577
  annoDict[name] = sub;
567
578
  }
568
579
  if (Object.keys( annoDict ).length) {
569
- if (returns)
580
+ if (insideReturns)
570
581
  annotate.returns = { elements: annoDict };
571
582
  else
572
583
  annotate[prop] = annoDict;
@@ -609,10 +620,18 @@ function set( prop, csn, node ) {
609
620
  }
610
621
 
611
622
  function targetAspect( val, csn, node ) {
623
+ if (universalCsn) {
624
+ if (val.$inferred)
625
+ return undefined;
626
+ if (node.target) {
627
+ csn.$origin = { target: (val.elements) ? standard( val ) : artifactRef( val, true ) };
628
+ return undefined;
629
+ }
630
+ }
612
631
  const ta = (val.elements)
613
632
  ? addLocation( val.location, standard( val ) )
614
633
  : artifactRef( val, true );
615
- if (!gensrcFlavor || node.target && !node.target.$inferred)
634
+ if (!gensrcFlavor && !universalCsn || node.target && !node.target.$inferred)
616
635
  return ta;
617
636
  // For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
618
637
  csn.target = ta;
@@ -624,7 +643,7 @@ function target( val, _csn, node ) {
624
643
  val = node._origin.target;
625
644
  if (val.elements)
626
645
  return standard( val ); // elements in target (parse-cdl)
627
- if (!universalCsn || node.on)
646
+ if (!universalCsn || gensrcFlavor || node.on)
628
647
  return artifactRef( val, true );
629
648
  const tref = artifactRef( val, true );
630
649
  const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
@@ -655,19 +674,15 @@ function enumDict( dict, csn, node ) {
655
674
  // no 'elements' with SELECT or inferred elements with gensrc;
656
675
  // hidden or visible 'elements' will be set in query()
657
676
  return undefined;
658
- 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)
659
679
  // derived type of enum type with individual annotations: also set $origin
660
680
  csn.$origin = originRef( node.type._artifact );
661
681
  return insertOrderDict( dict );
662
682
  }
663
683
 
664
684
  function enumerableQueryElements( select ) {
665
- if (!universalCsn || select === select._main._leadingQuery)
666
- return false;
667
- if (select.orderBy || select.$orderBy)
668
- return true;
669
- const alias = select._parent;
670
- return alias.query && (alias.query._leadingQuery || alias.query) === select;
685
+ return (universalCsn && select !== select._main._leadingQuery);
671
686
  }
672
687
 
673
688
  // Should we render the elements? (and items?)
@@ -746,7 +761,8 @@ function ignore() { /* no-op: ignore property */ }
746
761
 
747
762
  function location( loc, csn, xsn ) {
748
763
  if (xsn.kind && xsn.kind.charAt(0) !== '$' && xsn.kind !== 'select' &&
749
- (!xsn.$inferred || !xsn._main)) { // TODO: also for 'select'
764
+ (!xsn.$inferred || !xsn._main) &&
765
+ xsn.$inferred !== 'REDIRECTED') { // TODO: also for 'select'
750
766
  // Also include $location for elements in queries (if not via '*')
751
767
  addLocation( xsn.name && xsn.name.location || loc, csn );
752
768
  }
@@ -763,7 +779,7 @@ function addLocation( loc, csn ) {
763
779
  // Remove endLine/endCol:
764
780
  // Reasoning: $location is mostly attached to definitions/members but the name
765
781
  // is often not the reason for an error or warning. So we gain little benefit for
766
- // two more properties.
782
+ // two more properties. It is also an indication that the location is not exact.
767
783
  const val = { file: loc.file, line: loc.line, col: loc.col };
768
784
  Object.defineProperty( csn, '$location', {
769
785
  value: val, configurable: true, writable: true, enumerable: withLocations,
@@ -791,6 +807,13 @@ function actions( dict ) {
791
807
  : undefined;
792
808
  }
793
809
 
810
+ function params( dict ) {
811
+ const keys = Object.keys( dict );
812
+ return (keys.length)
813
+ ? insertOrderDict( dict )
814
+ : undefined;
815
+ }
816
+
794
817
  function dictionary( dict, keys, prop ) {
795
818
  const csn = Object.create( dictionaryPrototype );
796
819
  for (const name of keys) {
@@ -802,8 +825,10 @@ function dictionary( dict, keys, prop ) {
802
825
  }
803
826
 
804
827
  function foreignKeys( dict, csn, node ) {
805
- if (universalCsn && !target( node.target, csn, node ))
806
- return;
828
+ if (universalCsn) {
829
+ if (!target( node.target, csn, node ) || dict[$inferred] === 'keys')
830
+ return;
831
+ }
807
832
  if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
808
833
  dict = node._origin.foreignKeys;
809
834
  const keys = [];
@@ -817,6 +842,14 @@ function foreignKeys( dict, csn, node ) {
817
842
  csn.keys = keys;
818
843
  }
819
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
+
820
853
  function definition( art, _csn, _node, prop ) {
821
854
  if (!art || typeof art !== 'object')
822
855
  return undefined; // TODO: complain with strict
@@ -836,8 +869,8 @@ function definition( art, _csn, _node, prop ) {
836
869
  delete c.elements;
837
870
  c.returns = { elements: elems };
838
871
  }
839
- if (kind && kind !== 'key')
840
- addOrigin( c, art, art._origin );
872
+ // precondition already fulfilled: art.kind !== 'key'
873
+ addOrigin( c, art, art );
841
874
  return c;
842
875
  }
843
876
 
@@ -850,7 +883,8 @@ function includesOrigin( includes, art ) {
850
883
  for (const incl of includes.slice(1)) {
851
884
  const aspect = incl._artifact;
852
885
  for (const prop in aspect) {
853
- if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
886
+ if ((prop.charAt(0) === '@' || prop === 'doc') &&
887
+ (!art[prop] || art[prop].$inferred)) {
854
888
  const anno = aspect[prop];
855
889
  if (anno.val !== null)
856
890
  // matererialize non-null annos (whether direct or inherited)
@@ -861,9 +895,23 @@ function includesOrigin( includes, art ) {
861
895
  return (Object.keys( result ).length === 1) ? $origin : result;
862
896
  }
863
897
 
864
- function addOrigin( csn, xsn, origin ) {
865
- 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;
866
913
  return;
914
+ }
867
915
  if (xsn._from) {
868
916
  const source = xsn._from[0]._origin;
869
917
  csn.$origin = originRef( source );
@@ -877,23 +925,22 @@ function addOrigin( csn, xsn, origin ) {
877
925
  csn.$origin = includesOrigin( xsn.includes, xsn );
878
926
  return;
879
927
  }
880
- 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') {
881
934
  return;
882
935
  }
936
+ // from here on: member:
883
937
  const parent = getParent( xsn );
884
938
  const parentOrigin = getOrigin( parent );
885
- if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
886
- if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
939
+ if (!origin) {
940
+ if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
887
941
  csn.$origin = null;
888
942
  return;
889
943
  }
890
- // Skip all proxies which do not make it into the CSN, as there are no
891
- // individual annotations or redirection targets on it:
892
- while (origin._parent && origin._parent.$expand === 'origin')
893
- origin = origin._origin || origin.type._artifact;
894
- // The while loop is not only for the else case below: when setting implicit
895
- // prototypes, it is important that we do not have to follow the prototypes of
896
- // other object; we would need to ensure a right order to avoid issues otherwise.
897
944
  if (parentOrigin === getParent( origin )) {
898
945
  // implicit prototype or shortened reference
899
946
  const { id } = origin.name || {};
@@ -902,16 +949,21 @@ function addOrigin( csn, xsn, origin ) {
902
949
  return;
903
950
  }
904
951
  if (origin.kind === 'mixin') {
905
- // currently, target and on are always set - nothing to do here, just set type
906
- 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
907
955
  return;
908
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;
909
961
  const ref = originRef( origin, xsn );
910
962
  if (ref) {
911
963
  csn.$origin = ref;
912
964
  return;
913
965
  }
914
- // An element of a query with a query in FROM:
966
+ // An element of a query with a query in FROM: -----------------------------
915
967
  const anon = definition( origin ); // use $origin: {...} if necessary
916
968
  // as there are no implicit $origin prototypes on sub query elements (yet),
917
969
  // we do not have to care about $origin not being set
@@ -919,15 +971,26 @@ function addOrigin( csn, xsn, origin ) {
919
971
  if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
920
972
  // repeated anon: flatten
921
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];
922
986
  }
923
- else if (Object.keys( anon )
924
- // (we can use the properties in `csn`, because addOrigin() is called last)
925
- .every( p => p in csn || p === '$origin' || p === '$location')) {
926
- // nothing new in $origin: {...}
927
- addOrigin( csn, xsn, origin._origin );
987
+ if (Object.keys( annos ).length) {
988
+ if (!csn.type && $origin)
989
+ annos.$origin = $origin;
990
+ csn.$origin = annos;
928
991
  }
929
- else {
930
- csn.$origin = anon;
992
+ else if (!csn.type) {
993
+ addOrigin( csn, xsn, origin );
931
994
  }
932
995
  }
933
996
 
@@ -937,10 +1000,27 @@ function getParent( art ) {
937
1000
  return (main && parent === main._leadingQuery) ? main : parent;
938
1001
  }
939
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)
940
1017
  function getOrigin( art ) {
941
- if (art._origin)
942
- return art._origin;
943
- 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' ))
944
1024
  return art.type._artifact;
945
1025
  if (art.includes)
946
1026
  return art.includes[0]._artifact;
@@ -949,36 +1029,38 @@ function getOrigin( art ) {
949
1029
  return undefined;
950
1030
  }
951
1031
 
952
- function hasExplicitProp( ref ) {
953
- return ref && !ref.$inferred;
1032
+ function hasExplicitProp( ref, alsoLikeExplicit ) {
1033
+ return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
954
1034
  }
955
1035
 
956
1036
  function originRef( art, user ) {
957
1037
  const r = [];
958
- // do not use name.element, as we allow `.`s in name
1038
+ // do not use name.element, as we might allow `.`s in name
959
1039
  let parent = art;
960
- 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') {
961
1043
  const nkind = normalizedKind[parent.kind];
962
- if (parent.name.id || !r.length)
1044
+ const name = parent.name || parent._outer.name;
1045
+ if (name.id || !r.length)
963
1046
  // Return parameter is in XSN - kind: 'param', name.id: ''
964
1047
  // eslint-disable-next-line no-nested-ternary, max-len
965
- 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 } );
966
1049
  parent = parent._parent;
967
1050
  }
968
1051
  if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
969
1052
  // well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
970
- return null; // probably use $origin: {...}
1053
+ return false; // do not write, probably use $origin: {...}
971
1054
  // for sub query in FROM in sub query in FROM, we could condense the info
972
1055
 
973
- // Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
974
- if (r.length === 1 && normalizedKind[art.kind] === 'action')
975
- return [ art.name.absolute, art.name.id ];
976
- r.push( art.name.absolute );
1056
+ r.push( parent.name.absolute );
977
1057
  r.reverse();
978
1058
  return r;
979
1059
  }
980
1060
 
981
1061
  function kind( k, csn, node ) {
1062
+ if (node.$inferred === 'REDIRECTED')
1063
+ return;
982
1064
  if (k === 'annotate' || k === 'extend') {
983
1065
  // We just use `name.absolute` because it is very likely a "constructed"
984
1066
  // extensions. The CSN parser must produce name.path like for other refs.
@@ -987,20 +1069,44 @@ function kind( k, csn, node ) {
987
1069
  else if (k === 'extend')
988
1070
  csn.kind = k;
989
1071
  }
1072
+ else if (k === 'action' && node._main && universalCsn && node.$inferred) {
1073
+ // Universal CSN: do not mention kind: 'action' on expanded action
1074
+ }
1075
+ else if (k === 'aspect' && (node._outer || node.$inferred)) {
1076
+ return; // do not show kind for anonymous aspect
1077
+ }
990
1078
  else if (![
991
1079
  'element', 'key', 'param', 'enum', 'select', '$join',
992
1080
  '$tableAlias', 'annotation', 'mixin',
993
1081
  ].includes(k)) {
994
1082
  csn.kind = k;
995
1083
  }
1084
+ const generated = universalCsn && inferredAsGenerated[node.$inferred];
1085
+ if (typeof generated === 'string')
1086
+ csn.$generated = generated;
996
1087
  }
997
1088
 
998
1089
  function type( node, csn, xsn ) {
999
- if (universalCsn && node.$inferred && xsn._origin)
1090
+ if (!universalCsn)
1091
+ return artifactRef( node, !node.$extra );
1092
+ if (node.$inferred && node.$inferred !== 'cast')
1000
1093
  return undefined;
1094
+ if (xsn._origin) {
1095
+ if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
1096
+ csn.$origin = definition( xsn._origin );
1097
+ }
1098
+ }
1001
1099
  return artifactRef( node, !node.$extra );
1002
1100
  }
1003
1101
 
1102
+ function cardinality( node ) {
1103
+ if (!universalCsn)
1104
+ return standard( node );
1105
+ if (node.$inferred)
1106
+ return undefined;
1107
+ return standard( node );
1108
+ }
1109
+
1004
1110
  function artifactRef( node, terse ) {
1005
1111
  // When called as transformer function, a CSN node is provided as argument
1006
1112
  // for `terse`, i.e. it is usually truthy, except for FROM
@@ -1104,8 +1210,12 @@ function value( node ) {
1104
1210
  // "Short" value form, e.g. for annotation assignments
1105
1211
  if (!node)
1106
1212
  return true; // `@aBool` short for `@aBool: true`
1107
- if (universalCsn && node.$inferred === 'prop') // via propagator.js
1108
- 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
+ }
1109
1219
  if (node.$inferred && gensrcFlavor)
1110
1220
  return undefined;
1111
1221
  if (node.path) {
@@ -1117,7 +1227,7 @@ function value( node ) {
1117
1227
  if (node.literal === 'array')
1118
1228
  return node.val.map( value );
1119
1229
  if (node.literal === 'token' && node.val === '...')
1120
- return extra( { '...': true } );
1230
+ return extra( { '...': !node.upTo || value( node.upTo ) } );
1121
1231
  if (node.literal !== 'struct')
1122
1232
  // no val (undefined) as true only for annotation values (and struct elem values)
1123
1233
  return node.name && !('val' in node) || node.val;
@@ -1274,7 +1384,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1274
1384
  if (node.op.val === 'SELECT') {
1275
1385
  if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
1276
1386
  node.from && node.from.path && !projectionAsQuery) {
1277
- csn.projection = standard( node );
1387
+ csn.projection = addLocation( node.location, standard( node ) );
1278
1388
  return undefined;
1279
1389
  }
1280
1390
  const select = { SELECT: extra( standard( node ), node, expectedParens ) };
@@ -1296,6 +1406,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1296
1406
  gensrcFlavor = gensrcSaved;
1297
1407
  }
1298
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 );
1299
1412
  return addLocation( node.location, select );
1300
1413
  }
1301
1414
  const union = {};
@@ -1344,11 +1457,13 @@ function from( node ) {
1344
1457
  else if (node.query) {
1345
1458
  return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
1346
1459
  }
1347
- else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
1348
- return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
1349
- }
1350
- return extra( addExplicitAs( artifactRef( node, false ), node.name, (id) => {
1351
- 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;
1352
1467
  const dot = name.lastIndexOf('.');
1353
1468
  return name.substring( dot + 1 ) !== id;
1354
1469
  }), node );
@@ -1397,7 +1512,7 @@ function addElementAsColumn( elem, cols ) {
1397
1512
  }
1398
1513
 
1399
1514
  function orderBy( node ) {
1400
- const expr = expression( node, 'ignoreExtra' );
1515
+ const expr = expression( node );
1401
1516
  if (node.sort)
1402
1517
  expr.sort = node.sort.val;
1403
1518
  if (node.nulls)
@@ -1470,6 +1585,9 @@ function compactExpr( e ) { // TODO: options
1470
1585
  return e && expression( e, true );
1471
1586
  }
1472
1587
 
1588
+ /**
1589
+ * @param {CSN.Options} options
1590
+ */
1473
1591
  function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
1474
1592
  gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
1475
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;
@@ -114,9 +114,10 @@ function sync( recognizer ) {
114
114
  }
115
115
  return;
116
116
  }
117
- // TODO: expected token is identifier, current is KEYWORD
118
117
 
119
118
  if (nextTokens.contains(antlr4.Token.EPSILON)) {
119
+ // when exiting a (innermost) rule, remember the state to make
120
+ // getExpectedTokensForMessage() calculate the full "expected set"
120
121
  if (recognizer.$nextTokensToken !== token) {
121
122
  // console.log('SET:',token.type,recognizer.state,recognizer.$nextTokensToken && recognizer.$nextTokensToken.type)
122
123
  recognizer.$nextTokensToken = token;
@@ -126,13 +127,29 @@ function sync( recognizer ) {
126
127
  return;
127
128
  }
128
129
 
130
+ // Expected token is identifier, current is (reserved) KEYWORD:
131
+ // TODO: do not use this if "close enough" (1 char diff) to a keyword in nextTokens
132
+ //
133
+ // NOTE: it is important to do this only if EPSILON is not in `nextTokens`,
134
+ // which means that we cannot bring the better special syntax-fragile-ident
135
+ // in all cases. Reason: high performance impact of the alternative,
136
+ // i.e. calling method Parser#isExpectedToken() = invoking the ATN
137
+ // interpreter to see behind EPSILON.
138
+ const identType = recognizer.constructor.Identifier;
139
+ if (keywordRegexp.test( token.text ) && nextTokens.contains( identType )) {
140
+ recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text },
141
+ '$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
142
+ token.type = identType; // make next ANTLR decision assume identifier
143
+ return;
144
+ }
145
+
129
146
  if (recognizer._ctx._sync === 'nop')
130
147
  return;
131
148
  switch (s.stateType) {
132
- case ATNState.BLOCK_START:
133
- case ATNState.STAR_BLOCK_START:
134
- case ATNState.PLUS_BLOCK_START:
135
- case ATNState.STAR_LOOP_ENTRY:
149
+ case ATNState.BLOCK_START: // 3
150
+ case ATNState.STAR_BLOCK_START: // 5
151
+ case ATNState.PLUS_BLOCK_START: // 4
152
+ case ATNState.STAR_LOOP_ENTRY: // 10
136
153
  // report error and recover if possible
137
154
  if ( token.text !== '}' && // do not just delete a '}'
138
155
  this.singleTokenDeletion(recognizer) !== null) { // also calls reportUnwantedToken
@@ -145,8 +162,8 @@ function sync( recognizer ) {
145
162
  }
146
163
  throw new InputMismatchException(recognizer);
147
164
 
148
- case ATNState.PLUS_LOOP_BACK:
149
- case ATNState.STAR_LOOP_BACK: {
165
+ case ATNState.PLUS_LOOP_BACK: // 11
166
+ case ATNState.STAR_LOOP_BACK: { // 9
150
167
  // TODO: do not delete a '}'
151
168
  this.reportUnwantedToken(recognizer);
152
169
  const expecting = new IntervalSet.IntervalSet();
@@ -425,7 +442,8 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
425
442
  }
426
443
  else if (offendingToken && recognizer.$nextTokensContext &&
427
444
  offendingToken === recognizer.$nextTokensToken) {
428
- // We have a state (via sync()) with more "expecting" for the same token
445
+ // Before exiting a rule, we had a state (via sync()) with a bigger
446
+ // "expecting set" for the same token
429
447
  ll1._LOOK( atn.states[recognizer.$nextTokensState], null,
430
448
  predictionContext( atn, recognizer.$nextTokensContext ),
431
449
  expected, lookBusy, calledRules, true, true );