@sap/cds-compiler 4.0.2 → 4.2.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 (101) hide show
  1. package/CHANGELOG.md +200 -5
  2. package/bin/cdsc.js +18 -15
  3. package/doc/CHANGELOG_BETA.md +16 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +33 -13
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +25 -25
  8. package/lib/base/location.js +6 -7
  9. package/lib/base/message-registry.js +123 -42
  10. package/lib/base/messages.js +18 -10
  11. package/lib/base/model.js +43 -10
  12. package/lib/checks/defaultValues.js +6 -6
  13. package/lib/checks/elements.js +11 -10
  14. package/lib/checks/foreignKeys.js +0 -5
  15. package/lib/checks/manyNavigations.js +33 -0
  16. package/lib/checks/onConditions.js +22 -14
  17. package/lib/checks/queryNoDbArtifacts.js +132 -73
  18. package/lib/checks/selectItems.js +4 -55
  19. package/lib/checks/sql-snippets.js +15 -4
  20. package/lib/checks/types.js +3 -3
  21. package/lib/checks/utils.js +4 -3
  22. package/lib/checks/validator.js +3 -1
  23. package/lib/compiler/.eslintrc.json +2 -1
  24. package/lib/compiler/assert-consistency.js +71 -40
  25. package/lib/compiler/base.js +7 -2
  26. package/lib/compiler/builtins.js +40 -41
  27. package/lib/compiler/checks.js +415 -367
  28. package/lib/compiler/classes.js +62 -0
  29. package/lib/compiler/cycle-detector.js +9 -9
  30. package/lib/compiler/define.js +124 -90
  31. package/lib/compiler/extend.js +115 -88
  32. package/lib/compiler/finalize-parse-cdl.js +26 -25
  33. package/lib/compiler/generate.js +57 -49
  34. package/lib/compiler/index.js +56 -56
  35. package/lib/compiler/kick-start.js +10 -7
  36. package/lib/compiler/moduleLayers.js +1 -1
  37. package/lib/compiler/populate.js +180 -144
  38. package/lib/compiler/propagator.js +10 -9
  39. package/lib/compiler/resolve.js +321 -246
  40. package/lib/compiler/shared.js +812 -433
  41. package/lib/compiler/tweak-assocs.js +114 -50
  42. package/lib/compiler/utils.js +241 -46
  43. package/lib/edm/.eslintrc.json +40 -1
  44. package/lib/edm/annotations/genericTranslation.js +721 -707
  45. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  46. package/lib/edm/csn2edm.js +389 -378
  47. package/lib/edm/edm.js +679 -770
  48. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  49. package/lib/edm/edmInboundChecks.js +29 -27
  50. package/lib/edm/edmPreprocessor.js +689 -648
  51. package/lib/edm/edmUtils.js +279 -300
  52. package/lib/gen/Dictionary.json +34 -10
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +1 -1
  55. package/lib/gen/languageParser.js +2857 -2856
  56. package/lib/json/from-csn.js +77 -51
  57. package/lib/json/to-csn.js +15 -15
  58. package/lib/language/antlrParser.js +2 -1
  59. package/lib/language/genericAntlrParser.js +52 -43
  60. package/lib/language/language.g4 +61 -64
  61. package/lib/language/multiLineStringParser.js +2 -0
  62. package/lib/main.d.ts +65 -0
  63. package/lib/model/csnRefs.js +37 -19
  64. package/lib/model/csnUtils.js +51 -18
  65. package/lib/model/revealInternalProperties.js +30 -22
  66. package/lib/modelCompare/compare.js +149 -41
  67. package/lib/modelCompare/utils/filter.js +55 -25
  68. package/lib/optionProcessor.js +21 -9
  69. package/lib/render/manageConstraints.js +20 -17
  70. package/lib/render/toCdl.js +63 -23
  71. package/lib/render/toHdbcds.js +2 -2
  72. package/lib/render/toRename.js +4 -9
  73. package/lib/render/toSql.js +82 -35
  74. package/lib/render/utils/common.js +11 -9
  75. package/lib/render/utils/unique.js +52 -0
  76. package/lib/transform/db/applyTransformations.js +62 -21
  77. package/lib/transform/db/assertUnique.js +7 -8
  78. package/lib/transform/db/associations.js +2 -2
  79. package/lib/transform/db/cdsPersistence.js +9 -9
  80. package/lib/transform/db/constraints.js +47 -17
  81. package/lib/transform/db/expansion.js +138 -68
  82. package/lib/transform/db/flattening.js +98 -30
  83. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  84. package/lib/transform/db/temporal.js +1 -1
  85. package/lib/transform/db/transformExists.js +8 -7
  86. package/lib/transform/db/views.js +73 -33
  87. package/lib/transform/draft/db.js +11 -9
  88. package/lib/transform/draft/odata.js +1 -1
  89. package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
  90. package/lib/transform/forRelationalDB.js +148 -136
  91. package/lib/transform/localized.js +92 -54
  92. package/lib/transform/odata/toFinalBaseType.js +3 -3
  93. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
  94. package/lib/transform/translateAssocsToJoins.js +14 -28
  95. package/lib/utils/file.js +7 -7
  96. package/lib/utils/moduleResolve.js +210 -121
  97. package/lib/utils/objectUtils.js +1 -1
  98. package/package.json +5 -5
  99. package/share/messages/check-proper-type-of.md +1 -1
  100. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  101. package/share/messages/message-explanations.json +1 -1
@@ -72,8 +72,9 @@
72
72
  * What "kind" values are possible in a definition. The root "definitions" properties allows
73
73
  * more kinds than e.g. definitions inside "elements".
74
74
  *
75
- * @property {string|string[]} [onlyWith]
76
- * Defines that the property *must* be used with these properties.
75
+ * @property {string|string[]|Object} [onlyWith]
76
+ * Defines that the property *must* be used with one of these properties.
77
+ * If an object, it maps the kind value to a string or array of strings.
77
78
  *
78
79
  * @property {number} [minLength]
79
80
  * Minimum number of elements that an array must have.
@@ -84,6 +85,7 @@
84
85
  * @property {string[]} [xorGroups]
85
86
  * Corresponding xor groups. It references a value of xorGroups. If set then only one property
86
87
  * of the xorGroup may be set, e.g. if target is set, elements may not.
88
+ * If you are looking for a `notWith` (which should be symmetric), this is your property.
87
89
  *
88
90
  * @property {string} [xsnOp]
89
91
  * Defines the operator to be used for XSN. Used for SET and SELECT. See queryTerm().
@@ -117,6 +119,7 @@
117
119
  const { dictAdd } = require('../base/dictionaries');
118
120
  const { quotedLiteralPatterns } = require('../compiler/builtins');
119
121
  const { CompilerAssertion } = require('../base/error');
122
+ const { XsnSource, CsnLocation } = require('../compiler/classes');
120
123
 
121
124
  const $location = Symbol.for('cds.$location');
122
125
 
@@ -144,20 +147,22 @@ const exprProperties = [
144
147
  // Groups of properties which cannot be used together:
145
148
  const xorGroups = {
146
149
  // include CSN v0.1.0 properties here:
147
- ':type': [ 'target', 'elements', 'enum', 'items' ],
150
+ ':type': [
151
+ 'target', 'targetAspect', 'elements', 'items', // xorException: target+targetAspect
152
+ 'length', 'precision', 'scale', 'srid', // xorException: precision+scale
153
+ ],
154
+ ':enum': [ 'target', 'targetAspect', 'elements', 'enum', 'items' ],
148
155
  ':expr': [ // see also xorException property in schema
149
156
  'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET', 'expand',
150
157
  '=', 'path', 'value', 'op', // '='/'path' is CSN v0.1.0 here
151
158
  ],
152
159
  ':col': [ 'expand', 'inline' ],
153
160
  ':ext': [ 'annotate', 'extend' ], // TODO: better msg for test/negative/UnexpectedProperties.csn
154
- ':assoc': [ 'on', 'keys', 'foreignKeys', 'onCond' ], // 'foreignKeys'/'onCond' is CSN v0.1.0
155
-
156
- // TODO: Maybe use a field "notWith", similar to "onlyWith" on `elements`/`items`?
157
- ':length': [ 'elements', 'items', 'length' ],
158
- ':srid': [ 'elements', 'items', 'srid' ],
159
- ':precision': [ 'elements', 'items', 'precision' ],
160
- ':scale': [ 'elements', 'items', 'scale' ],
161
+ ':assoc': [
162
+ 'on', 'keys',
163
+ 'foreignKeys', 'onCond', // 'foreignKeys'/'onCond' is CSN v0.1.0
164
+ ],
165
+ ':on': [ 'on', 'default' ],
161
166
 
162
167
  // TODO - improve consequential errors: assume no name given with `join` or `inline`?
163
168
  as: [ 'as', 'join', 'inline' ],
@@ -251,12 +256,14 @@ const schema = compileSchema( {
251
256
  requires: [ 'extend', 'annotate' ],
252
257
  },
253
258
  enum: {
259
+ type: enumDict,
254
260
  dictionaryOf: definition,
255
261
  defaultKind: 'enum',
256
262
  validKinds: [ 'enum' ],
257
263
  inKind: [ 'element', 'type', 'param', 'annotation', 'annotate', 'extend' ],
258
264
  },
259
265
  elements: {
266
+ type: elementsDict,
260
267
  dictionaryOf: definition,
261
268
  defaultKind: 'element',
262
269
  validKinds: [ 'element' ],
@@ -272,17 +279,11 @@ const schema = compileSchema( {
272
279
  'extend',
273
280
  ],
274
281
  },
275
- payload: { // keep it for a while, TODO: remove with v2 - at least warning
276
- dictionaryOf: definition, // duplicate of line below only for better error message
277
- type: renameTo( 'elements', dictionaryOf( definition ) ),
278
- defaultKind: 'element',
279
- validKinds: [],
280
- inKind: [ 'event' ],
281
- },
282
282
  actions: {
283
283
  dictionaryOf: actions,
284
284
  defaultKind: 'action',
285
285
  validKinds: [ 'action', 'function' ],
286
+ onlyWith: { aspect: 'elements' },
286
287
  inKind: [ 'entity', 'aspect', 'annotate', 'extend' ],
287
288
  },
288
289
  params: {
@@ -364,6 +365,7 @@ const schema = compileSchema( {
364
365
  },
365
366
  targetAspect: {
366
367
  type: artifactRef,
368
+ xorException: 'target', // see xorGroup :type
367
369
  msgVariant: 'or-object', // for 'syntax-expecting-string',
368
370
  requires: 'elements',
369
371
  optional: [ 'elements' ], // 'elements' for ad-hoc aspect compositions
@@ -371,6 +373,7 @@ const schema = compileSchema( {
371
373
  },
372
374
  target: {
373
375
  type: artifactRef,
376
+ xorException: 'targetAspect', // see xorGroup :type
374
377
  msgVariant: 'or-object', // for 'syntax-expecting-string',
375
378
  requires: 'elements',
376
379
  optional: [ 'elements' ], // 'elements' for ad-hoc COMPOSITION OF (gensrc style CSN)
@@ -398,10 +401,12 @@ const schema = compileSchema( {
398
401
  },
399
402
  precision: {
400
403
  type: natnum,
404
+ xorException: 'scale', // see xorGroup :type
401
405
  inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
402
406
  },
403
407
  scale: {
404
408
  type: scalenum,
409
+ xorException: 'precision', // see xorGroup :type
405
410
  inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
406
411
  },
407
412
  srid: {
@@ -655,7 +660,7 @@ const schema = compileSchema( {
655
660
  '-expr': { // '-expr' and '-' must not exist top-level
656
661
  prop: '@‹anno›',
657
662
  type: object,
658
- optional: [ '=', '#', 'xpr', 'ref', 'val', 'list', 'literal', 'func', 'args' ],
663
+ optional: [ '=', '#', 'xpr', 'ref', 'val', 'list', 'literal', 'func', 'args', 'param' ],
659
664
  schema: {
660
665
  '=': {
661
666
  type: renameTo( '$tokenTexts', string ),
@@ -990,15 +995,12 @@ function definition( def, spec, xsn, csn, name ) {
990
995
  const kind0 = (spec.validKinds.length || spec.prop === 'extensions') && kind;
991
996
  const csnProps = Object.keys( def );
992
997
 
993
- // If we have `extend … with { name = 3 }`, it could mean adding an element or an enum.
994
- // We use `elements: { name: { val: 3 } }` to represent this ambiguity.
995
- if (savedInExtensions === '' && prop === 'elements' &&
996
- (def.val !== undefined || def['#'] !== undefined ||
997
- def.value?.val !== undefined || def.value?.['#'] !== undefined) &&
998
- def.virtual === undefined && def.key === undefined && def.masked === undefined &&
999
- def.type === undefined && def.elements === undefined) {
1000
- r.$syntax = 'enum';
1001
- kind = 'enum'; // for function expected()
998
+ // For compatibility, extension property `elements` could actually be an `enum`:
999
+ if (savedInExtensions === '' && prop === 'elements' && // in extend property `elements`
1000
+ !Object.keys( def ).some( couldNotBeEnumProperty )) {
1001
+ r.$syntax = 'enum'; // could be an enum
1002
+ if (def.val !== undefined || def['#'] !== undefined)
1003
+ kind = 'enum'; // for function expected(), i.e. allow property `val`/`#`
1002
1004
  }
1003
1005
 
1004
1006
  if (csnProps.length) {
@@ -1018,15 +1020,6 @@ function definition( def, spec, xsn, csn, name ) {
1018
1020
  r.name = { id: name, location: r.location };
1019
1021
  if (prop === 'columns' || prop === 'keys' || prop === 'foreignKeys')
1020
1022
  r.name.$inferred = 'as';
1021
- // TODO the following 'if' (if necessary) should be part of the core compiler
1022
- if (prop === 'definitions' || prop === 'vocabularies') { // as spec property
1023
- r.name = {
1024
- absolute: name,
1025
- id: name.substring( name.lastIndexOf('.') + 1 ),
1026
- path: [ { id: name, location: r.location } ],
1027
- location: r.location,
1028
- };
1029
- }
1030
1023
  }
1031
1024
  if (spec.requires)
1032
1025
  onlyWith( spec, spec.requires, def, null, xor, () => true );
@@ -1049,11 +1042,18 @@ function definition( def, spec, xsn, csn, name ) {
1049
1042
  // for an 'annotate', both 'annotate' and the "host" kind must be expected
1050
1043
  (!inExtensions || s.inKind.includes( inExtensions ) ||
1051
1044
  // extending elements in returns can be without 'returns' in CSN
1052
- // TODO: with warning/info?
1045
+ // see function elementsDict() for detail, TODO: remove finally
1053
1046
  inExtensions === 'action' && p === 'elements');
1054
1047
  }
1055
1048
  }
1056
1049
 
1050
+ function couldNotBeEnumProperty( prop ) {
1051
+ // returns true for `value` (which we allow with warning when extending an enum with `elements`)
1052
+ const inKind = schema[prop]?.inKind; // undefined for annotations, $location, …
1053
+ // inKind for annotation assignments is function -> can be for enum
1054
+ return Array.isArray( inKind ) && inKind.includes( 'element' );
1055
+ }
1056
+
1057
1057
  function actions( def, spec, xsn, csn, name ) {
1058
1058
  if (def.kind === 'extend' && (def.elements || def.enum)) {
1059
1059
  // TODO: Handle this case in extend.js; already done for `returns`
@@ -1125,6 +1125,27 @@ function returnsDefinition( def, spec, xsn, csn ) {
1125
1125
  return definition( def, spec, xsn, csn, '' );
1126
1126
  }
1127
1127
 
1128
+ // Temporary function as long as the message below is not a hard error
1129
+ function elementsDict( def, spec, xsn ) {
1130
+ const elements = dictionaryOf( definition )( def, spec );
1131
+ if (inExtensions !== 'action')
1132
+ return elements;
1133
+ warning( 'syntax-expecting-returns', elements[$location],
1134
+ { prop: 'elements', parentprop: 'returns' },
1135
+ // eslint-disable-next-line max-len
1136
+ 'Expecting property $(PROP) to be put into an object for property $(PARENTPROP) when annotating action return structures' );
1137
+ xsn.returns = { kind: 'annotate', elements, location: elements[$location] };
1138
+ return undefined;
1139
+ }
1140
+
1141
+ function enumDict( def, spec, xsn ) {
1142
+ const dict = dictionaryOf( definition )( def, spec );
1143
+ if (!inExtensions)
1144
+ return dict;
1145
+ xsn.elements = dict; // normalize to `elements` for `annotate`
1146
+ return undefined;
1147
+ }
1148
+
1128
1149
  // For v1 CSNs with annotation definitions
1129
1150
  function attachVocabInDefinitions( csn ) {
1130
1151
  if (!csn.vocabularies) {
@@ -1472,7 +1493,7 @@ function annoValue( val, spec ) {
1472
1493
  function annotation( val, spec, xsn, csn, name ) {
1473
1494
  const absolute = (xsn ? name.substring(1) : name);
1474
1495
  // TODO: really care about variant (qualifier parts)?
1475
- const variantIndex = absolute.indexOf('#') + 1 || absolute.length;
1496
+ const variantIndex = absolute.indexOf('#') + 1 || absolute.length; // including '#'
1476
1497
  const n = refSplit( absolute.substring( 0, variantIndex ), !xsn && '{}' );
1477
1498
  if (!n)
1478
1499
  return undefined;
@@ -1831,6 +1852,11 @@ function calculateKind( def, spec ) {
1831
1852
  function onlyWith( spec, need, csn, prop, xor, expected ) {
1832
1853
  if (!need)
1833
1854
  return spec;
1855
+ if (typeof need === 'object' && !Array.isArray( need )) {
1856
+ need = need[csn.kind];
1857
+ if (!need)
1858
+ return spec;
1859
+ }
1834
1860
  if (typeof need === 'string') {
1835
1861
  if (need in csn) // TODO: enumerable ?
1836
1862
  return spec;
@@ -1941,7 +1967,8 @@ function replaceZeroValue( spec, msgVariant, newValue ) {
1941
1967
  */
1942
1968
  function location( enforceJsonPos ) {
1943
1969
  return !enforceJsonPos && dollarLocations.length &&
1944
- dollarLocations[dollarLocations.length - 1] || {
1970
+ dollarLocations[dollarLocations.length - 1] || {
1971
+ __proto__: CsnLocation.prototype,
1945
1972
  file: csnFilename,
1946
1973
  line: virtualLine,
1947
1974
  col: 0,
@@ -1954,7 +1981,7 @@ function pushLocation( obj ) {
1954
1981
  if (loc === undefined)
1955
1982
  return;
1956
1983
  if (loc && typeof loc === 'object' && !Array.isArray( loc )) {
1957
- dollarLocations.push( loc.line ? loc : null );
1984
+ dollarLocations.push( loc.line ? { __proto__: CsnLocation.prototype, ...loc } : null );
1958
1985
  return;
1959
1986
  }
1960
1987
  else if (!loc || typeof loc !== 'string') {
@@ -1970,10 +1997,9 @@ function pushLocation( obj ) {
1970
1997
  else {
1971
1998
  const line = Number( m[1] );
1972
1999
  const column = m[2] && Number( m[2] ) || 0;
1973
- dollarLocations.push( {
1974
- file: loc.substring( 0, m.index ),
1975
- line,
1976
- col: column,
2000
+ const file = loc.substring( 0, m.index );
2001
+ dollarLocations.push({
2002
+ __proto__: CsnLocation.prototype, file, line, col: column,
1977
2003
  } );
1978
2004
  }
1979
2005
  }
@@ -2011,7 +2037,8 @@ function toXsn( csn, filename, options, messageFunctions ) {
2011
2037
  inExtensions = null;
2012
2038
  vocabInDefinitions = null;
2013
2039
 
2014
- const xsn = { $frontend: 'json' };
2040
+ const xsn = new XsnSource();
2041
+ xsn.$frontend = 'json';
2015
2042
 
2016
2043
  // eslint-disable-next-line object-curly-newline
2017
2044
  ({ message, error, warning, info } = messageFunctions);
@@ -2050,7 +2077,7 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
2050
2077
  resetHeapModuleVars();
2051
2078
  if (!(e instanceof SyntaxError))
2052
2079
  throw e;
2053
- const xsn = {};
2080
+ const xsn = new XsnSource();
2054
2081
  const msg = e.message;
2055
2082
  const p = /in JSON at position ([0-9]+)/.exec( msg );
2056
2083
  let line = 1;
@@ -2067,12 +2094,11 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
2067
2094
  }
2068
2095
  column = end - eol + 1;
2069
2096
  }
2070
- /** @type {CSN.Location} */
2071
- const loc = {
2072
- file: filename,
2097
+ const loc = new CsnLocation(
2098
+ filename,
2073
2099
  line,
2074
- col: column,
2075
- };
2100
+ column
2101
+ );
2076
2102
  messageFunctions.error( 'syntax-invalid-json', loc, { msg },
2077
2103
  'Invalid JSON: $(MSG)' );
2078
2104
  return xsn;
@@ -115,7 +115,6 @@ const transformers = {
115
115
  offset: expression,
116
116
  on: onCondition,
117
117
  // definitions, extensions, members ----------------------------------------
118
- returns, // storing the return type of actions
119
118
  notNull: value,
120
119
  default: expression,
121
120
  // targetElement: ignore, // special display of foreign key, renameTo: select
@@ -123,6 +122,7 @@ const transformers = {
123
122
  query,
124
123
  elements,
125
124
  actions, // TODO: just normal dictionary
125
+ returns, // storing the return type of actions
126
126
  // special: top-level, cardinality -----------------------------------------
127
127
  sources,
128
128
  definitions: sortedDict,
@@ -335,7 +335,8 @@ function compactModel( model, options = model.options || {} ) {
335
335
  break;
336
336
  }
337
337
  set( 'definitions', csn, model );
338
- set( 'vocabularies', csn, model );
338
+ if (Object.keys(model.vocabularies || {}).length > 0)
339
+ set( 'vocabularies', csn, model );
339
340
  const exts = extensions( model.extensions || [], csn, model );
340
341
  if (exts && exts.length)
341
342
  csn.extensions = exts;
@@ -464,13 +465,13 @@ function attachAnnotations( annotate, prop, dict, inferred, insideReturns = fals
464
465
  attachAnnotations( sub, 'actions', entry.actions, inf );
465
466
  else if (entry.params)
466
467
  attachAnnotations( sub, 'params', entry.params, inf );
467
- const obj = entry.returns || entry; // TODO: create returns !
468
+ const obj = entry.returns || entry;
468
469
  const many = obj.items || obj;
469
470
  const elems = (many.targetAspect || many).elements;
470
471
  if (elems)
471
472
  attachAnnotations( sub, 'elements', elems, inf, entry.returns );
472
- if (many.enum)
473
- attachAnnotations( sub, 'enum', many.enum, inf );
473
+ else if (many.enum) // make 'enum' annotations appear in 'elements' annotate
474
+ attachAnnotations( sub, 'elements', many.enum, inf, entry.returns );
474
475
  }
475
476
  if (Object.keys( sub ).length)
476
477
  annoDict[name] = sub;
@@ -493,10 +494,12 @@ function standard( node ) {
493
494
  // XSN input node, not the CSN result node. Not really an issue...
494
495
  const keys = Object.keys( node ).sort( compareProperties );
495
496
  for (const prop of keys) {
496
- const transformer = transformers[prop] || transformers[prop.charAt(0)] || unexpected;
497
- const sub = transformer( node[prop], csn, node, prop );
498
- if (sub !== undefined)
499
- csn[prop] = sub;
497
+ if (node[prop] !== undefined) {
498
+ const transformer = transformers[prop] || transformers[prop.charAt(0)] || unexpected;
499
+ const sub = transformer( node[prop], csn, node, prop );
500
+ if (sub !== undefined)
501
+ csn[prop] = sub;
502
+ }
500
503
  }
501
504
  return csn;
502
505
  }
@@ -715,14 +718,12 @@ function actions( dict, _csn, node ) {
715
718
  const keys = Object.keys( dict );
716
719
  if (strictMode && node.kind === 'annotate')
717
720
  keys.sort(); // TODO: always sort with --test-mode ?
718
- return (keys.length)
719
- ? dictionary( dict, keys, 'actions' )
720
- : undefined;
721
+ return dictionary( dict, keys, 'actions' );
721
722
  }
722
723
 
723
724
  function params( dict ) {
724
725
  const keys = Object.keys( dict );
725
- return (keys.length)
726
+ return (keys.length) // TODO: still?
726
727
  ? insertOrderDict( dict )
727
728
  : undefined;
728
729
  }
@@ -1212,8 +1213,7 @@ function value( node ) {
1212
1213
  function enumValueOrCalc( v, csn, node ) {
1213
1214
  if (v.$inferred && (universalCsn || gensrcFlavor))
1214
1215
  return undefined;
1215
- // Enums can have values but if enums are extended, their kind is 'element'.
1216
- // In v4, we don't check `node.$syntax === 'enum'` anymore.
1216
+ // Enums values in CSN are without outer `value: { … }`:
1217
1217
  if (node.kind === 'enum') {
1218
1218
  Object.assign( csn, expression( v ) );
1219
1219
  }
@@ -12,6 +12,7 @@ const antlr4 = require('antlr4');
12
12
 
13
13
  const { CompileMessage } = require('../base/messages');
14
14
  const errorStrategy = require('./errorStrategy');
15
+ const { XsnSource } = require('../compiler/classes');
15
16
 
16
17
  const Parser = require('../gen/languageParser').default;
17
18
  const Lexer = require('../gen/languageLexer').default;
@@ -167,7 +168,7 @@ function parse( source, filename = '<undefined>.cds',
167
168
  throw e;
168
169
  }
169
170
  }
170
- const ast = tree && tree[rulespec.returns] || {};
171
+ const ast = tree && tree[rulespec.returns] || new XsnSource();
171
172
  ast.options = options;
172
173
  if (rulespec.$frontend)
173
174
  ast.$frontend = rulespec.$frontend;
@@ -18,6 +18,9 @@ const {
18
18
  quotedLiteralPatterns,
19
19
  } = require('../compiler/builtins');
20
20
  const { pathName } = require('../compiler/utils');
21
+ const {
22
+ XsnArtifact, XsnName, CsnLocation, XsnSource,
23
+ } = require('../compiler/classes');
21
24
  const { isBetaEnabled } = require('../base/model');
22
25
  const { weakLocation } = require('../base/messages');
23
26
  const { normalizeNewLine } = require('./textUtils');
@@ -110,7 +113,6 @@ Object.assign(GenericAntlrParser.prototype, {
110
113
  addDef,
111
114
  addItem,
112
115
  addExtension,
113
- aspectWithoutElements,
114
116
  createSource,
115
117
  createDict,
116
118
  createArray,
@@ -139,6 +141,8 @@ Object.assign(GenericAntlrParser.prototype, {
139
141
  prepareGenericKeywords,
140
142
  reportErrorForGenericKeyword,
141
143
  parseMultiLineStringLiteral,
144
+ XsnArtifact,
145
+ XsnName,
142
146
  });
143
147
 
144
148
  // Use the following function for language constructs which we (currently)
@@ -160,7 +164,13 @@ function csnParseOnly( msgId, tokens, textArgs ) {
160
164
  this.error( msgId, loc, textArgs );
161
165
  }
162
166
 
163
- /** @this {object} */
167
+ /**
168
+ * Do not propose a `;` or closing brace `}` at this position.
169
+ *
170
+ * Attention: May conflict with excludeExpected()!
171
+ *
172
+ * @this {object}
173
+ * */
164
174
  function noSemicolonHere() {
165
175
  const handler = this._errHandler;
166
176
  const t = this.getCurrentToken();
@@ -173,10 +183,21 @@ function noSemicolonHere() {
173
183
  handler.reportIgnoredWith( this, t );
174
184
  }
175
185
 
176
- // Using this function "during ATN decision making" has no effect
177
- // In front of an ATN decision, you might specify dedicated excludes
178
- // for non-LA1 tokens via a sub-array in excludes[0].
179
- // TODO: consider $nextTokens…, see commented use in rule `elementProperties`
186
+ /**
187
+ * Using this function "during ATN decision making" has no effect
188
+ * In front of an ATN decision, you might specify dedicated excludes
189
+ * for non-LA1 tokens via a sub-array in excludes[0].
190
+ * TODO: consider $nextTokens…, see commented use in rule `elementProperties`
191
+ *
192
+ * Usage Note:
193
+ * Must be used at all positions where sync() is called in the generated coding.
194
+ * ```antlr4
195
+ * { this.excludeExpected(['ACTIONS']); }
196
+ * ( WITH { this.excludeExpected(['ACTIONS']); } )?
197
+ * annotationAssignment_ll1[ $art ]* { this.excludeExpected(['ACTIONS']); }
198
+ * ACTIONS
199
+ * ```
200
+ */
180
201
  function excludeExpected( excludes ) {
181
202
  if (excludes) {
182
203
  // @ts-ignore
@@ -414,6 +435,12 @@ function checkExtensionDict( dict ) {
414
435
  def[prop] = dup[prop]; // continuation semantics: last wins
415
436
  }
416
437
  }
438
+ if (dup.$annotations) { // update deprecated $annotations for cds-lsp / annotation modeler
439
+ if (def.$annotations)
440
+ def.$annotations.push( ...dup.$annotations );
441
+ else
442
+ def.$annotations = dup.$annotations;
443
+ }
417
444
  }
418
445
  def.$duplicates = null;
419
446
  }
@@ -445,14 +472,14 @@ function handleDuplicateExtension( ext, name, numDefines ) {
445
472
  * Return start location of `token`, or the first token matched by the current
446
473
  * rule if `token` is undefined
447
474
  *
448
- * @returns {CSN.Location}
475
+ * @returns {CsnLocation}
449
476
  */
450
477
  function startLocation( token = this._ctx.start ) {
451
- return {
452
- file: this.filename,
453
- line: token.line,
454
- col: token.column + 1,
455
- };
478
+ return new CsnLocation(
479
+ this.filename,
480
+ token.line,
481
+ token.column + 1
482
+ );
456
483
  }
457
484
 
458
485
  /**
@@ -461,7 +488,7 @@ function startLocation( token = this._ctx.start ) {
461
488
  *
462
489
  * @param {object} token
463
490
  * @param {object} endToken
464
- * @return {CSN.Location}
491
+ * @return {CsnLocation}
465
492
  */
466
493
  function tokenLocation( token, endToken = null ) {
467
494
  if (!token)
@@ -469,16 +496,11 @@ function tokenLocation( token, endToken = null ) {
469
496
  if (!endToken) // including null
470
497
  endToken = token;
471
498
 
472
- /** @type {CSN.Location} */
473
- const loc = {
474
- file: this.filename,
475
- line: token.line,
476
- col: token.column + 1,
477
- // Default for single line tokens
478
- endLine: endToken.line,
479
- // after the last char (special for EOF?)
480
- endCol: endToken.stop - endToken.start + endToken.column + 2,
481
- };
499
+ // Default for single line tokens
500
+ const endLine = endToken.line;
501
+ // after the last char (special for EOF?)
502
+ const endCol = endToken.stop - endToken.start + endToken.column + 2;
503
+ const loc = new CsnLocation( this.filename, token.line, token.column + 1, endLine, endCol );
482
504
 
483
505
  // This check is done for performance reason. No need to access a token's
484
506
  // data if we know that it spans only one single line.
@@ -579,7 +601,7 @@ function secureParens( expr ) {
579
601
  return {
580
602
  op: { val: 'xpr', location: this.startLocation() },
581
603
  args: [ expr ],
582
- location: { ...expr.location },
604
+ location: { __proto__: CsnLocation.prototype, ...expr.location },
583
605
  $parens,
584
606
  };
585
607
  }
@@ -733,7 +755,11 @@ function argsExpression( args, nary, location ) {
733
755
  val, // there is no n-ary in rule conditionTerm
734
756
  location: this.startLocation(),
735
757
  };
736
- return this.attachLocation( { op, args, location: location && { ...location } } );
758
+ return this.attachLocation( {
759
+ op,
760
+ args,
761
+ location: location && { __proto__: CsnLocation.prototype, ...location },
762
+ } );
737
763
  }
738
764
 
739
765
  function pushXprToken( args ) {
@@ -1097,16 +1123,6 @@ function addExtension( ext, parent, kind, artName, elemPath ) {
1097
1123
  }
1098
1124
  }
1099
1125
 
1100
- function aspectWithoutElements( art ) {
1101
- // Empty dictionary to allow element extensions. NO, please NO empty dict.
1102
- // TODO: Checking it here does not prevent aspect in CSN input having no elements!
1103
- art.elements = this.createDict();
1104
- if (!isBetaEnabled( this.options, 'aspectWithoutElements' )) {
1105
- this.error( null, [ art.name.location, null ], {},
1106
- 'Aspects without elements are not supported, yet' );
1107
- }
1108
- }
1109
-
1110
1126
  // must be in action directly after having parsed '{', '(`, or a keyword before
1111
1127
  function createDict() {
1112
1128
  const dict = Object.create(null);
@@ -1132,14 +1148,7 @@ function finalizeDictOrArray( dict ) {
1132
1148
  }
1133
1149
 
1134
1150
  function createSource() {
1135
- return {
1136
- kind: 'source',
1137
- usings: [],
1138
- dependencies: [],
1139
- artifacts: Object.create(null),
1140
- // vocabularies: Object.create(null),
1141
- extensions: [],
1142
- };
1151
+ return new XsnSource();
1143
1152
  }
1144
1153
 
1145
1154
  // Create AST node for prefix operator `op` and arguments `args`