@sap/cds-compiler 2.7.0 → 2.10.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 (63) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/lib/api/main.js +8 -10
  3. package/lib/api/options.js +13 -9
  4. package/lib/api/validate.js +11 -8
  5. package/lib/base/keywords.js +32 -2
  6. package/lib/base/message-registry.js +16 -0
  7. package/lib/base/messages.js +2 -0
  8. package/lib/base/model.js +1 -0
  9. package/lib/checks/onConditions.js +5 -0
  10. package/lib/checks/types.js +26 -2
  11. package/lib/checks/unknownMagic.js +38 -0
  12. package/lib/checks/validator.js +7 -2
  13. package/lib/compiler/assert-consistency.js +11 -5
  14. package/lib/compiler/builtins.js +2 -0
  15. package/lib/compiler/checks.js +3 -1
  16. package/lib/compiler/definer.js +87 -29
  17. package/lib/compiler/resolver.js +75 -16
  18. package/lib/compiler/shared.js +29 -9
  19. package/lib/edm/annotations/genericTranslation.js +182 -186
  20. package/lib/edm/csn2edm.js +93 -98
  21. package/lib/edm/edm.js +16 -20
  22. package/lib/edm/edmPreprocessor.js +274 -83
  23. package/lib/edm/edmUtils.js +29 -10
  24. package/lib/gen/language.checksum +1 -1
  25. package/lib/gen/language.interp +12 -1
  26. package/lib/gen/language.tokens +57 -53
  27. package/lib/gen/languageLexer.interp +10 -1
  28. package/lib/gen/languageLexer.js +770 -744
  29. package/lib/gen/languageLexer.tokens +49 -46
  30. package/lib/gen/languageParser.js +4727 -4323
  31. package/lib/json/from-csn.js +52 -23
  32. package/lib/json/to-csn.js +185 -71
  33. package/lib/language/errorStrategy.js +1 -0
  34. package/lib/language/genericAntlrParser.js +9 -0
  35. package/lib/language/language.g4 +90 -31
  36. package/lib/main.js +4 -0
  37. package/lib/model/api.js +78 -0
  38. package/lib/model/csnRefs.js +7 -1
  39. package/lib/model/csnUtils.js +5 -4
  40. package/lib/optionProcessor.js +7 -1
  41. package/lib/render/.eslintrc.json +3 -1
  42. package/lib/render/toCdl.js +45 -9
  43. package/lib/render/toHdbcds.js +100 -34
  44. package/lib/render/toSql.js +12 -4
  45. package/lib/render/utils/common.js +5 -9
  46. package/lib/sql-identifier.js +6 -1
  47. package/lib/transform/db/draft.js +6 -4
  48. package/lib/transform/db/expansion.js +14 -4
  49. package/lib/transform/db/flattening.js +13 -5
  50. package/lib/transform/db/transformExists.js +252 -58
  51. package/lib/transform/forHanaNew.js +7 -1
  52. package/lib/transform/forOdataNew.js +12 -8
  53. package/lib/transform/odata/attachPath.js +19 -4
  54. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  55. package/lib/transform/odata/referenceFlattener.js +44 -38
  56. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  57. package/lib/transform/odata/structuralPath.js +76 -0
  58. package/lib/transform/odata/structureFlattener.js +13 -10
  59. package/lib/transform/odata/typesExposure.js +22 -12
  60. package/lib/transform/transformUtilsNew.js +33 -1
  61. package/lib/transform/translateAssocsToJoins.js +6 -4
  62. package/lib/transform/universalCsnEnricher.js +67 -0
  63. package/package.json +1 -1
@@ -20,7 +20,7 @@
20
20
  // allow integers at certain places), one token type for non-quoted and
21
21
  // quoted identifiers.
22
22
  //
23
- // * Keep the number of keywords as small as possibile. Thus, built-ins is a
23
+ // * Keep the number of keywords as small as possible. Thus, built-ins is a
24
24
  // topic for the semantic analysis, not the grammar. Examples: no keywords
25
25
  // for built-in types or built-in SQL functions. This also avoids noise in
26
26
  // the grammar and a huge/slow generated parser.
@@ -43,7 +43,7 @@
43
43
  // is not enough for a decision), write a comment starting with `#ATN:`
44
44
  // which describes the ambiguity. Additionally, put a comment `/* #ATN n
45
45
  // */` INSIDE an (`@after`) action of a rule if the corresponding function
46
- // in '../gen/langageParser.js' contains `n` occurrences of
46
+ // in '../gen/languageParser.js' contains `n` occurrences of
47
47
  // `adaptivePredict` calls. This is checked in 'test/testCompiler.js',
48
48
  // which also counts the total number of `adaptivePredict` occurrences.
49
49
  // └─────────────────────────────────────────────────────────────────────────┘
@@ -55,6 +55,10 @@
55
55
  // good idea anyway to avoid calls to `adaptivePredict`, see the rules
56
56
  // starting with `annotationAssignment_`.
57
57
  //
58
+ // * Factoring out a sub rule into a named rule influences the error recovery:
59
+ // the parser tries to consume all tokens which are neither in the follow
60
+ // set of loops and named rules. So be careful.
61
+ //
58
62
  // * Do not use actions in the lexer. Examples: de-quote string literals not
59
63
  // in the lexer, but in the parser; do not throw errors, but produce error
60
64
  // tokens if necessary.
@@ -106,6 +110,9 @@
106
110
  //
107
111
  // * The ANTLR error "missing attribute access on rule reference c in $c" can
108
112
  // be solved with using $ctx.c instead of $c
113
+ //
114
+ // * If you want to set a property starting with '$' like $syntax, use
115
+ // obj['$'+'syntax'] as the ANTLR tool would replace $syntax by $ctx.syntax
109
116
 
110
117
  grammar language;
111
118
  options {
@@ -114,6 +121,7 @@ options {
114
121
  }
115
122
  tokens {
116
123
  VIRTUAL, // used with setLocalToken()
124
+ OVER, // used with setLocalTokenIfBefore()
117
125
  HelperToken1, // used with setLocalToken(), does not appear in messages
118
126
  HelperToken2, // used with setLocalToken(), does not appear in messages
119
127
  HideAlternatives, // hide alternative tokens (no token seq!)
@@ -818,7 +826,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
818
826
  )*
819
827
  ')'
820
828
  (
821
- RETURNS '{'
829
+ RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
822
830
  annotateElement[ $art ]*
823
831
  '}'
824
832
  optionalSemi
@@ -826,7 +834,7 @@ annotateArtifact[ outer, loc, annos ] locals[ art, name = {} ]
826
834
  requiredSemi
827
835
  )
828
836
  |
829
- RETURNS '{'
837
+ RETURNS '{' { $art['$'+'syntax'] = 'returns'; }
830
838
  annotateElement[ $art ]*
831
839
  '}'
832
840
  optionalSemi
@@ -1124,13 +1132,7 @@ bracedSelectItemListDef[ query ]
1124
1132
  '{'
1125
1133
  { if (!$query.columns) $query.columns = []; } // set it early to avoid "wildcard" errors
1126
1134
  (
1127
- ( star='*' // TODO: allow everywhere
1128
- {
1129
- $query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
1130
- }
1131
- |
1132
- selectItemDef[ $query.columns ]
1133
- )
1135
+ selectItemDef[ $query.columns ]
1134
1136
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1135
1137
  selectItemDef[ $query.columns ]
1136
1138
  )*
@@ -1139,8 +1141,11 @@ bracedSelectItemListDef[ query ]
1139
1141
  ;
1140
1142
 
1141
1143
  selectItemDef[ outer ] locals[ annos = [] ]
1142
- @after{ this.attachLocation($art.art); }
1144
+ @after{ if ($ctx.art) this.attachLocation($art.art); }
1143
1145
  :
1146
+ star='*'
1147
+ { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1148
+ |
1144
1149
  { this.docComment( $annos ); }
1145
1150
  annotationAssignment_atn[ $annos ]*
1146
1151
  // VIRTUAL is keyword, except if before the following tokens texts:
@@ -1159,6 +1164,8 @@ selectItemDefBody[ outer, annos ] returns[ art = {} ]
1159
1164
  :
1160
1165
  (
1161
1166
  e=expression
1167
+ // we cannot use 'condition' instead, as long as we allow aliases without
1168
+ // AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
1162
1169
  {
1163
1170
  $art = this.addItem( $outer, null, null, $annos, { value: $e.expr } );
1164
1171
  }
@@ -1218,11 +1225,7 @@ selectItemInlineList[ art, clause ]
1218
1225
  '{'
1219
1226
  { $art[$clause] = []; }
1220
1227
  (
1221
- ( star='*' // TODO: allow everywhere
1222
- { $art[$clause].push( this.tokenLocation( $star, undefined, '*' ) ); }
1223
- |
1224
- selectItemInlineDef[ $art[$clause] ]
1225
- )
1228
+ selectItemInlineDef[ $art[$clause] ]
1226
1229
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1227
1230
  selectItemInlineDef[ $art[$clause] ]
1228
1231
  )*
@@ -1231,8 +1234,11 @@ selectItemInlineList[ art, clause ]
1231
1234
  ;
1232
1235
 
1233
1236
  selectItemInlineDef[ outer ] locals[ annos = [] ]
1234
- @after{ this.attachLocation($art.art); }
1237
+ @after{ if ($ctx.art) this.attachLocation($art.art); }
1235
1238
  :
1239
+ star='*'
1240
+ { $outer.push( this.tokenLocation( $star, undefined, '*' ) ); }
1241
+ |
1236
1242
  { this.docComment( $annos ); }
1237
1243
  annotationAssignment_atn[ $annos ]*
1238
1244
  art=selectItemDefBody[ $outer, $annos ]
@@ -1471,8 +1477,20 @@ typeSpecSemi[ art, annos ] // with 'includes', for type and annotation defs
1471
1477
  head=Number
1472
1478
  { $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
1473
1479
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1474
- tail=Number
1475
- { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1480
+ (
1481
+ v=VARIABLE
1482
+ { $art['$'+'typeArgs'].push(
1483
+ { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1484
+ }
1485
+ |
1486
+ f=FLOATING
1487
+ { $art['$'+'typeArgs'].push(
1488
+ { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1489
+ }
1490
+ |
1491
+ tail=Number
1492
+ { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1493
+ )
1476
1494
  )*
1477
1495
  ')'
1478
1496
  { this.docComment( $annos ); }
@@ -1696,8 +1714,20 @@ typeRefOptArgs[ art ]
1696
1714
  head=Number
1697
1715
  { $art['$'+'typeArgs'] = [ this.numberLiteral( $head ) ]; }
1698
1716
  ( ',' { if (this.isStraightBefore(')')) break; } // allow ',' before ')'
1699
- tail=Number
1700
- { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1717
+ (
1718
+ v=VARIABLE
1719
+ { $art['$'+'typeArgs'].push(
1720
+ { literal: 'string', val: 'variable', location: this.tokenLocation($v) } );
1721
+ }
1722
+ |
1723
+ f=FLOATING
1724
+ { $art['$'+'typeArgs'].push(
1725
+ { literal: 'string', val: 'floating', location: this.tokenLocation($f) } );
1726
+ }
1727
+ |
1728
+ tail=Number
1729
+ { $art['$'+'typeArgs'].push( this.numberLiteral( $tail ) ); }
1730
+ )
1701
1731
  )*
1702
1732
  ')'
1703
1733
  |
@@ -1732,6 +1762,30 @@ orderByClause[ inQuery ] returns [ query ]
1732
1762
  ( ',' obn=orderBySpec { $query.orderBy.push( $obn.ob ); } )*
1733
1763
  ;
1734
1764
 
1765
+ overOrderByClause returns [ expr ]
1766
+ :
1767
+ o=ORDER b=BY { $expr = { op: this.tokenLocation($o, $b, 'orderBy' ) , args: [] }}
1768
+ ob1=orderBySpec { $expr.args.push( $ob1.ob ); }
1769
+ ( ',' obn=orderBySpec { $expr.args.push( $obn.ob ); } )*
1770
+ ;
1771
+
1772
+ partitionByClause returns [ expr ]
1773
+ :
1774
+ p=PARTITION b=BY { $expr = { op: this.tokenLocation($p, $b, 'partitionBy' ) , args: [] }}
1775
+ e1=expression { $expr.args.push( $e1.expr ); }
1776
+ ( ',' en=expression { $expr.args.push( $en.expr ); } )*
1777
+ ;
1778
+
1779
+ overClause returns [ over ]
1780
+ @after { this.attachLocation($over); }
1781
+ :
1782
+ o=OVER { $over = { op: this.tokenLocation( $o, null, 'over' ) , args: [] } }
1783
+ '('
1784
+ ( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
1785
+ ( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
1786
+ ')'
1787
+ ;
1788
+
1735
1789
  limitClause[ inQuery ] returns [ query ]
1736
1790
  :
1737
1791
  limkw=LIMIT { $query = this.unaryOpForParens( $inQuery, '$'+'query' ); }
@@ -1790,13 +1844,7 @@ queryPrimary returns[ query = {} ]
1790
1844
  { $query.quantifier = this.tokenLocation( $ad, undefined, $ad.text.toLowerCase() ); }
1791
1845
  )?
1792
1846
  { $query.columns = []; } // set it early to avoid "wildcard" errors
1793
- ( star='*'
1794
- {
1795
- $query.columns = [ this.tokenLocation( $star, undefined, '*' ) ];
1796
- }
1797
- |
1798
- selectItemDef[ $query.columns ]
1799
- )
1847
+ selectItemDef[ $query.columns ]
1800
1848
  ( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
1801
1849
  selectItemDef[ $query.columns ]
1802
1850
  )*
@@ -1954,7 +2002,7 @@ fromPath[ qp, idkind ]
1954
2002
  condition returns [ cond ] locals [ args = [], orl = [] ]
1955
2003
  @after{
1956
2004
  $cond = ($args.length == 1)
1957
- ? $args[0]
2005
+ ? this.attachLocation( $args[0] )
1958
2006
  : this.attachLocation({ op: $orl[0], args: $args });
1959
2007
  }
1960
2008
  :
@@ -2115,6 +2163,10 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
2115
2163
  this.notSupportedYet( $ne ); }
2116
2164
  |
2117
2165
  vp=valuePath[ 'ref', null ] { $expr = this.valuePathAst( $vp.qp ); }
2166
+ { this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
2167
+ (
2168
+ over=overClause { $expr.suffix = [ $over.over ] }
2169
+ )?
2118
2170
  |
2119
2171
  ':'
2120
2172
  ( vp=valuePath[ 'paramref', this.startLocation() ]
@@ -2315,7 +2367,7 @@ optionalCardinality[ pathStep ]
2315
2367
  // completion just produces `:`after having inserted a Number - TODO.
2316
2368
  { if (this._input.LT(2).text !== ':') return $ctx; }
2317
2369
  ( trgMax=Number ':'
2318
- { if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ) } }
2370
+ { if ($pathStep) $pathStep.cardinality = { targetMax: this.numberLiteral( $trgMax ), location: this.startLocation() }; }
2319
2371
  )
2320
2372
  ;
2321
2373
 
@@ -2566,6 +2618,7 @@ ident[ category ] returns[ id ]
2566
2618
  | EXCLUDING
2567
2619
  | EXTEND
2568
2620
  | FIRST
2621
+ | FLOATING
2569
2622
  | FULL
2570
2623
  | FUNCTION
2571
2624
  | GROUP
@@ -2596,6 +2649,7 @@ ident[ category ] returns[ id ]
2596
2649
  | ORDER
2597
2650
  | OUTER
2598
2651
  | PARAMETERS
2652
+ | PARTITION
2599
2653
  | PROJECTION
2600
2654
  | REDIRECTED
2601
2655
  | RETURNS
@@ -2608,6 +2662,7 @@ ident[ category ] returns[ id ]
2608
2662
  | TO
2609
2663
  | TYPE
2610
2664
  | USING
2665
+ | VARIABLE
2611
2666
  | VIEW
2612
2667
  | YEAR
2613
2668
  ;
@@ -2724,6 +2779,7 @@ EXCEPT : [eE][xX][cC][eE][pP][tT] ;
2724
2779
  EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
2725
2780
  EXTEND : [eE][xX][tT][eE][nN][dD] ;
2726
2781
  FIRST : [fF][iI][rR][sS][tT] ;
2782
+ FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
2727
2783
  FULL : [fF][uU][lL][lL] ;
2728
2784
  FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
2729
2785
  GROUP : [gG][rR][oO][uU][pP] ;
@@ -2753,7 +2809,9 @@ ONE : [oO][nN][eE] ;
2753
2809
  OR : [oO][rR] ;
2754
2810
  ORDER : [oO][rR][dD][eE][rR] ;
2755
2811
  OUTER : [oO][uU][tT][eE][rR] ;
2812
+ // OVER : [oO][vV][eE][rR] ;
2756
2813
  PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
2814
+ PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
2757
2815
  PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
2758
2816
  REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
2759
2817
  RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
@@ -2766,6 +2824,7 @@ TO : [tT][oO] ; // or make reserved? (is in SQL-92)
2766
2824
  TYPE : [tT][yY][pP][eE] ;
2767
2825
  UNION : [uU][nN][iI][oO][nN] ;
2768
2826
  USING : [uU][sS][iI][nN][gG] ;
2827
+ VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
2769
2828
  VIEW : [vV][iI][eE][wW] ;
2770
2829
  // VIRTUAL: [vV][iI][rR][tT][uU][aA][lL] ; see tokens {}
2771
2830
  YEAR : [yY][eE][aA][rR] ;
package/lib/main.js CHANGED
@@ -16,6 +16,7 @@
16
16
  const backends = require('./backends');
17
17
  const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
18
18
  const { getArtifactDatabaseNameOf, getElementDatabaseNameOf } = require('./model/csnUtils');
19
+ const { traverseCsn } = require('./model/api');
19
20
  const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
20
21
 
21
22
  const parseLanguage = require('./language/antlrParser');
@@ -111,6 +112,9 @@ module.exports = {
111
112
  getArtifactCdsPersistenceName: getArtifactDatabaseNameOf,
112
113
  getElementCdsPersistenceName: getElementDatabaseNameOf,
113
114
 
115
+ // Other API functions:
116
+ traverseCsn,
117
+
114
118
  // INTERNAL functions for the cds-lsp package and friends - before you use
115
119
  // it, you MUST talk with us - there can be potential incompatibilities with
116
120
  // new releases (even having the same major version):
@@ -0,0 +1,78 @@
1
+ // Miscellaneous CSN functions we put into our compiler API
2
+
3
+ // Do not change at will - they are in the compiler API!
4
+
5
+ /**
6
+ * Dictionary of default traversal functions for function `traverseCsn`.
7
+ * It maps CSN property names to functions which are used by default
8
+ * to traverse the CSN node which is the value of the corresponding property.
9
+ * Users specify their own traversal function via argument `userFunctions`.
10
+ *
11
+ * Each function in `userFunctions` and `defaultFunctions` is called with:
12
+ * - `userFunctions`
13
+ * - the current CSN node, i.e. ‹parent node›.‹property name›
14
+ * - the the ‹parent node›
15
+ * - the ‹property name› (might be useful if the same function is used for several props)
16
+ */
17
+ const defaultFunctions = {
18
+ '@': () => {}, // do not traverse annotation assignments
19
+ args: dictionary,
20
+ elements: dictionary,
21
+ enum: dictionary,
22
+ params: dictionary,
23
+ actions: dictionary,
24
+ mixin: dictionary,
25
+ definitions: dictionary,
26
+ '$': () => {}, // do not traverse properties starting with '$'
27
+ }
28
+
29
+ /**
30
+ * Traverse the CSN node `csn`.
31
+ * If `csn` is an array, call it recursively on each array item.
32
+ * If `csn` is an(other) object, call a function on each property:
33
+ * - The property name is a used as key in argument `userFunctions` and the
34
+ * constant `defaultFunctions` above to get the function which is called on
35
+ * the property value, see `defaultFunctions` for details.
36
+ * - If no function is found with the property name, try to find one with the first char.
37
+ * - If still not found, call `traverseCsn` recursively.
38
+ *
39
+ * The functions in `userFunctions` are usually transformer functions, which
40
+ * change the input CSN destructively.
41
+ */
42
+ function traverseCsn( userFunctions, csn ) {
43
+ if (!csn || typeof csn !== 'object')
44
+ return;
45
+ if (Array.isArray( csn )) {
46
+ csn.forEach( node => traverseCsn( userFunctions, node ) );
47
+ }
48
+ else {
49
+ for (const prop of Object.keys( csn )) {
50
+ const func = userFunctions[prop] || defaultFunctions[prop] ||
51
+ userFunctions[prop.charAt(0)] || defaultFunctions[prop.charAt(0)] ||
52
+ traverseCsn;
53
+ func( userFunctions, csn[prop], csn, prop );
54
+ }
55
+ }
56
+ }
57
+ // people might want to have their own traversal function for `elements`, etc:
58
+ traverseCsn.dictionary = dictionary;
59
+
60
+ /**
61
+ * Traverse the CSN dictionary node `csn`.
62
+ * Call `traverseCsn` on each property value in `csn`, passing down `userFunctions`.
63
+ */
64
+ function dictionary( userFunctions, csn ) {
65
+ if (!csn || typeof csn !== 'object')
66
+ return;
67
+ if (Array.isArray( csn )) { // args can be both array and dictionary
68
+ csn.forEach( node => traverseCsn( userFunctions, node ) );
69
+ }
70
+ else {
71
+ for (const name of Object.keys( csn ))
72
+ traverseCsn( userFunctions, csn[name] );
73
+ }
74
+ }
75
+
76
+ module.exports = {
77
+ traverseCsn,
78
+ };
@@ -175,6 +175,7 @@ const referenceSemantics = {
175
175
  type: { lexical: false, dynamic: 'global' },
176
176
  includes: { lexical: false, dynamic: 'global' },
177
177
  target: { lexical: false, dynamic: 'global' },
178
+ targetAspect: { lexical: false, dynamic: 'global' },
178
179
  from: { lexical: false, dynamic: 'global' },
179
180
  keys: { lexical: false, dynamic: 'target' },
180
181
  excluding: { lexical: false, dynamic: 'source' },
@@ -688,6 +689,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
688
689
  let isName = false;
689
690
  let baseRef = null;
690
691
  let baseEnv = null;
692
+ let main = csn.definitions[csnPath[1]];
691
693
 
692
694
  for (let index = 0; index < csnPath.length; index++) {
693
695
  const prop = csnPath[index];
@@ -700,6 +702,10 @@ function analyseCsnPath( csnPath, csn, resolve ) {
700
702
  isName = false;
701
703
  }
702
704
  else if (artifactProperties.includes( String(prop) )) {
705
+ if (refCtx === 'target' || refCtx === 'targetAspect') { // with 'elements'
706
+ main = art = obj; // $self refers to the anonymous aspect
707
+ parent = null;
708
+ }
703
709
  isName = prop;
704
710
  refCtx = prop;
705
711
  }
@@ -758,7 +764,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
758
764
  // console.log( 'CPATH:', csnPath, refCtx, obj, parent.$location );
759
765
  if (!resolve)
760
766
  return { query }; // for constructSemanticLocationFromCsnPath
761
- return resolve( obj, refCtx, csn.definitions[csnPath[1]], query, parent, baseEnv );
767
+ return resolve( obj, refCtx, main, query, parent, baseEnv );
762
768
  }
763
769
 
764
770
  module.exports = {
@@ -36,7 +36,7 @@ const version = require('../../package.json').version;
36
36
  * @param {CSN.Model} model (Compact) CSN model
37
37
  */
38
38
  function getUtils(model) {
39
- const { artifactRef, inspectRef, effectiveType } = csnRefs(model);
39
+ const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
40
40
 
41
41
  return {
42
42
  getCsnDef,
@@ -59,6 +59,7 @@ function getUtils(model) {
59
59
  artifactRef,
60
60
  effectiveType,
61
61
  get$combined,
62
+ getOrigin,
62
63
  };
63
64
 
64
65
  /**
@@ -630,12 +631,12 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
630
631
  // Backends rely on the fact that `forEachElement` also goes through all
631
632
  // `elements` of the return type (if structured).
632
633
  // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
633
- if (construct.returns) {
634
+ if (construct.returns && !iterateOptions.elementsOnly) {
634
635
  forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
635
636
  }
636
637
 
637
638
  path = [...path]; // Copy
638
- const propsWithMembers = ['elements', 'enum', 'foreignKeys', 'actions', 'params'];
639
+ const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
639
640
  propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
640
641
  }
641
642
 
@@ -1097,7 +1098,7 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
1097
1098
  for (let name of Object.getOwnPropertyNames( node )) {
1098
1099
  const trans = transformers[name] || standard;
1099
1100
  if(customTransformers[name])
1100
- customTransformers[name](node, name, node[name], csnPath);
1101
+ customTransformers[name](node, name, node[name], csnPath, parent, prop);
1101
1102
 
1102
1103
  trans( node, name, node[name], csnPath );
1103
1104
  }
@@ -37,8 +37,10 @@ optionProcessor
37
37
  .option(' --test-mode')
38
38
  .option(' --test-sort-csn')
39
39
  .option(' --doc-comment')
40
+ .option(' --add-texts-language-assoc')
40
41
  .option(' --localized-without-coalesce')
41
42
  .option(' --defaultStringLength <length>')
43
+ .option(' --no-recompile')
42
44
  .positionalArgument('<files...>')
43
45
  .help(`
44
46
  Usage: cdsc <command> [options] <files...>
@@ -93,6 +95,7 @@ optionProcessor
93
95
  hanaAssocRealCardinality
94
96
  mapAssocToJoinCardinality
95
97
  ignoreAssocPublishingInUnion
98
+ windowFunctions
96
99
  --constraints-not-enforced If this option is supplied, referential constraints are NOT ENFORCED
97
100
  This option is also applied to result of "cdsc manageConstraints"
98
101
  --constraints-not-validated If this option is supplied, referential constraints are NOT VALIDATED
@@ -123,8 +126,11 @@ optionProcessor
123
126
  OData CSN, CDL order and more. When --test-mode is enabled, this
124
127
  option is implicitly enabled as well.
125
128
  --doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
129
+ --add-texts-language-assoc In generated texts entities, add association "language"
130
+ to "sap.common.Languages" if it exists
126
131
  --localized-without-coalesce Omit coalesce in localized convenience views
127
-
132
+ --no-recompile Don't recompile in case of internal errors
133
+
128
134
  Commands
129
135
  H, toHana [options] <files...> Generate HANA CDS source files
130
136
  O, toOdata [options] <files...> Generate ODATA metadata and annotations
@@ -7,9 +7,11 @@
7
7
  "prefer-template": "error",
8
8
  "no-trailing-spaces": "error",
9
9
  "sonarjs/cognitive-complexity": "off",
10
+ "sonarjs/no-duplicate-string": ["off"],
10
11
  "sonarjs/no-nested-template-literals": "off",
11
12
  "template-curly-spacing":["error", "never"],
12
- "complexity": ["warn", 30],
13
+ // Who cares - just very whiny and in the way
14
+ "complexity": "off",
13
15
  "max-len": "off",
14
16
  // We should enable this
15
17
  "no-shadow": "off"
@@ -8,11 +8,13 @@ const {
8
8
  isBuiltinType, generatedByCompilerVersion, getNormalizedQuery,
9
9
  } = require('../model/csnUtils');
10
10
  const keywords = require('../base/keywords');
11
- const { renderFunc, processExprArray, findElement } = require('./utils/common');
11
+ const { renderFunc, beautifyExprArray, findElement } = require('./utils/common');
12
12
  const { checkCSNVersion } = require('../json/csnVersion');
13
13
  const timetrace = require('../utils/timetrace');
14
14
  const { csnRefs } = require('../model/csnRefs');
15
15
  const { forEachDefinition } = require('../model/csnUtils');
16
+ const enrichUniversalCsn = require('../transform/universalCsnEnricher');
17
+ const { isBetaEnabled } = require('../base/model');
16
18
 
17
19
  /**
18
20
  * Render the CSN model 'model' to CDS source text. One source is created per
@@ -33,6 +35,9 @@ function toCdsSourceCsn(csn, options) {
33
35
  // Skip compactModel if already using CSN
34
36
  // const csn = cloneCsn(model, options);
35
37
 
38
+ if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
39
+ enrichUniversalCsn(csn, options);
40
+
36
41
  checkCSNVersion(csn, options);
37
42
 
38
43
  const result = Object.create(null);
@@ -50,16 +55,20 @@ function toCdsSourceCsn(csn, options) {
50
55
  result[main] += `${sourceStr}\n`;
51
56
  });
52
57
 
53
- // Apply possible subelement annotations with an "annotate X with"
58
+ // Apply possible subelement/action return annotations with an "annotate X with"
59
+ // Some of them appear in csn.extensions, some not...
54
60
  if (subelementAnnotates.length > 0) {
55
- for (const [ artName, element, elementName ] of subelementAnnotates) {
56
- let sourceStr = `annotate ${artName} with {\n`;
57
- sourceStr += ` ${elementName} {\n`;
61
+ for (const [ artName, element, elementName, suffix ] of subelementAnnotates) {
62
+ // Suffix is used with action return annotations
63
+ let sourceStr = `annotate ${artName} with ${suffix ? `${suffix} ` : ''}{\n`;
64
+ if (elementName) // action returns do not have element name - we need less {} there
65
+ sourceStr += ` ${elementName} {\n`;
58
66
  const env = increaseIndent(increaseIndent(createEnv()));
59
67
  const subelements = renderSubelementAnnotates(element, env);
60
68
  if (subelements !== '') {
61
69
  sourceStr += `${subelements}\n`;
62
- sourceStr += ' }\n';
70
+ if (elementName) // action returns do not have element name - we need less {} there
71
+ sourceStr += ' }\n';
63
72
  sourceStr += '}\n';
64
73
  result[main] += `${sourceStr}\n`;
65
74
  }
@@ -145,6 +154,12 @@ function toCdsSourceCsn(csn, options) {
145
154
  if (ext.elements)
146
155
  result += renderElementExtensions(ext.elements, env);
147
156
 
157
+ // Returns annotations
158
+ if (ext.returns) {
159
+ const childEnv = increaseIndent(env);
160
+ result += ` with returns${renderElementExtensions(ext.returns.elements, childEnv)}`;
161
+ }
162
+
148
163
  // Action annotations
149
164
  if (ext.actions) {
150
165
  result += ' actions {\n';
@@ -162,10 +177,19 @@ function toCdsSourceCsn(csn, options) {
162
177
 
163
178
  result += `${paramAnnotations.join(',\n')}\n${childEnv.indent})`;
164
179
  }
180
+ // Annotations on action returns
181
+ if (action.returns && action.returns.elements) {
182
+ const grandChildEnv = increaseIndent(childEnv);
183
+ result += ` returns${renderElementExtensions(action.returns.elements, grandChildEnv)}`;
184
+ }
185
+
186
+
165
187
  result += ';\n';
166
188
  }
167
189
  result += `${env.indent}}`;
168
190
  }
191
+
192
+
169
193
  result += ';';
170
194
  return result;
171
195
  }).join('\n');
@@ -205,6 +229,7 @@ function toCdsSourceCsn(csn, options) {
205
229
  function renderArtifact(artifactName, art, env) {
206
230
  // FIXME: Correctly build the paths during runtime to give better locations
207
231
  env.path = [ 'definitions', artifactName ];
232
+ env.artifactName = artifactName;
208
233
 
209
234
  switch (art.kind) {
210
235
  case 'entity':
@@ -509,6 +534,7 @@ function toCdsSourceCsn(csn, options) {
509
534
  * @param {Boolean} [isSubElement]
510
535
  */
511
536
  function renderElement(elementName, elm, env, isSubElement) {
537
+ env.elementName = elementName;
512
538
  let result = renderDocComment(elm, env) + renderAnnotationAssignments(elm, env);
513
539
  const quotedElementName = quoteOrUppercaseId(elementName);
514
540
  result += `${env.indent + (elm.virtual ? 'virtual ' : '') +
@@ -516,10 +542,11 @@ function toCdsSourceCsn(csn, options) {
516
542
  ((elm.masked && !elm._ignoreMasked) ? 'masked ' : '') +
517
543
  quotedElementName} : ${
518
544
  renderTypeReference(elm, env, undefined)
519
- }${renderNullability(elm)}`;
545
+ }${elm.on ? '' : renderNullability(elm)}`;
520
546
  if (elm.default)
521
547
  result += ` default ${renderExpr(elm.default, env)}`;
522
548
 
549
+ delete env.elementName;
523
550
  return `${result};\n`;
524
551
  }
525
552
 
@@ -1056,8 +1083,11 @@ function toCdsSourceCsn(csn, options) {
1056
1083
  const childEnv = increaseIndent(env);
1057
1084
  const parameters = Object.keys(act.params || []).map(name => renderParameter(name, act.params[name], childEnv)).join(',\n');
1058
1085
  result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
1059
- if (act.returns)
1086
+ if (act.returns) {
1087
+ if (act.returns.type && act.returns.elements) // action returns annotations
1088
+ subelementAnnotates.push([ actionName, act.returns, '', 'returns' ]);
1060
1089
  result += ` returns ${renderTypeReference(act.returns, env)}${renderNullability(act.returns)}`;
1090
+ }
1061
1091
 
1062
1092
  result += ';\n';
1063
1093
  return result;
@@ -1133,6 +1163,10 @@ function toCdsSourceCsn(csn, options) {
1133
1163
  let rc = `many ${renderTypeReference(elm.items, env)}`;
1134
1164
  if (elm.items.notNull != null)
1135
1165
  rc += elm.items.notNull ? ' not null' : ' null';
1166
+ // many sub element annotates
1167
+ if (elm.items.type && elm.items.elements && env.artifactName)
1168
+ subelementAnnotates.push([ env.artifactName, elm.items, env.elementName ]);
1169
+
1136
1170
  return rc;
1137
1171
  }
1138
1172
 
@@ -1316,7 +1350,7 @@ function toCdsSourceCsn(csn, options) {
1316
1350
  function renderExpr(x, env, inline = true, inExpr = false) {
1317
1351
  // Compound expression
1318
1352
  if (Array.isArray(x))
1319
- return processExprArray(x, renderExpr, env, inline, inExpr);
1353
+ return beautifyExprArray(x.map(item => renderExpr(item, env, inline, inExpr)));
1320
1354
 
1321
1355
  if (typeof x === 'object' && x !== null) {
1322
1356
  if (inExpr && x.cast && x.cast.type)
@@ -1861,6 +1895,8 @@ function toCdsSourceCsn(csn, options) {
1861
1895
  *
1862
1896
  * @property {string} indent Current indentation as a string, e.g. ' ' for two spaces.
1863
1897
  * @property {CSN.Path} [path] CSN path to the current artifact
1898
+ * @property {string} artifactName Name of the artifact - set in renderArtifact
1899
+ * @property {string} elementName Name of the element being rendered - set in renderElement
1864
1900
  * @property {{[name: string]: {
1865
1901
  quotedName: string,
1866
1902
  quotedAlias: string