@sap/cds-compiler 2.12.0 → 2.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -15,18 +15,17 @@
15
15
  // 0.1.99 : Like 0.1.0, but with new-style CSN
16
16
  // 0.2 : same as 0.1.99, but with new top-level properties: $version, meta
17
17
 
18
- // Use literal version constants intentionally and not number intervals to
18
+ // Use literal version constants intentionally and not number intervals to
19
19
  // record all published version strings of the core compiler.
20
- const newCSNVersions = ["0.1.99","0.2","0.2.0","1.0","2.0"];
20
+ const newCSNVersions = [ '0.1.99', '0.2', '0.2.0', '1.0', '2.0' ];
21
21
  // checks if new-csn is requested via the options of already specified in the CSN
22
22
  // default: old-style
23
23
  function isNewCSN(csn, options) {
24
- if( (options && options.newCsn === false) ||
24
+ if ( (options && options.newCsn === false) ||
25
25
  (csn.version && !newCSNVersions.includes(csn.version.csn)) ||
26
26
  (csn.$version && !newCSNVersions.includes(csn.$version)))
27
- {
28
27
  return false;
29
- }
28
+
30
29
  return true;
31
30
  }
32
31
 
@@ -34,18 +33,18 @@ function checkCSNVersion(csn, options) {
34
33
  if (!isNewCSN(csn, options)) {
35
34
  // the new transformer works only with new CSN
36
35
  const { makeMessageFunction } = require('../base/messages');
37
- const { error, throwWithError } = makeMessageFunction(csn, options);
36
+ const { error, throwWithAnyError } = makeMessageFunction(csn, options);
38
37
 
39
38
  let errStr = 'CSN Version not supported, version tag: "';
40
- errStr += (csn.version && csn.version.csn ? csn.version.csn : (csn.$version ? csn.$version : 'not available')) + '"';
41
- errStr += (options.newCsn !== undefined) ? ', options.newCsn: ' + options.newCsn : '';
39
+ errStr += `${ csn.version && csn.version.csn ? csn.version.csn : (csn.$version ? csn.$version : 'not available') }"`;
40
+ errStr += (options.newCsn !== undefined) ? `, options.newCsn: ${ options.newCsn }` : '';
42
41
 
43
42
  error(null, null, errStr);
44
- throwWithError();
43
+ throwWithAnyError();
45
44
  }
46
45
  }
47
46
 
48
47
  module.exports = {
49
48
  isNewCSN,
50
- checkCSNVersion
51
- }
49
+ checkCSNVersion,
50
+ };
@@ -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!
@@ -150,7 +152,8 @@ const schemaClasses = {
150
152
  msgId: 'syntax-csn-expected-column',
151
153
  defaultKind: '$column',
152
154
  validKinds: [], // pseudo kind '$column'
153
- requires: [ 'ref', 'xpr', 'val', '#', 'func', 'list', 'SELECT', 'SET', 'expand' ],
155
+ // A column with only as+cast.type is a new association
156
+ requires: [ 'ref', 'as', 'xpr', 'val', '#', 'func', 'list', 'SELECT', 'SET', 'expand' ],
154
157
  schema: {
155
158
  xpr: {
156
159
  class: 'condition',
@@ -195,7 +198,7 @@ const schema = compileSchema( {
195
198
  dictionaryOf: definition,
196
199
  defaultKind: 'enum',
197
200
  validKinds: [ 'enum' ],
198
- inKind: [ 'element', 'type', 'param', 'annotation', 'annotate' ],
201
+ inKind: [ 'element', 'type', 'param', 'annotation', 'annotate', 'extend' ],
199
202
  },
200
203
  elements: {
201
204
  dictionaryOf: definition,
@@ -661,6 +664,9 @@ const schema = compileSchema( {
661
664
  ignore: true, type: ignore,
662
665
  },
663
666
  // TODO: should we keep $parens ?
667
+ $generated: {
668
+ type: string,
669
+ },
664
670
  $: { type: ignore, ignore: true }, // including $origin
665
671
  _: { type: ignore, ignore: true },
666
672
  } );
@@ -931,8 +937,10 @@ function definition( def, spec, xsn, csn, name ) {
931
937
  popLocation( def );
932
938
  if (kind !== 'annotation' || prop === 'vocabularies')
933
939
  return r;
934
- if (!vocabInDefinitions)
940
+ if (!vocabInDefinitions) {
935
941
  vocabInDefinitions = Object.create(null);
942
+ vocabInDefinitions[$location] = location();
943
+ }
936
944
  vocabInDefinitions[name] = r; // deprecated: anno def in 'definitions'
937
945
  return undefined;
938
946
 
@@ -957,6 +965,7 @@ function dictionaryOf( elementFct ) {
957
965
  return ignore( dict );
958
966
  }
959
967
  const r = Object.create(null);
968
+ r[$location] = location();
960
969
  const allNames = Object.keys( dict );
961
970
  if (!allNames.length)
962
971
  return r; // {} in one JSON line
@@ -980,6 +989,7 @@ function keys( array, spec, xsn ) {
980
989
  if (!isArray( array, spec ))
981
990
  return;
982
991
  const r = Object.create(null);
992
+ r[$location] = location();
983
993
  ++virtualLine;
984
994
  for (const def of array) {
985
995
  const id = def.as || implicitName( def.ref );
@@ -1177,6 +1187,7 @@ function annoValue( val, spec ) {
1177
1187
  if (lit !== 'object')
1178
1188
  return { val, literal: lit, location: location() };
1179
1189
  if (Array.isArray( val )) {
1190
+ /** @type {string|boolean} */
1180
1191
  let seenEllipsis = false;
1181
1192
  if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
1182
1193
  if (val.some( isEllipsis )) {
@@ -1435,6 +1446,7 @@ function excluding( array, spec, xsn ) {
1435
1446
  if (!isArray( array, spec ))
1436
1447
  return;
1437
1448
  const r = Object.create(null);
1449
+ r[$location] = location();
1438
1450
  ++virtualLine;
1439
1451
  for (const ex of array) {
1440
1452
  const id = string( ex, spec ) || '';
@@ -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!
@@ -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
  *
@@ -401,8 +404,6 @@ function usings( srcDict ) {
401
404
  * @param {object} csn
402
405
  * @param {object} model
403
406
  */
404
-
405
-
406
407
  function extensions( node, csn, model ) {
407
408
  if (model.kind && model.kind !== 'source')
408
409
  return undefined;
@@ -621,10 +622,7 @@ function targetAspect( val, csn, node ) {
621
622
  if (val.$inferred)
622
623
  return undefined;
623
624
  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 );
625
+ csn.$origin = { target: (val.elements) ? standard( val ) : artifactRef( val, true ) };
628
626
  return undefined;
629
627
  }
630
628
  }
@@ -643,7 +641,7 @@ function target( val, _csn, node ) {
643
641
  val = node._origin.target;
644
642
  if (val.elements)
645
643
  return standard( val ); // elements in target (parse-cdl)
646
- if (!universalCsn || node.on)
644
+ if (!universalCsn || gensrcFlavor || node.on)
647
645
  return artifactRef( val, true );
648
646
  const tref = artifactRef( val, true );
649
647
  const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
@@ -779,7 +777,7 @@ function addLocation( loc, csn ) {
779
777
  // Remove endLine/endCol:
780
778
  // Reasoning: $location is mostly attached to definitions/members but the name
781
779
  // is often not the reason for an error or warning. So we gain little benefit for
782
- // two more properties.
780
+ // two more properties. It is also an indication that the location is not exact.
783
781
  const val = { file: loc.file, line: loc.line, col: loc.col };
784
782
  Object.defineProperty( csn, '$location', {
785
783
  value: val, configurable: true, writable: true, enumerable: withLocations,
@@ -807,6 +805,13 @@ function actions( dict ) {
807
805
  : undefined;
808
806
  }
809
807
 
808
+ function params( dict ) {
809
+ const keys = Object.keys( dict );
810
+ return (keys.length)
811
+ ? insertOrderDict( dict )
812
+ : undefined;
813
+ }
814
+
810
815
  function dictionary( dict, keys, prop ) {
811
816
  const csn = Object.create( dictionaryPrototype );
812
817
  for (const name of keys) {
@@ -863,7 +868,7 @@ function definition( art, _csn, _node, prop ) {
863
868
  c.returns = { elements: elems };
864
869
  }
865
870
  // precondition already fulfilled: art.kind !== 'key'
866
- addOrigin( c, art, art._origin );
871
+ addOrigin( c, art, art );
867
872
  return c;
868
873
  }
869
874
 
@@ -876,7 +881,8 @@ function includesOrigin( includes, art ) {
876
881
  for (const incl of includes.slice(1)) {
877
882
  const aspect = incl._artifact;
878
883
  for (const prop in aspect) {
879
- if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
884
+ if ((prop.charAt(0) === '@' || prop === 'doc') &&
885
+ (!art[prop] || art[prop].$inferred)) {
880
886
  const anno = aspect[prop];
881
887
  if (anno.val !== null)
882
888
  // matererialize non-null annos (whether direct or inherited)
@@ -887,9 +893,23 @@ function includesOrigin( includes, art ) {
887
893
  return (Object.keys( result ).length === 1) ? $origin : result;
888
894
  }
889
895
 
890
- function addOrigin( csn, xsn, origin ) {
891
- if (!universalCsn || hasExplicitProp( xsn.type ))
896
+ function addOrigin( csn, xsn, node ) {
897
+ if (!universalCsn)
898
+ return;
899
+ if (hasExplicitProp( xsn.type, 'cast' )) {
900
+ const main = xsn._main || xsn;
901
+ let count = 0;
902
+ let source = xsn;
903
+ while (source && source._main === main) {
904
+ source = source.value && source.value._artifact;
905
+ ++count;
906
+ }
907
+ if (count > 0 && source && source.kind !== 'builtin')
908
+ csn.$source = originRef( source, xsn );
909
+ else if (count > 1)
910
+ csn.$source = null;
892
911
  return;
912
+ }
893
913
  if (xsn._from) {
894
914
  const source = xsn._from[0]._origin;
895
915
  csn.$origin = originRef( source );
@@ -903,23 +923,22 @@ function addOrigin( csn, xsn, origin ) {
903
923
  csn.$origin = includesOrigin( xsn.includes, xsn );
904
924
  return;
905
925
  }
906
- else if (!xsn._main || xsn.kind === 'select') {
926
+ let origin = getOrigin( node );
927
+ if (xsn.$inferred === 'composition-entity') {
928
+ csn.$origin = originRef( origin, xsn );
929
+ return;
930
+ }
931
+ else if (!isMember( xsn ) || xsn.kind === 'select') {
907
932
  return;
908
933
  }
934
+ // from here on: member:
909
935
  const parent = getParent( xsn );
910
936
  const parentOrigin = getOrigin( parent );
911
- if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
912
- if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
937
+ if (!origin) {
938
+ if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
913
939
  csn.$origin = null;
914
940
  return;
915
941
  }
916
- // Skip all proxies which do not make it into the CSN, as there are no
917
- // individual annotations or redirection targets on it:
918
- while (origin._parent && origin._parent.$expand === 'origin')
919
- origin = origin._origin || origin.type._artifact;
920
- // The while loop is not only for the else case below: when setting implicit
921
- // prototypes, it is important that we do not have to follow the prototypes of
922
- // other object; we would need to ensure a right order to avoid issues otherwise.
923
942
  if (parentOrigin === getParent( origin )) {
924
943
  // implicit prototype or shortened reference
925
944
  const { id } = origin.name || {};
@@ -928,16 +947,21 @@ function addOrigin( csn, xsn, origin ) {
928
947
  return;
929
948
  }
930
949
  if (origin.kind === 'mixin') {
931
- // currently, target and on are always set - nothing to do here, just set type
932
- csn.type = 'cds.Association';
950
+ set( 'type', csn, origin );
951
+ set( 'cardinality', csn, origin );
952
+ // currently, target and on are always set - nothing to do here
933
953
  return;
934
954
  }
955
+ // Skip all proxies which do not make it into the CSN, as there are no
956
+ // individual annotations or redirection targets on it:
957
+ while (origin._parent && origin._parent.$expand === 'origin')
958
+ origin = origin._origin || origin.type._artifact;
935
959
  const ref = originRef( origin, xsn );
936
960
  if (ref) {
937
961
  csn.$origin = ref;
938
962
  return;
939
963
  }
940
- // An element of a query with a query in FROM:
964
+ // An element of a query with a query in FROM: -----------------------------
941
965
  const anon = definition( origin ); // use $origin: {...} if necessary
942
966
  // as there are no implicit $origin prototypes on sub query elements (yet),
943
967
  // we do not have to care about $origin not being set
@@ -945,15 +969,26 @@ function addOrigin( csn, xsn, origin ) {
945
969
  if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
946
970
  // repeated anon: flatten
947
971
  csn.$origin = Object.assign( $origin, anon );
972
+ return;
973
+ }
974
+ // Annotations and 'doc' must keep the distinction between direct or inherited,
975
+ // other properties can as well be set as direct element properties
976
+ const annos = {};
977
+ for (const prop of Object.keys( anon )) {
978
+ if (prop.charAt(0) === '@' || prop === 'doc')
979
+ annos[prop] = anon[prop];
980
+ else if (prop === '$source')
981
+ csn[prop] = anon[prop]; // overwrite from inner
982
+ else if (prop !== '$location' && prop !== '$origin' && !(prop in csn))
983
+ csn[prop] = anon[prop];
948
984
  }
949
- else if (Object.keys( anon )
950
- // (we can use the properties in `csn`, because addOrigin() is called last)
951
- .every( p => p in csn || p === '$origin' || p === '$location')) {
952
- // nothing new in $origin: {...}
953
- addOrigin( csn, xsn, origin._origin );
985
+ if (Object.keys( annos ).length) {
986
+ if (!csn.type && $origin)
987
+ annos.$origin = $origin;
988
+ csn.$origin = annos;
954
989
  }
955
- else {
956
- csn.$origin = anon;
990
+ else if (!csn.type) {
991
+ addOrigin( csn, xsn, origin );
957
992
  }
958
993
  }
959
994
 
@@ -963,10 +998,27 @@ function getParent( art ) {
963
998
  return (main && parent === main._leadingQuery) ? main : parent;
964
999
  }
965
1000
 
1001
+ function isMember( art ) {
1002
+ // TODO: introduce art.kind = '$aspect' for anonymous aspect (is a member) ?
1003
+ return !!(art._main || art._outer);
1004
+ }
1005
+
1006
+ // function getDefinition( art ) {
1007
+ // let main = art._main || art;
1008
+ // while (main._outer) // anonymous aspect
1009
+ // main = main._outer._main;
1010
+ // return main;
1011
+ // }
1012
+
1013
+ // XSN `_origin` is (currently?) not the same as _origin in Universal CSN...
1014
+ // TODO: at least with expand, set it correctly (alias: keep, assoc: to entity, $builtin: no)
966
1015
  function getOrigin( art ) {
967
- if (art._origin)
968
- return art._origin;
969
- if (hasExplicitProp( art.type ))
1016
+ if (art.$noOrigin)
1017
+ return undefined;
1018
+ const { _origin } = art;
1019
+ if (_origin)
1020
+ return (_origin.kind === 'builtin') ? undefined : _origin; // not $dollarVariable
1021
+ if (hasExplicitProp( art.type, 'cast' ))
970
1022
  return art.type._artifact;
971
1023
  if (art.includes)
972
1024
  return art.includes[0]._artifact;
@@ -975,31 +1027,31 @@ function getOrigin( art ) {
975
1027
  return undefined;
976
1028
  }
977
1029
 
978
- function hasExplicitProp( ref ) {
979
- return ref && !ref.$inferred;
1030
+ function hasExplicitProp( ref, alsoLikeExplicit ) {
1031
+ return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
980
1032
  }
981
1033
 
982
1034
  function originRef( art, user ) {
983
1035
  const r = [];
984
- // do not use name.element, as we allow `.`s in name
1036
+ // do not use name.element, as we might allow `.`s in name
985
1037
  let parent = art;
986
- while (parent._main && parent.kind !== 'select') {
1038
+ if (parent._outer && parent.kind === 'aspect')
1039
+ r.push( { target: true } );
1040
+ while (isMember( parent ) && parent.kind !== 'select') {
987
1041
  const nkind = normalizedKind[parent.kind];
988
- if (parent.name.id || !r.length)
1042
+ const name = parent.name || parent._outer.name;
1043
+ if (name.id || !r.length)
989
1044
  // Return parameter is in XSN - kind: 'param', name.id: ''
990
1045
  // eslint-disable-next-line no-nested-ternary, max-len
991
- r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
1046
+ r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
992
1047
  parent = parent._parent;
993
1048
  }
994
1049
  if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
995
1050
  // well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
996
- return null; // probably use $origin: {...}
1051
+ return false; // do not write, probably use $origin: {...}
997
1052
  // for sub query in FROM in sub query in FROM, we could condense the info
998
1053
 
999
- // Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
1000
- if (r.length === 1 && normalizedKind[art.kind] === 'action')
1001
- return [ art.name.absolute, art.name.id ];
1002
- r.push( art.name.absolute );
1054
+ r.push( parent.name.absolute );
1003
1055
  r.reverse();
1004
1056
  return r;
1005
1057
  }
@@ -1018,6 +1070,9 @@ function kind( k, csn, node ) {
1018
1070
  else if (k === 'action' && node._main && universalCsn && node.$inferred) {
1019
1071
  // Universal CSN: do not mention kind: 'action' on expanded action
1020
1072
  }
1073
+ else if (k === 'aspect' && (node._outer || node.$inferred)) {
1074
+ return; // do not show kind for anonymous aspect
1075
+ }
1021
1076
  else if (![
1022
1077
  'element', 'key', 'param', 'enum', 'select', '$join',
1023
1078
  '$tableAlias', 'annotation', 'mixin',
@@ -1032,26 +1087,20 @@ function kind( k, csn, node ) {
1032
1087
  function type( node, csn, xsn ) {
1033
1088
  if (!universalCsn)
1034
1089
  return artifactRef( node, !node.$extra );
1035
- if (node.$inferred)
1090
+ if (node.$inferred && node.$inferred !== 'cast')
1036
1091
  return undefined;
1037
1092
  if (xsn._origin) {
1038
1093
  if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
1039
1094
  csn.$origin = definition( xsn._origin );
1040
- return undefined;
1041
1095
  }
1042
1096
  }
1043
- else if ( xsn.targetAspect && xsn.target ) {
1044
- // type moved to $origin: { type: … }
1045
- return undefined;
1046
- }
1047
1097
  return artifactRef( node, !node.$extra );
1048
1098
  }
1049
1099
 
1050
- function cardinality( node, csn, xsn ) {
1100
+ function cardinality( node ) {
1051
1101
  if (!universalCsn)
1052
1102
  return standard( node );
1053
- // cardinality might be moved to $origin: { cardinality: … }
1054
- if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
1103
+ if (node.$inferred)
1055
1104
  return undefined;
1056
1105
  return standard( node );
1057
1106
  }
@@ -1159,8 +1208,12 @@ function value( node ) {
1159
1208
  // "Short" value form, e.g. for annotation assignments
1160
1209
  if (!node)
1161
1210
  return true; // `@aBool` short for `@aBool: true`
1162
- if (universalCsn && node.$inferred === 'prop') // via propagator.js
1163
- return undefined;
1211
+ if (universalCsn && node.$inferred) {
1212
+ if (node.$inferred === 'prop' || node.$inferred === '$generated') // via propagator.js
1213
+ return undefined;
1214
+ else if (node.$inferred === 'NULL')
1215
+ return null;
1216
+ }
1164
1217
  if (node.$inferred && gensrcFlavor)
1165
1218
  return undefined;
1166
1219
  if (node.path) {
@@ -1329,7 +1382,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1329
1382
  if (node.op.val === 'SELECT') {
1330
1383
  if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
1331
1384
  node.from && node.from.path && !projectionAsQuery) {
1332
- csn.projection = standard( node );
1385
+ csn.projection = addLocation( node.location, standard( node ) );
1333
1386
  return undefined;
1334
1387
  }
1335
1388
  const select = { SELECT: extra( standard( node ), node, expectedParens ) };
@@ -1351,6 +1404,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
1351
1404
  gensrcFlavor = gensrcSaved;
1352
1405
  }
1353
1406
  }
1407
+ // the $location is better put inside the SELECT value, not as sibling (but
1408
+ // we keep it as sibling also for compatibility):
1409
+ addLocation( node.location, select.SELECT );
1354
1410
  return addLocation( node.location, select );
1355
1411
  }
1356
1412
  const union = {};
@@ -1399,11 +1455,13 @@ function from( node ) {
1399
1455
  else if (node.query) {
1400
1456
  return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
1401
1457
  }
1402
- else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
1403
- return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
1404
- }
1405
- return extra( addExplicitAs( artifactRef( node, false ), node.name, (id) => {
1406
- const name = node._artifact.name.absolute;
1458
+
1459
+ const ref = artifactRef( node, false );
1460
+ return extra( addExplicitAs( ref, node.name, (id) => {
1461
+ let name = ref.ref ? ref.ref[ref.ref.length - 1] : ref;
1462
+ name = name && name.id || name;
1463
+ if (!name)
1464
+ return false;
1407
1465
  const dot = name.lastIndexOf('.');
1408
1466
  return name.substring( dot + 1 ) !== id;
1409
1467
  }), node );
@@ -1452,7 +1510,7 @@ function addElementAsColumn( elem, cols ) {
1452
1510
  }
1453
1511
 
1454
1512
  function orderBy( node ) {
1455
- const expr = expression( node, 'ignoreExtra' );
1513
+ const expr = expression( node );
1456
1514
  if (node.sort)
1457
1515
  expr.sort = node.sort.val;
1458
1516
  if (node.nulls)
@@ -36,14 +36,14 @@ function parseDocComment(comment) {
36
36
  else if (lines.length === 2) {
37
37
  // Comment that is essentially just a header + footer.
38
38
  // First line, i.e. header, is always trimmed from left.
39
- lines[0] = lines[0].trimLeft();
39
+ lines[0] = lines[0].trimStart();
40
40
 
41
41
  // If the second line starts with an asterisk then remove it.
42
42
  // Otherwise trim all whitespace.
43
43
  if ((/^\s*[*]/.test(lines[1])))
44
44
  lines[1] = removeFence(lines[1]);
45
45
  else
46
- lines[1] = lines[1].trimLeft();
46
+ lines[1] = lines[1].trimStart();
47
47
  }
48
48
  else {
49
49
  const firstNonEmptyLine = lines.find((line, index) => index !== 0 && /[^\s]/.test(line)) || '';
@@ -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;