@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.
- package/CHANGELOG.md +103 -0
- package/lib/api/main.js +8 -10
- package/lib/api/options.js +13 -9
- package/lib/api/validate.js +11 -8
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +16 -0
- package/lib/base/messages.js +2 -0
- package/lib/base/model.js +1 -0
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +3 -1
- package/lib/compiler/definer.js +87 -29
- package/lib/compiler/resolver.js +75 -16
- package/lib/compiler/shared.js +29 -9
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +93 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +274 -83
- package/lib/edm/edmUtils.js +29 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4727 -4323
- package/lib/json/from-csn.js +52 -23
- package/lib/json/to-csn.js +185 -71
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +9 -0
- package/lib/language/language.g4 +90 -31
- package/lib/main.js +4 -0
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +7 -1
- package/lib/model/csnUtils.js +5 -4
- package/lib/optionProcessor.js +7 -1
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/toCdl.js +45 -9
- package/lib/render/toHdbcds.js +100 -34
- package/lib/render/toSql.js +12 -4
- package/lib/render/utils/common.js +5 -9
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +14 -4
- package/lib/transform/db/flattening.js +13 -5
- package/lib/transform/db/transformExists.js +252 -58
- package/lib/transform/forHanaNew.js +7 -1
- package/lib/transform/forOdataNew.js +12 -8
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +76 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +33 -1
- package/lib/transform/translateAssocsToJoins.js +6 -4
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/package.json +1 -1
package/lib/language/language.g4
CHANGED
|
@@ -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
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1475
|
-
|
|
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
|
-
|
|
1700
|
-
|
|
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
|
-
|
|
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):
|
package/lib/model/api.js
ADDED
|
@@ -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
|
+
};
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -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,
|
|
767
|
+
return resolve( obj, refCtx, main, query, parent, baseEnv );
|
|
762
768
|
}
|
|
763
769
|
|
|
764
770
|
module.exports = {
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -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
|
}
|
package/lib/optionProcessor.js
CHANGED
|
@@ -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
|
-
|
|
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"
|
package/lib/render/toCdl.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
57
|
-
sourceStr
|
|
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
|
-
|
|
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
|
|
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
|