@sap/cds-compiler 3.4.4 → 3.5.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 (129) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +1 -0
  3. package/bin/cds_update_identifiers.js +5 -5
  4. package/bin/cdsc.js +12 -12
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +9 -1
  7. package/doc/CHANGELOG_DEPRECATED.md +2 -0
  8. package/lib/api/main.js +58 -59
  9. package/lib/api/options.js +4 -2
  10. package/lib/api/validate.js +2 -2
  11. package/lib/base/cleanSymbols.js +2 -3
  12. package/lib/base/dictionaries.js +6 -6
  13. package/lib/base/error.js +2 -2
  14. package/lib/base/keywords.js +6 -6
  15. package/lib/base/location.js +11 -12
  16. package/lib/base/message-registry.js +124 -28
  17. package/lib/base/messages.js +247 -179
  18. package/lib/base/model.js +14 -11
  19. package/lib/base/node-helpers.js +9 -10
  20. package/lib/base/optionProcessorHelper.js +138 -129
  21. package/lib/checks/actionsFunctions.js +5 -5
  22. package/lib/checks/annotationsOData.js +4 -4
  23. package/lib/checks/arrayOfs.js +1 -1
  24. package/lib/checks/cdsPersistence.js +1 -1
  25. package/lib/checks/checkForTypes.js +3 -3
  26. package/lib/checks/defaultValues.js +3 -3
  27. package/lib/checks/elements.js +7 -7
  28. package/lib/checks/emptyOrOnlyVirtual.js +2 -2
  29. package/lib/checks/foreignKeys.js +1 -1
  30. package/lib/checks/invalidTarget.js +4 -4
  31. package/lib/checks/managedInType.js +1 -1
  32. package/lib/checks/managedWithoutKeys.js +1 -1
  33. package/lib/checks/nonexpandableStructured.js +5 -3
  34. package/lib/checks/nullableKeys.js +1 -1
  35. package/lib/checks/onConditions.js +5 -6
  36. package/lib/checks/parameters.js +1 -1
  37. package/lib/checks/queryNoDbArtifacts.js +2 -2
  38. package/lib/checks/selectItems.js +4 -4
  39. package/lib/checks/sql-snippets.js +4 -4
  40. package/lib/checks/types.js +7 -7
  41. package/lib/checks/utils.js +4 -4
  42. package/lib/checks/validator.js +16 -13
  43. package/lib/compiler/.eslintrc.json +1 -1
  44. package/lib/compiler/assert-consistency.js +0 -1
  45. package/lib/compiler/builtins.js +1 -1
  46. package/lib/compiler/checks.js +73 -15
  47. package/lib/compiler/define.js +3 -7
  48. package/lib/compiler/extend.js +212 -32
  49. package/lib/compiler/finalize-parse-cdl.js +7 -2
  50. package/lib/compiler/index.js +17 -14
  51. package/lib/compiler/populate.js +2 -5
  52. package/lib/compiler/propagator.js +2 -0
  53. package/lib/compiler/shared.js +23 -12
  54. package/lib/compiler/tweak-assocs.js +5 -6
  55. package/lib/compiler/utils.js +6 -0
  56. package/lib/edm/annotations/genericTranslation.js +553 -319
  57. package/lib/edm/annotations/preprocessAnnotations.js +39 -35
  58. package/lib/edm/csn2edm.js +88 -75
  59. package/lib/edm/edm.js +17 -3
  60. package/lib/edm/edmAnnoPreprocessor.js +5 -5
  61. package/lib/edm/edmPreprocessor.js +106 -76
  62. package/lib/edm/edmUtils.js +41 -2
  63. package/lib/gen/Dictionary.json +34 -0
  64. package/lib/gen/language.checksum +1 -1
  65. package/lib/gen/language.interp +66 -63
  66. package/lib/gen/language.tokens +81 -81
  67. package/lib/gen/languageLexer.interp +4 -10
  68. package/lib/gen/languageLexer.js +854 -869
  69. package/lib/gen/languageLexer.tokens +79 -81
  70. package/lib/gen/languageParser.js +14360 -14146
  71. package/lib/inspect/inspectModelStatistics.js +2 -2
  72. package/lib/inspect/inspectPropagation.js +6 -6
  73. package/lib/inspect/inspectUtils.js +2 -2
  74. package/lib/json/from-csn.js +82 -40
  75. package/lib/json/to-csn.js +82 -157
  76. package/lib/language/.eslintrc.json +1 -4
  77. package/lib/language/genericAntlrParser.js +59 -38
  78. package/lib/language/language.g4 +1508 -1490
  79. package/lib/language/multiLineStringParser.js +1 -1
  80. package/lib/main.js +3 -3
  81. package/lib/model/csnUtils.js +130 -122
  82. package/lib/model/revealInternalProperties.js +1 -1
  83. package/lib/model/sortViews.js +4 -6
  84. package/lib/modelCompare/utils/filter.js +4 -3
  85. package/lib/optionProcessor.js +5 -0
  86. package/lib/render/DuplicateChecker.js +1 -1
  87. package/lib/render/manageConstraints.js +12 -12
  88. package/lib/render/toCdl.js +225 -159
  89. package/lib/render/toHdbcds.js +63 -63
  90. package/lib/render/toRename.js +5 -5
  91. package/lib/render/toSql.js +55 -65
  92. package/lib/render/utils/common.js +20 -37
  93. package/lib/render/utils/delta.js +3 -3
  94. package/lib/render/utils/sql.js +22 -6
  95. package/lib/render/utils/stringEscapes.js +3 -3
  96. package/lib/transform/db/applyTransformations.js +3 -3
  97. package/lib/transform/db/assertUnique.js +13 -12
  98. package/lib/transform/db/associations.js +5 -5
  99. package/lib/transform/db/cdsPersistence.js +10 -8
  100. package/lib/transform/db/constraints.js +14 -14
  101. package/lib/transform/db/expansion.js +20 -22
  102. package/lib/transform/db/flattening.js +24 -42
  103. package/lib/transform/db/groupByOrderBy.js +3 -3
  104. package/lib/transform/db/temporal.js +6 -6
  105. package/lib/transform/db/transformExists.js +23 -23
  106. package/lib/transform/db/views.js +16 -16
  107. package/lib/transform/draft/db.js +10 -10
  108. package/lib/transform/draft/odata.js +2 -2
  109. package/lib/transform/forOdataNew.js +12 -40
  110. package/lib/transform/forRelationalDB.js +17 -7
  111. package/lib/transform/localized.js +2 -2
  112. package/lib/transform/odata/toFinalBaseType.js +41 -27
  113. package/lib/transform/odata/typesExposure.js +106 -62
  114. package/lib/transform/parseExpr.js +209 -106
  115. package/lib/transform/transformUtilsNew.js +2 -2
  116. package/lib/transform/translateAssocsToJoins.js +24 -19
  117. package/lib/transform/universalCsn/coreComputed.js +10 -10
  118. package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
  119. package/lib/transform/universalCsn/utils.js +3 -3
  120. package/lib/utils/file.js +5 -5
  121. package/lib/utils/moduleResolve.js +13 -13
  122. package/lib/utils/objectUtils.js +6 -6
  123. package/lib/utils/term.js +5 -2
  124. package/lib/utils/timetrace.js +51 -24
  125. package/package.json +5 -7
  126. package/share/messages/check-proper-type-of.md +1 -1
  127. package/share/messages/message-explanations.json +1 -1
  128. package/share/messages/redirected-to-complex.md +4 -4
  129. package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
@@ -30,9 +30,9 @@ const normalizedKind = {
30
30
  let gensrcFlavor = true; // good enough here...
31
31
  let universalCsn = false;
32
32
  let strictMode = false; // whether to dump with unknown properties (in standard)
33
- let parensAsStrings = false;
34
33
  let projectionAsQuery = false;
35
34
  let withLocations = false;
35
+ let structXpr = false;
36
36
  let dictionaryPrototype = null;
37
37
 
38
38
  // Properties for dictionaries, set in compileX() and TODO: parseX(), must be
@@ -93,7 +93,7 @@ const transformers = {
93
93
  where: condition, // also pathItem after 'cardinality' before 'args'
94
94
  having: condition,
95
95
  args, // also pathItem after 'where', before 'on'/'orderBy'
96
- suffix: node => [].concat( ...node.suffix.map( xprArg ) ),
96
+ suffix: ignore, // handled in exprInternal()
97
97
  orderBy: arrayOf( orderBy ), // TODO XSN: make `sort` and `nulls` sibling properties
98
98
  sort: value,
99
99
  nulls: value,
@@ -191,41 +191,6 @@ const typeProperties = [
191
191
  'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
192
192
  ];
193
193
 
194
- const operators = {
195
- // standard is: binary infix (and corresponding n-ary), unary prefix
196
- isNot: [ 'is', 'not' ], // TODO XSN: 'is not'
197
- isNull: postfix( [ 'is', 'null' ] ),
198
- isNotNull: postfix( [ 'is', 'not', 'null' ] ),
199
- in: binaryRightParen( [ 'in' ] ),
200
- notIn: binaryRightParen( [ 'not', 'in' ] ),
201
- between: ternary( [ 'between' ], [ 'and' ] ),
202
- notBetween: ternary( [ 'not', 'between' ], [ 'and' ] ),
203
- like: ternary( [ 'like' ], [ 'escape' ] ),
204
- notLike: ternary( [ 'not', 'like' ], [ 'escape' ] ),
205
- when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
206
- case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
207
- over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
208
- orderBy: exprs => [ // ORDER BY in generic functions
209
- ...exprs[0], ...operators.overOrderBy(exprs.slice(1)),
210
- ],
211
- overOrderBy: exprs => [ // ORDER BY in OVER() clause
212
- 'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
213
- ],
214
- partitionBy: exprs => [
215
- 'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
216
- ],
217
- rows: exprs => [
218
- 'rows', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
219
- ],
220
- preceding: postfix( [ 'preceding' ] ),
221
- unboundedPreceding: [ 'unbounded', 'preceding' ],
222
- currentRow: [ 'current', 'row' ],
223
- unboundedFollowing: [ 'unbounded', 'following' ],
224
- following: postfix( [ 'following' ] ),
225
- frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
226
- ixpr: exprs => [].concat( ...exprs ), // xpr extra, due to extra parentheses
227
- };
228
-
229
194
  const csnDictionaries = [
230
195
  'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
231
196
  ];
@@ -281,7 +246,7 @@ function sortCsn( csn, cloneOptions = false ) {
281
246
  return r;
282
247
  }
283
248
 
284
- function cloneAnnotationValue(val) {
249
+ function cloneAnnotationValue( val ) {
285
250
  if (typeof val !== 'object') // scalar
286
251
  return val;
287
252
  return JSON.parse( JSON.stringify( val ) );
@@ -296,7 +261,7 @@ function cloneAnnotationValue(val) {
296
261
  * @param {string} property
297
262
  * @returns
298
263
  */
299
- function hasNonEnumerable(object, property) {
264
+ function hasNonEnumerable( object, property ) {
300
265
  return {}.hasOwnProperty.call( object, property ) &&
301
266
  !{}.propertyIsEnumerable.call( object, property );
302
267
  }
@@ -346,7 +311,7 @@ function compactModel( model, options = model.options || {} ) {
346
311
  for (const first in srcDict) {
347
312
  const { namespace } = srcDict[first];
348
313
  if (namespace && namespace.path)
349
- csn.namespace = namespace.path.map( i => i.id ).join('.');
314
+ csn.namespace = pathName( namespace.path );
350
315
  break;
351
316
  }
352
317
  set( 'definitions', csn, model );
@@ -740,8 +705,11 @@ function annotationsAndDocComment( node ) {
740
705
  if (sub !== undefined)
741
706
  csn[prop] = sub;
742
707
  }
743
- if (node.doc)
744
- csn.doc = transformers.doc(node.doc);
708
+ if (node.doc) {
709
+ const doc = transformers.doc(node.doc);
710
+ if (doc !== undefined)
711
+ csn.doc = doc;
712
+ }
745
713
  return csn;
746
714
  }
747
715
 
@@ -829,21 +797,18 @@ function dictionary( dict, keys, prop ) {
829
797
  }
830
798
 
831
799
  function foreignKeys( dict, csn, node ) {
832
- if (universalCsn) {
833
- if (!target( node.target, csn, node ) || dict[$inferred] === 'keys')
834
- return;
835
- }
836
- if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
837
- dict = node._origin.foreignKeys;
838
- const keys = [];
839
- for (const n in dict) {
840
- const d = definition( dict[n] );
841
- if (d !== undefined)
842
- keys.push( d );
843
- else
800
+ if (universalCsn && dict[$inferred] === 'keys' || !target( node.target, csn, node ) )
801
+ return;
802
+
803
+ if (gensrcFlavor) {
804
+ if (node._origin?.$inferred === 'REDIRECTED')
805
+ dict = node._origin.foreignKeys;
806
+ if (dict[$inferred])
844
807
  return;
845
808
  }
846
- csn.keys = keys;
809
+ csn.keys = [];
810
+ for (const n in dict)
811
+ csn.keys.push( definition( dict[n] ) );
847
812
  }
848
813
 
849
814
  function returns( art, csn, _node, prop ) {
@@ -1231,7 +1196,7 @@ function renderArtifactPath( node, path, terse, scope ) {
1231
1196
  const name = item._artifact && item._artifact.name;
1232
1197
  // In localization views, the _artifact link of `item` is important
1233
1198
  const id = name && name.absolute ||
1234
- path.slice( 0, scope ).map( i => i.id ).join('.');
1199
+ pathName( path.slice( 0, scope ) );
1235
1200
  path = [ Object.assign( {}, item, { id } ), ...path.slice( scope ) ];
1236
1201
  }
1237
1202
  const ref = path.map( pathItem );
@@ -1255,7 +1220,7 @@ function args( node ) {
1255
1220
  return node.map( expression );
1256
1221
  const dict = Object.create( dictionaryPrototype );
1257
1222
  for (const param in node)
1258
- dict[param] = expression( node[param], true );
1223
+ dict[param] = expression( node[param] );
1259
1224
  return dict;
1260
1225
  }
1261
1226
 
@@ -1298,7 +1263,7 @@ function enumValue( v, csn, node ) {
1298
1263
  return;
1299
1264
  // (with gensrc, the symbol itself would not make it into the CSN)
1300
1265
  if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
1301
- Object.assign( csn, expression( v, true ) );
1266
+ Object.assign( csn, expression( v ) );
1302
1267
  }
1303
1268
 
1304
1269
 
@@ -1313,128 +1278,89 @@ function onCondition( cond, csn, node ) {
1313
1278
  }
1314
1279
 
1315
1280
  function condition( node ) {
1316
- const expr = expression( node );
1317
- // we do not set a hidden $parens on array - we could still do it if requested
1318
- return !expr.cast && !expr.func && expr.xpr || [ expr ];
1281
+ const expr = exprInternal( node, 'no' );
1282
+ return (Array.isArray( expr ))
1283
+ ? flattenenInternalXpr( expr )
1284
+ : !expr.cast && !expr.func && expr.xpr || [ expr ];
1285
+ }
1286
+
1287
+ function expression( node ) {
1288
+ const expr = exprInternal( node, 'no' );
1289
+ return (Array.isArray( expr ))
1290
+ ? { xpr: flattenenInternalXpr( expr ) }
1291
+ : expr;
1319
1292
  }
1320
1293
 
1321
- function expression( node, dollarExtra ) {
1322
- const dollarExtraNode = dollarExtra !== 'ignoreExtra' && node;
1294
+ function exprInternal( node, xprParens ) {
1323
1295
  if (typeof node === 'string')
1324
1296
  return node;
1325
1297
  if (!node) // make to-csn robst
1326
1298
  return {};
1327
1299
  if (node.scope === 'param') {
1328
1300
  if (node.path)
1329
- return extra( { ref: node.path.map( pathItem ), param: true }, dollarExtraNode );
1301
+ return extra( { ref: node.path.map( pathItem ), param: true }, node );
1330
1302
  return { ref: [ node.param.val ], param: true }; // CDL rule for runtimes
1331
1303
  }
1332
1304
  if (node.path) {
1333
1305
  const ref = node.path.map( pathItem );
1334
- if (node.path.$prefix)
1306
+ if (node.path.$prefix) // auto-corrected ORDER BY refs without table alias
1335
1307
  ref.unshift( node.path.$prefix );
1336
1308
  // we would need to consider node.global here if we introduce that
1337
- return extra( { ref }, dollarExtraNode );
1309
+ return extra( { ref }, node );
1338
1310
  }
1339
1311
  if (node.literal) {
1340
1312
  if (typeof node.val === node.literal || node.val === null)
1341
- return extra( { val: node.val }, dollarExtraNode );
1313
+ return extra( { val: node.val }, node );
1342
1314
  else if (node.literal === 'enum')
1343
- return extra( { '#': node.sym.id }, dollarExtraNode );
1315
+ return extra( { '#': node.sym.id }, node );
1344
1316
  else if (node.literal === 'token')
1345
1317
  return node.val; // * in COUNT(*)
1346
- return extra( { val: node.val, literal: node.literal }, dollarExtraNode );
1318
+ return extra( { val: node.val, literal: node.literal }, node );
1347
1319
  }
1348
1320
  if (node.func) { // TODO XSN: remove op: 'call', func is no path
1349
1321
  const call = { func: node.func.path[0].id };
1350
- if (node.args) { // no args from CSN input for CURRENT_DATE etc
1322
+ if (node.args) // no args from CSN input for CURRENT_DATE etc
1351
1323
  call.args = args( node.args );
1352
- const arg0 = call.args[0];
1353
- const { quantifier } = node.func.path[0];
1354
- if (arg0 && quantifier) {
1355
- if (typeof arg0 !== 'object' || !arg0.xpr)
1356
- call.args[0] = { xpr: [ quantifier.val, arg0 ] };
1357
- else
1358
- arg0.xpr.unshift( quantifier.val );
1359
- }
1360
- }
1361
1324
  if (node.suffix)
1362
- call.xpr = [].concat( ...node.suffix.map( xprArg ) );
1363
- return extra( call, dollarExtraNode );
1325
+ call.xpr = condition( { op: { val: 'ixpr' }, args: node.suffix } );
1326
+ // remark: node.suffix.map( expression ) would add $parens: 1 for xpr after "over"
1327
+ return extra( call, node );
1364
1328
  }
1365
1329
  if (node.query)
1366
1330
  return query( node.query, null, null, null, 1 );
1367
1331
  if (!node.op) // parse error
1368
1332
  return { xpr: [] };
1369
- else if (node.op.val === 'xpr')
1370
- // do not use xpr() for xpr, as it would flatten inner xpr's
1371
- return extra({ xpr: node.args.map( expression ) }, dollarExtraNode, 1 );
1372
- else if (node.op.val === 'cast')
1373
- return cast( expression( node.args[0] ), dollarExtraNode );
1374
- // from here on: CDL input (no $extra possible - but $parens)
1375
- else if (node.op.val !== ',')
1376
- return extra( { xpr: xpr( node ) }, dollarExtraNode, (dollarExtra === 'sub-xpr' ? 1 : 0) );
1377
- return (parensAsStrings)
1378
- ? { xpr: [ '(', ...xpr( node ), ')' ] }
1379
- // the inner parens belong to the tuple construct, i.e. won't count as parens
1380
- : extra( { list: node.args.map( expression ) }, dollarExtraNode, 0 );
1381
- }
1382
-
1383
- function xpr( node ) {
1384
- // if (!node.op) console.log(node)
1385
- const op = operators[node.op.val] || node.op.val.split(' ');
1386
- const exprs = node.args.map( xprArg );
1387
- if (op instanceof Function)
1388
- return op( exprs );
1389
- if (node.quantifier)
1390
- op.push( node.quantifier.val );
1391
- if (exprs.length < 2)
1392
- return [ ...op, ...exprs[0] || [] ];
1393
- return exprs[0].concat( ...exprs.slice(1).map( a => [ ...op, ...a ] ) );
1394
- }
1395
-
1396
- function xprArg( sub ) {
1397
- const realXpr = sub.op && sub.op.val === 'xpr';
1398
- const expr = expression( sub, 'sub-xpr' );
1399
- // `sort`/`nulls` will be attached to arguments of orderBy
1400
- // which might be either `path`s or `xpr`s
1401
- const sortAndNulls = [];
1402
- if (sub.sort)
1403
- sortAndNulls.push( sub.sort.val );
1404
- if (sub.nulls)
1405
- sortAndNulls.push( ...[ 'nulls', sub.nulls.val ] );
1406
- // return !sub.$parens && !expr.cast && !expr.func && expr.xpr || [ expr ];
1407
- // if parensAsStrings is gone
1408
- if (realXpr || expr.cast || expr.func || !expr.xpr || sub.$parens && !parensAsStrings)
1409
- return [ expr, ...sortAndNulls ];
1410
- else if (sub.$parens && sub.op.val !== ',')
1411
- return [ '(', ...expr.xpr, ')' ];
1412
-
1413
- expr.xpr.push( ...sortAndNulls );
1414
- return expr.xpr;
1415
- }
1416
-
1417
- function ternary( op1, op2 ) {
1418
- return function ternaryOp( exprs ) {
1419
- return (exprs[2])
1420
- ? [ ...exprs[0], ...op1, ...exprs[1], ...op2, ...exprs[2] ]
1421
- : [ ...exprs[0], ...op1, ...exprs[1] ];
1422
- };
1423
- }
1424
1333
 
1425
- function postfix( op ) {
1426
- return function postfixOp( exprs ) {
1427
- return [ ...exprs[0], ...op ];
1428
- };
1334
+ const { val } = node.op;
1335
+ switch (val) {
1336
+ case 'ixpr':
1337
+ case 'xpr':
1338
+ break;
1339
+ case 'cast':
1340
+ return cast( expression( node.args[0] ), node );
1341
+ case 'list':
1342
+ return extra( { list: node.args.map( expression ) }, node, 0 );
1343
+ default: { // '=', 'and', CSN v0 input: binary (n-ary) and unary prefix
1344
+ if (!node.args.length)
1345
+ return { xpr: [] };
1346
+ const nary = [];
1347
+ for (const item of node.args)
1348
+ nary.push( { val, literal: 'token' }, item );
1349
+ node = {
1350
+ op: { val: 'ixpr' },
1351
+ args: (nary.length > 2 ? nary.slice(1) : nary),
1352
+ $parens: node.$parens,
1353
+ };
1354
+ }
1355
+ }
1356
+ const rargs = node.args.map( exprInternal );
1357
+ if (val === 'xpr' || node.$parens)
1358
+ return extra( { xpr: flattenenInternalXpr( rargs ) }, node, (xprParens === 'no' ? 0 : 1) );
1359
+ return rargs.length === 1 ? rargs[0] : rargs;
1429
1360
  }
1430
1361
 
1431
- function binaryRightParen( op ) {
1432
- return ( exprs ) => {
1433
- const right = exprs[1].length === 1 ? exprs[1][0] : {};
1434
- return (right.xpr || right.list || !right.$parens)
1435
- ? [ ...exprs[0], ...op, ...exprs[1] ]
1436
- : [ ...exprs[0], ...op, { xpr: exprs[1] } ];
1437
- };
1362
+ function flattenenInternalXpr( array ) {
1363
+ return (structXpr) ? array : array.flat( Infinity );
1438
1364
  }
1439
1365
 
1440
1366
  function query( node, csn, xsn, _prop, expectedParens = 0 ) {
@@ -1538,7 +1464,7 @@ function addElementAsColumn( elem, cols ) {
1538
1464
  gensrcFlavor = gensrcFlavor || 'column';
1539
1465
  set( 'virtual', col, elem );
1540
1466
  set( 'key', col, elem );
1541
- const expr = expression( elem.value, true );
1467
+ const expr = expression( elem.value );
1542
1468
  Object.assign( col, (expr.cast ? { xpr: [ expr ] } : expr) );
1543
1469
  gensrcFlavor = gensrcSaved; // for not having annotations in inline etc
1544
1470
  if (elem.expand)
@@ -1574,7 +1500,7 @@ function orderBy( node ) {
1574
1500
  expr.sort = node.sort.val;
1575
1501
  if (node.nulls)
1576
1502
  expr.nulls = node.nulls.val;
1577
- return extra( expr, node ); // extra properties after sort/nulls
1503
+ return expr; // extra properties are before sort/nulls - who cares?
1578
1504
  }
1579
1505
 
1580
1506
  function extra( csn, node, expectedParens = 0 ) {
@@ -1639,19 +1565,18 @@ function compactQuery( q ) { // TODO: options
1639
1565
 
1640
1566
  function compactExpr( e ) { // TODO: options
1641
1567
  initModuleVars();
1642
- return e && expression( e, true );
1568
+ return e && expression( e );
1643
1569
  }
1644
1570
 
1645
1571
  /**
1646
1572
  * @param {CSN.Options} options
1647
1573
  */
1648
1574
  function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
1649
- gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
1650
- ( options.toCsn && options.toCsn.flavor === 'gensrc');
1651
- universalCsn = ( options.csnFlavor === 'universal' ||
1652
- ( options.toCsn && options.toCsn.flavor === 'universal') ) &&
1653
- isBetaEnabled( options, 'enableUniversalCsn' ) &&
1654
- !options.parseCdl;
1575
+ const flavor = options.csnFlavor || options.toCsn?.flavor;
1576
+ gensrcFlavor = options.parseCdl || flavor === 'gensrc';
1577
+ universalCsn = flavor === 'universal' &&
1578
+ isBetaEnabled( options, 'enableUniversalCsn' ) &&
1579
+ !options.parseCdl;
1655
1580
  strictMode = options.testMode;
1656
1581
  const proto = options.dictionaryPrototype;
1657
1582
  // eslint-disable-next-line no-nested-ternary
@@ -1659,7 +1584,7 @@ function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
1659
1584
  ? proto
1660
1585
  : (proto) ? Object.prototype : null;
1661
1586
  withLocations = options.withLocations;
1662
- parensAsStrings = isDeprecatedEnabled( options, '_parensAsStrings' );
1587
+ structXpr = options.structXpr;
1663
1588
  projectionAsQuery = isDeprecatedEnabled( options, '_projectionAsQuery' );
1664
1589
  }
1665
1590
 
@@ -1,7 +1,4 @@
1
1
  {
2
2
  "root": true,
3
- "extends": "../../.eslintrc-ydkjsi.json",
4
- "rules": {
5
- "cds-compiler/space-in-func-decl": "error"
6
- }
3
+ "extends": "../../.eslintrc-ydkjsi.json"
7
4
  }
@@ -86,15 +86,15 @@ Object.assign(GenericAntlrParser.prototype, {
86
86
  previousTokenAtLocation,
87
87
  combinedLocation,
88
88
  surroundByParens,
89
+ secureParens,
89
90
  unaryOpForParens,
90
91
  leftAssocBinaryOp,
91
92
  classifyImplicitName,
92
93
  warnIfColonFollows,
93
94
  fragileAlias,
94
95
  identAst,
95
- functionAst,
96
- setLastAsXpr,
97
- xprToken,
96
+ pushXprToken,
97
+ argsExpression,
98
98
  valuePathAst,
99
99
  signedExpression,
100
100
  numberLiteral,
@@ -249,9 +249,15 @@ const genericTokenTypes = {
249
249
  intro: 'GenericIntro',
250
250
  };
251
251
 
252
+ /**
253
+ * @memberOf GenericAntlrParser
254
+ *
255
+ * @param pathItem
256
+ * @param [expected]
257
+ */
252
258
  function prepareGenericKeywords( pathItem, expected = null ) {
253
259
  const length = pathItem?.args?.length || 0;
254
- const argPos = (expected ? length - 1 : length);
260
+ const argPos = length;
255
261
  const func = pathItem?.id && specialFunctions[pathItem.id.toUpperCase()];
256
262
  const spec = func && func[argPos] || specialFunctions[''][argPos ? 1 : 0];
257
263
  this.$genericKeywords = spec;
@@ -477,7 +483,7 @@ function tokenLocation( token, endToken = null ) {
477
483
  function isMultiLineToken( token ) {
478
484
  return (
479
485
  token.type === this.constructor.DocComment ||
480
- token.type === this.constructor.String ||
486
+ token.type === this.constructor.String || // TODO: do not check every string content
481
487
  token.type === this.constructor.UnterminatedLiteral
482
488
  );
483
489
  }
@@ -547,6 +553,23 @@ function combinedLocation( start, end ) {
547
553
  return locUtils.combinedLocation( start, end );
548
554
  }
549
555
 
556
+ // make sure that the parens of `IN (…)` do not disappear:
557
+ function secureParens( expr ) {
558
+ const op = expr?.op?.val;
559
+ const $parens = expr?.$parens;
560
+ if (!$parens || expr.query || op && op !== 'call' && op !== 'cast')
561
+ return expr;
562
+ // ensure that references, literals and functions keep their surrounding parentheses
563
+ // (is for expressions the case anyway)
564
+ delete expr.$parens;
565
+ return {
566
+ op: { val: 'xpr', location: this.startLocation() },
567
+ args: [ expr ],
568
+ location: { ...expr.location },
569
+ $parens,
570
+ };
571
+ }
572
+
550
573
  function surroundByParens( expr, open, close, asQuery = false ) {
551
574
  if (!expr)
552
575
  return expr;
@@ -657,33 +680,27 @@ function identAst( token, category, noTokenTypeCheck = false ) {
657
680
  return { id, $delimited: true, location: this.tokenLocation( token ) };
658
681
  }
659
682
 
660
- function functionAst( token, xpr ) { // only for special function TRIM and EXTRACT
661
- // TODO: XSN func cleanup
662
- const location = this.tokenLocation( token );
663
- const args = xpr
664
- ? [ { op: { location, val: 'ixpr' }, args: [], location: this.tokenLocation( xpr ) } ]
665
- : [];
666
- return {
667
- op: { location, val: 'call' },
668
- func: { path: [ { id: token.text, location, args } ], location },
669
- args,
670
- location,
671
- };
672
- }
673
-
674
- function setLastAsXpr( args ) {
675
- const pos = args.length - 1;
676
- const last = args[pos];
677
- if (!last || last?.op?.val === 'ixpr') // actuall an internal xpr, which is always flattened
678
- return last;
679
- const location = { ...last.location };
680
- args[pos] = { op: { location, val: 'ixpr' }, args: [ last ], location };
681
- return args[pos].args;
683
+ // only to be used in @after
684
+ // TODO: remove compatible stuff (A2J/checks use op: 'and'/'=')
685
+ function argsExpression( args, useFirstLocation, compatible = null ) {
686
+ // console.log('AE:',args);
687
+ if (args.length === 1)
688
+ return args[0];
689
+ const location = useFirstLocation && args[0] && { ...args[0].location };
690
+ const op = compatible && args.length === 3 && args[1].val === compatible && args[1];
691
+ const expr = (op)
692
+ ? { op: { val: op.val, location: op.location }, args: [ args[0], args[2] ], location }
693
+ : { op: { val: 'ixpr', location: this.startLocation() }, args, location };
694
+ return this.attachLocation( expr );
682
695
  }
683
696
 
684
- function xprToken() {
697
+ function pushXprToken( args ) {
685
698
  const token = this._input.LT(-1);
686
- return { location: this.tokenLocation( token ), val: token.text, literal: 'token' };
699
+ args.push( {
700
+ location: this.tokenLocation( token ),
701
+ val: token.text.toLowerCase(), // TODO: remove toLowerCase() ?
702
+ literal: 'token',
703
+ } );
687
704
  }
688
705
 
689
706
  function valuePathAst( ref ) {
@@ -720,10 +737,11 @@ function valuePathAst( ref ) {
720
737
 
721
738
  // If a '-' is directly before an unsigned number, consider it part of the number;
722
739
  // otherwise (including for '+'), represent it as extra unary prefix operator.
723
- function signedExpression( signToken, expr ) {
724
- const sign = this.valueWithTokenLocation( signToken.text, signToken );
740
+ function signedExpression( args, expr ) {
741
+ // if (args.length !== 1) throw Error()
742
+ const sign = args[0];
725
743
  const nval
726
- = (signToken.text === '-' &&
744
+ = (sign.val === '-' &&
727
745
  expr && // expr may be null if `-` rule can't be parsed
728
746
  expr.literal === 'number' &&
729
747
  sign.location.endLine === expr.location.line &&
@@ -731,11 +749,14 @@ function signedExpression( signToken, expr ) {
731
749
  (typeof expr.val === 'number'
732
750
  ? expr.val >= 0 && -expr.val
733
751
  : !expr.val.startsWith('-') && `-${ expr.val }`)) || false;
734
- if (nval === false)
735
- return { op: sign, args: expr ? [ expr ] : [] };
736
- expr.val = nval;
737
- --expr.location.col;
738
- return expr;
752
+ if (nval === false) {
753
+ args.push( expr );
754
+ }
755
+ else {
756
+ expr.val = nval;
757
+ --expr.location.col;
758
+ args[0] = expr;
759
+ }
739
760
  }
740
761
 
741
762
  // Return AST for number token `token` with optional token `sign`. Represent
@@ -756,7 +777,7 @@ function numberLiteral( token, sign, text = token.text ) {
756
777
  const num = Number.parseFloat( text || '0' ); // not Number.parseInt() !
757
778
  if (!Number.isSafeInteger(num)) {
758
779
  if (sign == null) {
759
- this.error( 'syntax-expecting-integer', token,
780
+ this.error( 'syntax-expecting-unsigned-int', token,
760
781
  { '#': !text.match(/^[0-9]*$/) ? 'normal' : 'unsafe' } );
761
782
  }
762
783