@sap/cds-compiler 3.1.0 → 3.3.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 (100) hide show
  1. package/CHANGELOG.md +90 -3
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +18 -0
  4. package/lib/api/main.js +8 -13
  5. package/lib/base/error.js +2 -2
  6. package/lib/base/keywords.js +2 -24
  7. package/lib/base/message-registry.js +43 -14
  8. package/lib/base/messages.js +20 -10
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/actionsFunctions.js +1 -1
  11. package/lib/checks/annotationsOData.js +2 -2
  12. package/lib/checks/arrayOfs.js +15 -7
  13. package/lib/checks/cdsPersistence.js +1 -1
  14. package/lib/checks/checkForTypes.js +48 -0
  15. package/lib/checks/defaultValues.js +2 -2
  16. package/lib/checks/elements.js +81 -6
  17. package/lib/checks/foreignKeys.js +12 -13
  18. package/lib/checks/invalidTarget.js +10 -11
  19. package/lib/checks/managedInType.js +21 -15
  20. package/lib/checks/nullableKeys.js +1 -1
  21. package/lib/checks/onConditions.js +9 -9
  22. package/lib/checks/parameters.js +21 -0
  23. package/lib/checks/selectItems.js +1 -1
  24. package/lib/checks/types.js +2 -2
  25. package/lib/checks/utils.js +17 -7
  26. package/lib/checks/validator.js +26 -14
  27. package/lib/compiler/assert-consistency.js +13 -6
  28. package/lib/compiler/builtins.js +8 -0
  29. package/lib/compiler/checks.js +40 -33
  30. package/lib/compiler/define.js +50 -44
  31. package/lib/compiler/extend.js +303 -37
  32. package/lib/compiler/kick-start.js +2 -35
  33. package/lib/compiler/populate.js +83 -62
  34. package/lib/compiler/propagator.js +1 -1
  35. package/lib/compiler/resolve.js +61 -104
  36. package/lib/compiler/shared.js +16 -6
  37. package/lib/compiler/tweak-assocs.js +25 -12
  38. package/lib/compiler/utils.js +2 -2
  39. package/lib/edm/annotations/genericTranslation.js +15 -5
  40. package/lib/edm/csn2edm.js +10 -10
  41. package/lib/edm/edm.js +17 -9
  42. package/lib/edm/edmPreprocessor.js +82 -42
  43. package/lib/edm/edmUtils.js +18 -16
  44. package/lib/gen/Dictionary.json +14 -0
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +3 -2
  47. package/lib/gen/languageParser.js +4205 -4100
  48. package/lib/inspect/inspectModelStatistics.js +1 -1
  49. package/lib/inspect/inspectPropagation.js +23 -9
  50. package/lib/json/csnVersion.js +1 -1
  51. package/lib/json/from-csn.js +26 -19
  52. package/lib/json/to-csn.js +47 -5
  53. package/lib/language/antlrParser.js +1 -1
  54. package/lib/language/genericAntlrParser.js +29 -13
  55. package/lib/language/language.g4 +28 -8
  56. package/lib/main.d.ts +3 -6
  57. package/lib/model/.eslintrc.json +13 -0
  58. package/lib/model/api.js +4 -2
  59. package/lib/model/csnRefs.js +74 -47
  60. package/lib/model/csnUtils.js +236 -218
  61. package/lib/model/enrichCsn.js +41 -31
  62. package/lib/model/revealInternalProperties.js +61 -57
  63. package/lib/model/sortViews.js +31 -31
  64. package/lib/modelCompare/compare.js +6 -6
  65. package/lib/optionProcessor.js +5 -0
  66. package/lib/render/manageConstraints.js +2 -2
  67. package/lib/render/toCdl.js +31 -44
  68. package/lib/render/toHdbcds.js +7 -5
  69. package/lib/render/toRename.js +4 -4
  70. package/lib/render/toSql.js +11 -5
  71. package/lib/render/utils/common.js +20 -9
  72. package/lib/render/utils/sql.js +5 -5
  73. package/lib/transform/db/applyTransformations.js +32 -3
  74. package/lib/transform/db/expansion.js +81 -37
  75. package/lib/transform/db/flattening.js +1 -1
  76. package/lib/transform/db/temporal.js +1 -1
  77. package/lib/transform/db/transformExists.js +1 -1
  78. package/lib/transform/forOdataNew.js +10 -7
  79. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
  80. package/lib/transform/localized.js +28 -19
  81. package/lib/transform/odata/toFinalBaseType.js +8 -11
  82. package/lib/transform/odata/typesExposure.js +1 -1
  83. package/lib/transform/transformUtilsNew.js +101 -39
  84. package/lib/transform/translateAssocsToJoins.js +5 -4
  85. package/lib/utils/moduleResolve.js +5 -5
  86. package/lib/utils/objectUtils.js +3 -3
  87. package/package.json +2 -2
  88. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  89. package/share/messages/check-proper-type-of.md +4 -4
  90. package/share/messages/check-proper-type.md +2 -2
  91. package/share/messages/duplicate-autoexposed.md +4 -4
  92. package/share/messages/extend-repeated-intralayer.md +4 -5
  93. package/share/messages/extend-unrelated-layer.md +4 -4
  94. package/share/messages/message-explanations.json +3 -1
  95. package/share/messages/redirected-to-ambiguous.md +7 -6
  96. package/share/messages/redirected-to-complex.md +63 -0
  97. package/share/messages/redirected-to-unrelated.md +6 -5
  98. package/share/messages/rewrite-not-supported.md +4 -4
  99. package/share/messages/syntax-expected-integer.md +3 -3
  100. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -14,7 +14,7 @@ function inspectModelStatistics(xsn, options) {
14
14
  let result = '';
15
15
 
16
16
  // Default color mode is 'auto'
17
- const color = term(options.color || 'auto');
17
+ const color = term(options.color !== undefined ? options.color : 'auto');
18
18
 
19
19
  const defCount = countDefinitionKinds(xsn);
20
20
  const sources = {
@@ -16,7 +16,7 @@ function inspectPropagation(xsn, options, artifactName) {
16
16
  const result = [];
17
17
 
18
18
  // Default color mode is 'auto'
19
- const color = term(options.color || 'auto');
19
+ const color = term(options.color !== undefined ? options.color : 'auto');
20
20
 
21
21
  const path = stringRefToPath(artifactName);
22
22
  if (!path) {
@@ -121,19 +121,28 @@ function _inspectElements(artifactXsn) {
121
121
  const result = [];
122
122
  const elements = Object.keys(artifactXsn.elements);
123
123
 
124
- let maxElemLength = 12;
125
-
126
124
  const inferredNiceOutput = {
127
125
  '*': 'wildcard',
128
- 'expand-element': 'expanded',
129
- 'expand-param': 'expanded',
130
126
  'aspect-composition': 'composition',
131
127
  };
132
128
 
129
+ let maxElemLength = 12;
130
+ let maxOriginLength = 6;
131
+
132
+ // type: assume max length 11 of 'composition'
133
+ // element: assume average length of 30, chosen randomly
134
+ result.push([
135
+ 'type'.padStart(11),
136
+ 'element'.padEnd(maxElemLength),
137
+ 'origin'.padEnd(maxOriginLength),
138
+ 'location (definition)',
139
+ ].join(' | '));
140
+
133
141
  for (const element of elements) {
134
142
  const elementXsn = artifactXsn.elements[element];
135
143
  const loc = locationString(_origin(elementXsn).name.location);
136
144
  let origin;
145
+ const originName = elementXsn._origin?.name?.absolute || '';
137
146
 
138
147
  if (elementXsn.$inferred) {
139
148
  // Use nice(r) output for known $inferred
@@ -151,11 +160,16 @@ function _inspectElements(artifactXsn) {
151
160
  }
152
161
 
153
162
  maxElemLength = Math.max(maxElemLength, element.length);
154
-
155
- // origin: assume max length 11 of 'composition'
156
- // element: assume average length of 30, chosen randomly
157
- result.push([ origin.padStart(11), element.padEnd(maxElemLength), loc ].join(' | '));
163
+ maxOriginLength = Math.max(maxOriginLength, originName.length);
164
+
165
+ result.push([
166
+ origin.padStart(11),
167
+ element.padEnd(maxElemLength),
168
+ originName.padEnd(maxOriginLength),
169
+ loc,
170
+ ].join(' | '));
158
171
  }
172
+
159
173
  return result;
160
174
  }
161
175
 
@@ -39,7 +39,7 @@ function checkCSNVersion(csn, options) {
39
39
  errStr += `${ csn.version && csn.version.csn ? csn.version.csn : (csn.$version ? csn.$version : 'not available') }"`;
40
40
  errStr += (options.newCsn !== undefined) ? `, options.newCsn: ${ options.newCsn }` : '';
41
41
 
42
- error(null, null, errStr);
42
+ error(null, null, {}, errStr);
43
43
  throwWithAnyError();
44
44
  }
45
45
  }
@@ -117,13 +117,14 @@ const exprProperties = [
117
117
  const xorGroups = {
118
118
  // include CSN v0.1.0 properties here:
119
119
  ':type': [ 'target', 'elements', 'enum', 'items' ],
120
- ':expr': [
121
- 'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET',
120
+ ':expr': [ // see also xorException property in schema
121
+ 'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET', 'expand',
122
122
  '=', 'path', 'value', 'op', // '='/'path' is CSN v0.1.0 here
123
123
  ],
124
124
  ':ext': [ 'annotate', 'extend' ], // TODO: better msg for test/negative/UnexpectedProperties.csn
125
125
  ':assoc': [ 'on', 'keys', 'foreignKeys', 'onCond' ], // 'foreignKeys'/'onCond' is CSN v0.1.0
126
- ':join': [ 'join', 'as' ],
126
+ // TODO - improve consequential errors: assume no name given with `join` or `inline`?
127
+ as: [ 'as', 'join', 'inline' ],
127
128
  scope: [ 'param', 'global' ],
128
129
  quantifier: [ 'some', 'any', 'distinct', 'all' ],
129
130
  // quantifiers 'some' and 'any are 'xpr' token strings in CSN v1.0
@@ -155,11 +156,12 @@ const schemaClasses = {
155
156
  defaultKind: '$column',
156
157
  validKinds: [], // pseudo kind '$column'
157
158
  // A column with only as+cast.type is a new association
158
- requires: [ 'ref', 'as', 'xpr', 'val', '#', 'func', 'list', 'SELECT', 'SET', 'expand' ],
159
+ requires: [ 'ref', 'cast', 'xpr', 'val', '#', 'func', 'list', 'SELECT', 'SET', 'expand' ],
159
160
  schema: {
160
161
  xpr: {
161
162
  class: 'condition',
162
163
  type: xprInValue,
164
+ xorException: 'func', // see xorGroup :expr
163
165
  inKind: [ '$column' ],
164
166
  inValue: true,
165
167
  },
@@ -248,10 +250,12 @@ const schema = compileSchema( {
248
250
  },
249
251
  expand: {
250
252
  class: 'columns',
251
- inKind: [ '$column' ], // only valid in $column
253
+ xorException: 'ref', // see xorGroup :expr
254
+ inKind: [ '$column' ], // only valid in $column
252
255
  },
253
256
  inline: {
254
257
  class: 'columns',
258
+ onlyWith: 'ref',
255
259
  inKind: [ '$column' ], // only valid in $column
256
260
  },
257
261
  keys: {
@@ -294,7 +298,7 @@ const schema = compileSchema( {
294
298
  type: artifactRef,
295
299
  msgId: 'syntax-expected-reference',
296
300
  optional: [ 'ref', 'global' ],
297
- inKind: [ 'element', 'type', 'param', 'mixin', 'event', 'annotation' ],
301
+ inKind: [ 'element', 'type', 'param', 'mixin', 'event', 'annotation', 'extend' ],
298
302
  },
299
303
  targetAspect: {
300
304
  type: artifactRef,
@@ -323,20 +327,20 @@ const schema = compileSchema( {
323
327
  },
324
328
  length: {
325
329
  type: natnum,
326
- inKind: [ 'element', 'type', 'param', 'annotation' ],
330
+ inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
327
331
  // we do not require a 'type', too - could be useful alone in a 'cast'
328
332
  },
329
333
  precision: {
330
334
  type: natnum,
331
- inKind: [ 'element', 'type', 'param', 'annotation' ],
335
+ inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
332
336
  },
333
337
  scale: {
334
338
  type: scalenum,
335
- inKind: [ 'element', 'type', 'param', 'annotation' ],
339
+ inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
336
340
  },
337
341
  srid: {
338
342
  type: natnum,
339
- inKind: [ 'element', 'type', 'param', 'annotation' ],
343
+ inKind: [ 'element', 'type', 'param', 'annotation' ], // no 'extend'!
340
344
  },
341
345
  srcmin: { // in 'cardinality'
342
346
  type: renameTo( 'sourceMin', natnum ),
@@ -372,6 +376,7 @@ const schema = compileSchema( {
372
376
  minLength: 1,
373
377
  requires: 'id',
374
378
  optional: [ 'id', 'args', 'cardinality', 'where' ],
379
+ xorException: 'expand', // see xorGroup :expr
375
380
  inKind: [ '$column', 'key' ],
376
381
  },
377
382
  id: { // in 'ref' item
@@ -389,6 +394,7 @@ const schema = compileSchema( {
389
394
  },
390
395
  func: {
391
396
  type: func,
397
+ xorException: 'xpr', // see xorGroup :expr
392
398
  inKind: [ '$column' ],
393
399
  },
394
400
  args: {
@@ -405,6 +411,7 @@ const schema = compileSchema( {
405
411
  xpr: {
406
412
  class: 'condition',
407
413
  type: xpr,
414
+ xorException: 'func', // see xorGroup :expr
408
415
  // special treatment in $column
409
416
  },
410
417
  list: {
@@ -747,6 +754,7 @@ function compileSchema( specs, proto = null) {
747
754
  }
748
755
  if (proto)
749
756
  return r;
757
+ // Set property 'inValue' in main schema only:
750
758
  for (const prop of exprProperties) {
751
759
  if (r[prop].inValue === undefined)
752
760
  r[prop].inValue = true;
@@ -1559,7 +1567,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
1559
1567
  } );
1560
1568
  // TODO: or still augment it? (but then also handle xorGroup)
1561
1569
  }
1562
- else if (checkAndSetXorGroup( s.xorGroup, prop, xor )) {
1570
+ else if (checkAndSetXorGroup( s.xorGroup, s.xorException, prop, xor )) {
1563
1571
  onlyWith( s, s.onlyWith, csn, prop, xor, expected );
1564
1572
  return s;
1565
1573
  }
@@ -1622,18 +1630,17 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
1622
1630
  return spec;
1623
1631
  }
1624
1632
 
1625
- function checkAndSetXorGroup( group, prop, xor ) {
1633
+ function checkAndSetXorGroup( group, exception, prop, xor ) {
1626
1634
  if (!group)
1627
1635
  return true;
1628
- if (!xor[group]) {
1636
+ const otherprop = xor[group];
1637
+ if (!otherprop) {
1629
1638
  xor[group] = prop;
1630
1639
  return true;
1631
1640
  }
1632
- if (prop === 'func' && xor[group] === 'xpr' ||
1633
- prop === 'xpr' && xor[group] === 'func')
1634
- return true; // hack for window function: both func and xpr is allowed
1635
- error( 'syntax-excluded-property', location(true),
1636
- { prop, otherprop: xor[group] },
1641
+ if (otherprop === exception)
1642
+ return true;
1643
+ error( 'syntax-excluded-property', location(true), { prop, otherprop },
1637
1644
  'CSN property $(PROP) can only be used alternatively to $(OTHERPROP)');
1638
1645
  return false;
1639
1646
  }
@@ -1767,7 +1774,7 @@ function toXsn( csn, filename, options, messageFunctions ) {
1767
1774
  ({ message, error, warning, info } = messageFunctions);
1768
1775
 
1769
1776
  if (csnVersionZero) {
1770
- warning( 'syntax-csn-zero-version', location(true),
1777
+ warning( 'syntax-csn-zero-version', location(true), {},
1771
1778
  'Parsing CSN version 0.1.0' );
1772
1779
  }
1773
1780
  const r = object( csn, topLevelSpec );
@@ -252,9 +252,10 @@ function sortCsn( csn, cloneOptions = false ) {
252
252
  const sortDict = n === 'definitions' &&
253
253
  (!cloneOptions || cloneOptions.testMode || cloneOptions.testSortCsn);
254
254
  const val = csn[n];
255
- if (!val || typeof val !== 'object' || n.charAt(0) === '@' || csnDirectValues.includes(n))
255
+ if (!val || typeof val !== 'object' || csnDirectValues.includes(n))
256
256
  r[n] = val;
257
-
257
+ else if (n.charAt(0) === '@')
258
+ r[n] = cloneAnnotationValue( val );
258
259
  else if (csnDictionaries.includes(n) && !Array.isArray(val))
259
260
  // Array check for property `args` which may either be a dictionary or an array.
260
261
  r[n] = csnDictionary( val, sortDict, cloneOptions );
@@ -279,6 +280,12 @@ function sortCsn( csn, cloneOptions = false ) {
279
280
  return r;
280
281
  }
281
282
 
283
+ function cloneAnnotationValue(val) {
284
+ if (typeof val !== 'object') // scalar
285
+ return val;
286
+ return JSON.parse( JSON.stringify( val ) );
287
+ }
288
+
282
289
  /**
283
290
  * Check whether the given object has non enumerable property.
284
291
  * Ensure that we don't take it from the prototype, only "directly" - we accidentally
@@ -663,7 +670,9 @@ function elements( dict, csn, node ) {
663
670
  // no 'elements' with SELECT or inferred elements with gensrc;
664
671
  // hidden or visible 'elements' will be set in query()
665
672
  return undefined;
666
- return insertOrderDict( dict );
673
+ if (dict !== 0)
674
+ return insertOrderDict( dict );
675
+ return undefined;
667
676
  }
668
677
 
669
678
  function enumDict( dict, csn, node ) {
@@ -927,6 +936,11 @@ function addOrigin( csn, xsn, node ) {
927
936
  let origin = getOrigin( node );
928
937
  if (xsn.$inferred === 'composition-entity') {
929
938
  csn.$origin = originRef( origin, xsn );
939
+ inferredPropertiesForOrigin( csn, node );
940
+ return;
941
+ }
942
+ else if (xsn.$inferred === 'localized-entity') {
943
+ inferredPropertiesForOrigin( csn, node );
930
944
  return;
931
945
  }
932
946
  else if (!isMember( xsn ) || xsn.kind === 'select') {
@@ -993,6 +1007,30 @@ function addOrigin( csn, xsn, node ) {
993
1007
  }
994
1008
  }
995
1009
 
1010
+ /**
1011
+ * Copy properties with $inferred === 'parent-origin' to $origin.
1012
+ * This indicates that the property is neither direct nor can be inferred through $origin.
1013
+ *
1014
+ * @param csn
1015
+ * @param node
1016
+ */
1017
+ function inferredPropertiesForOrigin( csn, node ) {
1018
+ let hasProp = false;
1019
+ const props = {};
1020
+ for (const prop of Object.keys(node)) {
1021
+ if (node[prop]?.$inferred === 'parent-origin') {
1022
+ hasProp = true;
1023
+ props[prop] = value({ ...node[prop], $inferred: false });
1024
+ }
1025
+ }
1026
+ const origin = csn.$origin;
1027
+ if (hasProp) {
1028
+ csn.$origin = props;
1029
+ if (origin)
1030
+ csn.$origin.$origin = origin;
1031
+ }
1032
+ }
1033
+
996
1034
  function getParent( art ) {
997
1035
  const parent = art._parent;
998
1036
  const main = parent._main;
@@ -1092,7 +1130,9 @@ function type( node, csn, xsn ) {
1092
1130
  return undefined;
1093
1131
  if (xsn._origin) {
1094
1132
  if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
1095
- csn.$origin = definition( xsn._origin );
1133
+ const $origin = definition( xsn._origin );
1134
+ if ($origin) // if not rendered as column
1135
+ csn.$origin = $origin;
1096
1136
  }
1097
1137
  }
1098
1138
  return artifactRef( node, !node.$extra );
@@ -1210,7 +1250,8 @@ function value( node ) {
1210
1250
  if (!node)
1211
1251
  return true; // `@aBool` short for `@aBool: true`
1212
1252
  if (universalCsn && node.$inferred) {
1213
- if (node.$inferred === 'prop' || node.$inferred === '$generated') // via propagator.js
1253
+ if (node.$inferred === 'prop' || node.$inferred === '$generated' || // via propagator.js
1254
+ node.$inferred === 'parent-origin')
1214
1255
  return undefined;
1215
1256
  else if (node.$inferred === 'NULL')
1216
1257
  return null;
@@ -1610,6 +1651,7 @@ function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
1610
1651
 
1611
1652
  module.exports = {
1612
1653
  cloneCsnDictionary: (csn, options) => csnDictionary(csn, false, options),
1654
+ cloneAnnotationValue,
1613
1655
  compactModel,
1614
1656
  compactQuery,
1615
1657
  compactExpr,
@@ -25,7 +25,7 @@ class ErrorListener extends antlr4.error.ErrorListener {
25
25
  // method which is called by generated parser with --trace-parser[-amg]:
26
26
  syntaxError( recognizer, offendingSymbol, line, column, msg, e ) {
27
27
  if (!(e instanceof CompileMessage)) // not already reported
28
- recognizer.error( null, offendingSymbol, msg );
28
+ recognizer.error( null, offendingSymbol, {}, msg );
29
29
  }
30
30
  }
31
31
 
@@ -133,7 +133,7 @@ function notSupportedYet( text, ...tokens ) {
133
133
  tokens = [ text, ...tokens ];
134
134
  text = `${ tokens.map( t => t.text.toUpperCase() ).join(' ') } is not supported`;
135
135
  }
136
- this.error( null, this.tokenLocation( tokens[0], tokens[tokens.length - 1] ), text );
136
+ this.error( null, this.tokenLocation( tokens[0], tokens[tokens.length - 1] ), {}, text );
137
137
  }
138
138
 
139
139
  // Use the following function for language constructs which we (currently) do
@@ -146,7 +146,7 @@ function csnParseOnly( text, ...tokens ) {
146
146
  tokens = [ text, ...tokens ];
147
147
  text = `${ tokens.map( t => t.text.toUpperCase() ).join(' ') } is not supported`;
148
148
  }
149
- this.error( null, this.tokenLocation( tokens[0], tokens[tokens.length - 1] ), text );
149
+ this.error( null, this.tokenLocation( tokens[0], tokens[tokens.length - 1] ), {}, text );
150
150
  }
151
151
 
152
152
  /** @this {object} */
@@ -607,6 +607,8 @@ function classifyImplicitName( category, ref ) {
607
607
  }
608
608
 
609
609
  function fragileAlias( ast, safe = false ) {
610
+ if (this.getCurrentToken().text === '.')
611
+ return ast;
610
612
  if (safe || ast.$delimited || !/^[a-zA-Z][a-zA-Z_]+$/.test( ast.id ))
611
613
  this.warning( 'syntax-sloppy-alias', ast.location, { keyword: 'as' },
612
614
  'Please add the keyword $(KEYWORD) in front of the alias name' );
@@ -687,8 +689,8 @@ function valuePathAst( ref ) {
687
689
  const item = path.find( i => i.args && i.$syntax !== ':' );
688
690
  if (!item)
689
691
  return ref;
690
- this.error( 'syntax-not-supported', item.location,
691
- 'Methods in expressions are not supported yet' );
692
+ this.error( 'syntax-not-supported', item.location, {},
693
+ 'Methods in expressions are not supported yet' );
692
694
  path.broken = true;
693
695
  path.length = 1;
694
696
  }
@@ -1063,15 +1065,29 @@ function associationInSelectItem( art ) {
1063
1065
  }
1064
1066
  }
1065
1067
 
1066
- function reportExpandInline( clauseName ) {
1067
- let token = this.getCurrentToken();
1068
- // improve error location when using "inline" `.{…}` after ref (arguments and
1069
- // filters not covered, not worth the effort); after an expression where
1070
- // the last token is an identifier, not the `.` is wrong, but the `{`:
1071
- if (token.text === '.' && this._input.LT(-1).type >= this.constructor.Identifier)
1072
- token = this._input.LT(2);
1073
- this.error( 'syntax-unexpected-refclause', token, { prop: clauseName },
1074
- 'Unexpected nested $(PROP), can only be used after a reference' );
1068
+ function reportExpandInline( column, isInline ) {
1069
+ const { name } = column;
1070
+ if (column.value && !column.value.path) {
1071
+ let token = this.getCurrentToken();
1072
+ // improve error location when using "inline" `.{…}` after ref (arguments and
1073
+ // filters not covered, not worth the effort); after an expression where
1074
+ // the last token is an identifier, not the `.` is wrong, but the `{`:
1075
+ if (isInline && !name && this._input.LT(-1).type >= this.constructor.Identifier)
1076
+ token = this._input.LT(2);
1077
+ this.error( 'syntax-unexpected-nested-proj', token,
1078
+ { prop: isInline ? 'inline' : 'expand' },
1079
+ { std: 'Unexpected nested $(PROP), can only be used after a reference' } );
1080
+ // continuation semantics:
1081
+ // - add elements anyway (could lead to duplicate errors as usual)
1082
+ // - no errors for refs inside expand/inline, but for refs in sibling expr
1083
+ // - think about: reference to these (sub) elements from other view
1084
+ }
1085
+ if (isInline && name) {
1086
+ const location = this.tokenLocation( isInline, this._input.LT(-1) );
1087
+ this.error( 'syntax-unexpected-alias', location, { prop: 'inline' },
1088
+ 'Unexpected alias name before nested $(PROP)' );
1089
+ // continuation semantics: ignore AS
1090
+ }
1075
1091
  }
1076
1092
 
1077
1093
  function checkTypeFacet( art, argIdent ) {
@@ -751,7 +751,7 @@ extendType[ art, outer ] locals[ name = {} ]
751
751
  { $art.expectedKind = 'type'; $art.name = $name;
752
752
  this.addItem( $art, $outer, 'extensions', 'extend' );
753
753
  }
754
- extendWithOptElements[ $art, $art ]
754
+ extendWithOptElementsOrType[ $art, $art ]
755
755
  ;
756
756
 
757
757
  annotationDef[ art, outer ] locals[ name = {} ]
@@ -840,11 +840,16 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
840
840
  enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
841
841
  '}' { this.finalizeDictOrArray( $art.enum ); }
842
842
  optionalSemi
843
+ |
844
+ // extend Art with (length: 10);
845
+ // `with` is required, or we could have `extend String(length:10);`.
846
+ typeNamedArgList[ $art ]
847
+ requiredSemi
843
848
  )
844
849
  )
845
850
  ;
846
851
 
847
- extendWithOptElements[ art ]
852
+ extendWithOptElementsOrType[ art ]
848
853
  :
849
854
  WITH { this.noSemicolonHere(); this.docComment( $art ); }
850
855
  annotationAssignment_ll1[ $art ]*
@@ -857,6 +862,10 @@ extendWithOptElements[ art ]
857
862
  '}' { this.finalizeDictOrArray( $art.elements ); }
858
863
  { this.checkExtensionDict( $art.elements ); }
859
864
  optionalSemi
865
+ |
866
+ // extend type|element Art with (length: 10);
867
+ typeNamedArgList[ $art ]
868
+ requiredSemi
860
869
  |
861
870
  requiredSemi
862
871
  )
@@ -1220,7 +1229,7 @@ extendElement[ art, outer ]
1220
1229
  ( expected=ELEMENT { $art.expectedKind = 'element'; } )?
1221
1230
  name=ident['Element']
1222
1231
  { this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
1223
- extendWithOptElements[ $art, $art ]
1232
+ extendWithOptElementsOrType[ $art, $art ]
1224
1233
  ;
1225
1234
 
1226
1235
  selectItemDef[ outer ] locals[ art ]
@@ -1246,19 +1255,20 @@ selectItemDefBody[ art, outer ]
1246
1255
  e=expression { $art.value = $e.expr; }
1247
1256
  // we cannot use 'condition' instead, as long as we allow aliases without
1248
1257
  // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1249
- ( AS n1=ident['Item'] { $art.name = $n1.id }
1258
+ ( as=AS n1=ident['Item'] { $art.name = $n1.id }
1250
1259
  | n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
1251
1260
  | { if (this.getCurrentToken().text !== '.') this.classifyImplicitName( 'Item', $e.expr ); }
1252
1261
  )
1253
- { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] ); }
1262
+ { if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
1263
+ else if ($art.name) this.excludeExpected( ["'.'"] );
1264
+ }
1254
1265
  (
1255
- { if ($art.value && !$art.value.path) this.reportExpandInline( 'expand' ); }
1266
+ { this.reportExpandInline( $art, false ); }
1256
1267
  selectItemInlineList[ $art, 'expand' ]
1257
1268
  excludingClause[ $art ]?
1258
1269
  // TODO: we might alternatively allow AS here
1259
1270
  |
1260
- // TODO: complain if AS has been used - or in definer?
1261
- { if ($art.value && !$art.value.path) this.reportExpandInline( 'inline' ); }
1271
+ { this.reportExpandInline( $art, $as || this._input.LT(-1) ); }
1262
1272
  DOTbeforeBRACE // ...orASTERISK
1263
1273
  (
1264
1274
  selectItemInlineList[ $art, 'inline' ]
@@ -1799,6 +1809,16 @@ typeRefArgs[ art ]
1799
1809
  ')'{ this.finalizeDictOrArray( $art['$'+'typeArgs']); }
1800
1810
  ;
1801
1811
 
1812
+ typeNamedArgList[ art ]
1813
+ :
1814
+ paren='('
1815
+ typeNamedArg[ $art ]
1816
+ ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1817
+ typeNamedArg[ $art ]
1818
+ )*
1819
+ ')'
1820
+ ;
1821
+
1802
1822
  typeNamedArg[ art ] locals[ arg = '' ]
1803
1823
  :
1804
1824
  name=ident['paramname']
package/lib/main.d.ts CHANGED
@@ -786,15 +786,12 @@ declare namespace compiler {
786
786
  */
787
787
  export type CdlResult = {
788
788
  /**
789
- * Rendered model, excluding not-applied extensions.
789
+ * Rendered model, including extensions.
790
790
  */
791
791
  model?: string
792
792
  /**
793
- * Rendered extend/annotate statements of `csn.extensions`
794
- */
795
- unappliedExtensions?: string
796
- /**
797
- * Rendered csn.namespace property + using directives.
793
+ * Rendered `csn.namespace` property + using directives.
794
+ * Useful to keep the `csn.namespace` property when re-compiling the to.cdl() result.
798
795
  */
799
796
  namespace?: string
800
797
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "root": true,
3
+ "extends": "../../.eslintrc-ydkjsi.json",
4
+ "rules": {
5
+ "max-len": [ "error", {
6
+ "code": 180, // TODO: Remove
7
+ "tabWidth": 2,
8
+ "ignoreRegExpLiterals": false,
9
+ "ignoreStrings": false,
10
+ "ignoreTemplateLiterals": true
11
+ }]
12
+ }
13
+ }
package/lib/model/api.js CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Do not change at will - they are in the compiler API!
4
4
 
5
+ 'use strict';
6
+
5
7
  /**
6
8
  * Dictionary of default traversal functions for function `traverseCsn`.
7
9
  * It maps CSN property names to functions which are used by default
@@ -23,8 +25,8 @@ const defaultFunctions = {
23
25
  actions: dictionary,
24
26
  mixin: dictionary,
25
27
  definitions: dictionary,
26
- '$': () => { /* do not traverse properties starting with '$' */},
27
- }
28
+ $: () => { /* do not traverse properties starting with '$' */ },
29
+ };
28
30
 
29
31
  /**
30
32
  * Traverse the CSN node `csn`.