@sap/cds-compiler 5.7.2 → 5.8.0
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 +62 -2
- package/bin/cdsse.js +13 -1
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/options.js +2 -1
- package/lib/api/validate.js +9 -0
- package/lib/base/location.js +1 -1
- package/lib/base/message-registry.js +55 -20
- package/lib/base/messages.js +5 -2
- package/lib/base/model.js +8 -6
- package/lib/checks/assocOutsideService.js +40 -0
- package/lib/checks/featureFlags.js +4 -1
- package/lib/checks/types.js +7 -4
- package/lib/checks/validator.js +3 -0
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/checks.js +79 -17
- package/lib/compiler/define.js +60 -3
- package/lib/compiler/extend.js +1 -2
- package/lib/compiler/generate.js +1 -1
- package/lib/compiler/populate.js +17 -6
- package/lib/compiler/propagator.js +1 -1
- package/lib/compiler/resolve.js +181 -150
- package/lib/compiler/shared.js +276 -22
- package/lib/compiler/tweak-assocs.js +15 -4
- package/lib/compiler/xpr-rewrite.js +76 -50
- package/lib/edm/annotations/edmJson.js +1 -1
- package/lib/edm/annotations/genericTranslation.js +2 -2
- package/lib/edm/csn2edm.js +2 -2
- package/lib/edm/edmPreprocessor.js +15 -9
- package/lib/edm/edmUtils.js +12 -5
- package/lib/gen/CdlGrammar.checksum +1 -0
- package/lib/gen/CdlParser.js +2239 -2229
- package/lib/gen/Dictionary.json +55 -8
- package/lib/json/from-csn.js +37 -17
- package/lib/json/to-csn.js +4 -0
- package/lib/language/genericAntlrParser.js +7 -0
- package/lib/main.d.ts +5 -1
- package/lib/model/cloneCsn.js +1 -0
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +0 -5
- package/lib/modelCompare/utils/filter.js +2 -2
- package/lib/optionProcessor.js +2 -0
- package/lib/parsers/AstBuildingParser.js +72 -34
- package/lib/parsers/CdlGrammar.g4 +20 -19
- package/lib/parsers/XprTree.js +206 -0
- package/lib/parsers/index.js +1 -1
- package/lib/render/toCdl.js +61 -89
- package/lib/render/toSql.js +59 -29
- package/lib/render/utils/standardDatabaseFunctions.js +252 -15
- package/lib/transform/addTenantFields.js +9 -3
- package/lib/transform/db/assocsToQueries/transformExists.js +3 -0
- package/lib/transform/db/assocsToQueries/utils.js +10 -3
- package/lib/transform/db/expansion.js +3 -1
- package/lib/transform/db/flattening.js +7 -3
- package/lib/transform/db/killAnnotations.js +1 -0
- package/lib/transform/db/processSqlServices.js +70 -17
- package/lib/transform/draft/db.js +8 -3
- package/lib/transform/draft/odata.js +27 -4
- package/lib/transform/effective/main.js +37 -10
- package/lib/transform/effective/misc.js +4 -9
- package/lib/transform/effective/service.js +34 -0
- package/lib/transform/effective/types.js +28 -17
- package/lib/transform/forOdata.js +36 -10
- package/lib/transform/forRelationalDB.js +30 -18
- package/lib/transform/odata/adaptAnnotationRefs.js +37 -21
- package/lib/transform/odata/createForeignKeys.js +120 -116
- package/lib/transform/odata/flattening.js +10 -8
- package/lib/transform/transformUtils.js +58 -25
- package/lib/transform/translateAssocsToJoins.js +10 -6
- package/lib/transform/universalCsn/coreComputed.js +5 -1
- package/package.json +1 -1
- package/share/messages/message-explanations.json +1 -0
- package/share/messages/rewrite-not-supported.md +5 -0
- package/share/messages/rewrite-undefined-key.md +94 -0
|
@@ -45,6 +45,7 @@ tokens{ // reserved words
|
|
|
45
45
|
|
|
46
46
|
start returns[ source = new XsnSource( 'cdl' ) ]
|
|
47
47
|
:
|
|
48
|
+
{ this.afterBrace( null, 'init' ); }<always> // init sloppy semicolon handling
|
|
48
49
|
(
|
|
49
50
|
( <guard=namespaceRestriction> namespaceDeclaration[ $source ]
|
|
50
51
|
| usingDeclaration[ $source ]
|
|
@@ -57,7 +58,7 @@ start returns[ source = new XsnSource( 'cdl' ) ]
|
|
|
57
58
|
|
|
58
59
|
artifactsBlock[ art, start = undefined ]
|
|
59
60
|
:
|
|
60
|
-
'{'
|
|
61
|
+
'{' <prepare=afterBrace, arg=init>
|
|
61
62
|
{ $art.artifacts = this.createDict( $start ); $art.extensions = []; }
|
|
62
63
|
(
|
|
63
64
|
artifactDefOrExtend[ $art ]
|
|
@@ -235,7 +236,7 @@ aspectDef[ art, outer ]
|
|
|
235
236
|
entityDef[ art, outer ]
|
|
236
237
|
@finally{ this.attachLocation( $art ); }
|
|
237
238
|
:
|
|
238
|
-
ENTITY
|
|
239
|
+
ENTITY
|
|
239
240
|
name=namePath[ 'Entity' ]
|
|
240
241
|
{ this.addDef( $art, $outer, 'artifacts', 'entity', $name ); }
|
|
241
242
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
@@ -254,17 +255,12 @@ entityDef[ art, outer ]
|
|
|
254
255
|
query=queryExpression
|
|
255
256
|
{ $art.query = $query; $art.$syntax = 'entity'; }
|
|
256
257
|
|
|
|
258
|
+
<prepare=afterBrace, arg=sloppy> // enable special loop-exit, allow no `;`
|
|
257
259
|
query=projectionSpec
|
|
258
260
|
{ $art.query = $query; $art.$syntax = 'projection'; }
|
|
259
261
|
whereGroupByHaving[ $query ]?
|
|
260
262
|
orderByLimitOffset[ $query ]?
|
|
261
|
-
{
|
|
262
|
-
// not worth to nicer location here - is standard error in v6
|
|
263
|
-
this.warning( 'syntax-missing-proj-semicolon', this.la(),
|
|
264
|
-
{ expecting: [ "';'" ], offending: this.antlrName( this.la() ) },
|
|
265
|
-
'Missing $(EXPECTING) before $(OFFENDING)');
|
|
266
|
-
} }
|
|
267
|
-
<prepare=afterBrace> // disable special loop-exit, allow no `;`
|
|
263
|
+
{;}<prepare=afterBrace, arg=normal> // disable special loop-exit, allow no `;`
|
|
268
264
|
// TODO v6: these <prepare=afterBrace>s are extremely strange
|
|
269
265
|
)
|
|
270
266
|
)
|
|
@@ -811,6 +807,9 @@ typeOrIncludesSpec[ art ]
|
|
|
811
807
|
|
|
|
812
808
|
':'
|
|
813
809
|
(
|
|
810
|
+
// Since cds-compiler v5.8; new parser only
|
|
811
|
+
query=projectionSpec { $art.query = $query; $art.$syntax = 'projection'; }
|
|
812
|
+
|
|
|
814
813
|
typeExpression[ $art ]
|
|
815
814
|
|
|
|
816
815
|
<prefer>
|
|
@@ -1073,7 +1072,6 @@ projectionSpec returns[ default query = {} ]
|
|
|
1073
1072
|
@finally{ this.attachLocation($query); }
|
|
1074
1073
|
:
|
|
1075
1074
|
PROJECTION
|
|
1076
|
-
<prepare=afterBrace, arg=entity> // enable special loop-exit, TODO v6 delete
|
|
1077
1075
|
{ $query = { op: this.valueWithLocation( 'SELECT' ) }; }
|
|
1078
1076
|
ON
|
|
1079
1077
|
tab=fromRefWithOptAlias
|
|
@@ -1100,7 +1098,7 @@ queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
|
|
|
1100
1098
|
)
|
|
1101
1099
|
query=queryExpression
|
|
1102
1100
|
// with same op/quantifier: make left-assoc binary to nary:
|
|
1103
|
-
{ if ($expr.$parens || $op.val !== $expr.op
|
|
1101
|
+
{ if ($expr.$parens || $op.val !== $expr.op?.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier, location: { ...$.expr.location } }; } // TODO: ...$
|
|
1104
1102
|
{ $quantifier = undefined; }
|
|
1105
1103
|
{ $expr.args.push( $query ); this.attachLocation( $expr ); }
|
|
1106
1104
|
)*
|
|
@@ -1169,9 +1167,9 @@ tableExpression returns[ default expr = {} ] // TableOrJoin
|
|
|
1169
1167
|
(
|
|
1170
1168
|
join=CROSS JOIN
|
|
1171
1169
|
{ if ($expr?.join?.val !== 'cross' || $expr.$parens) $expr = { op: this.valueWithLocation(), join: this.valueWithLocation( undefined, $join ), args: [ $expr ] }; }
|
|
1172
|
-
( tab=tableOrQueryParens {
|
|
1170
|
+
( tab=tableOrQueryParens { const r = this.taggedIfQuery( $tab ); if (r) $expr.args.push( r ); }
|
|
1173
1171
|
{ this.attachLocation( $expr ); }
|
|
1174
|
-
| tab=fromRefWithOptAlias { $expr.args.push( $tab ); }
|
|
1172
|
+
| tab=fromRefWithOptAlias { if ($tab) $expr.args.push( $tab ); }
|
|
1175
1173
|
{ this.attachLocation( $expr ); }
|
|
1176
1174
|
)
|
|
1177
1175
|
|
|
|
@@ -1393,11 +1391,11 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
|
|
|
1393
1391
|
OVER { this.pushXprToken( $expr.suffix = [] ); }
|
|
1394
1392
|
overClause[ $expr.suffix ]
|
|
1395
1393
|
( e=expression[ ...{ expr: $expr } ]<atAltStart>
|
|
1396
|
-
{ Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
|
|
1394
|
+
{ Object.assign( $e.location || {}, $expr.location ); $art.value = this.attachLocation( $e )}
|
|
1397
1395
|
)?
|
|
1398
1396
|
|
|
|
1399
1397
|
e=expression[ ...{ expr: $expr } ]<atAltStart>
|
|
1400
|
-
{ Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
|
|
1398
|
+
{ Object.assign( $e.location || {}, $expr.location ); $art.value = this.attachLocation( $e )}
|
|
1401
1399
|
)
|
|
1402
1400
|
( AS Id['ItemAlias'] { $art.name = this.identAst(); }
|
|
1403
1401
|
| Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
|
|
@@ -1533,9 +1531,8 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
|
|
|
1533
1531
|
)*
|
|
1534
1532
|
;
|
|
1535
1533
|
|
|
1536
|
-
// TODO: ? params
|
|
1537
1534
|
expression returns[ default expr = {} ]
|
|
1538
|
-
|
|
1535
|
+
@finally{ if (this.s == null) this.attachLocation( $expr ); }
|
|
1539
1536
|
:
|
|
1540
1537
|
(
|
|
1541
1538
|
expressionOrQueryParens[ ...$ ]
|
|
@@ -1716,7 +1713,9 @@ options{ minTokensMatched = 1 }
|
|
|
1716
1713
|
(
|
|
1717
1714
|
','<prepare=nextFunctionArgument>
|
|
1718
1715
|
( expr=funcExpression { $pathStep.args.push( $expr ); }
|
|
1719
|
-
|
|
|
1716
|
+
| DeleteStarFromSet // Workaround for missing feature, see #13485, TODO
|
|
1717
|
+
| <exitLoop, guard=atRightParen>
|
|
1718
|
+
// TODO: later allow ')' <exitRule>, or ')'<mock, exitLoop>, or <exitBlock=MainAlt> with ( options{ block=MainAlt }: …)
|
|
1720
1719
|
)
|
|
1721
1720
|
)*
|
|
1722
1721
|
( // ORDER BY in generic functions, e.g. `first_value(id order by name)`
|
|
@@ -1725,6 +1724,8 @@ options{ minTokensMatched = 1 }
|
|
|
1725
1724
|
orderByClauseAsXpr[ $expr.args ]
|
|
1726
1725
|
{ this.attachLocation( $expr ); }
|
|
1727
1726
|
)?
|
|
1727
|
+
|
|
|
1728
|
+
DeleteStarFromSet // Workaround for missing feature, see #13485, TODO
|
|
1728
1729
|
)?
|
|
1729
1730
|
|
|
|
1730
1731
|
// TODO: if we want perfect CC and error recovery, `isNamedArg` would work
|
|
@@ -1772,6 +1773,7 @@ funcExpression returns[ default expr ] locals[ args ]
|
|
|
1772
1773
|
{ $expr = this.applyOpToken(); $args = $expr.args; }
|
|
1773
1774
|
e=expression { $expr.args.push( $e ); }
|
|
1774
1775
|
)
|
|
1776
|
+
// TODO: some <restrict=Id> here?
|
|
1775
1777
|
( options{ lookahead = lGenericSeparator; }
|
|
1776
1778
|
:
|
|
1777
1779
|
GenericSeparator
|
|
@@ -2011,7 +2013,6 @@ annoValue returns[ default value = {} ]
|
|
|
2011
2013
|
else { $value.struct = Object.create(null); $value.literal = 'struct'; }
|
|
2012
2014
|
}
|
|
2013
2015
|
(
|
|
2014
|
-
// TODO: complain about empty loop if top-level as before
|
|
2015
2016
|
// TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
|
|
2016
2017
|
// after `{` if top-level
|
|
2017
2018
|
sub=annoStructValue
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Create a hierarchical expression tree from an `xpr` array
|
|
2
|
+
|
|
3
|
+
// See ./CdlGrammar.js for the operator precedences.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
const prefixOperators = { // see <prec=…,prefix> in `expression` of CdlGrammar
|
|
10
|
+
__proto__: null,
|
|
11
|
+
new: 33, // special in CDL (only before ref), clarify with `.`
|
|
12
|
+
exists: 33, // special in CDL
|
|
13
|
+
'+': 30, // note: binary `.` and `over` have higher precedence!
|
|
14
|
+
'-': 30,
|
|
15
|
+
not: 8,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const binaryOperators = {
|
|
19
|
+
__proto__: null,
|
|
20
|
+
'.': 37, // nary
|
|
21
|
+
over: 35, // TODO: only after ref with arg?
|
|
22
|
+
'*': 24, // nary
|
|
23
|
+
'/': 24,
|
|
24
|
+
'+': 22,
|
|
25
|
+
'-': 22,
|
|
26
|
+
'||': 20,
|
|
27
|
+
'=': 10, // with ANY/SOME/ALL
|
|
28
|
+
'<>': 10,
|
|
29
|
+
'>': 10,
|
|
30
|
+
'>=': 10,
|
|
31
|
+
'<': 10,
|
|
32
|
+
'<=': 10,
|
|
33
|
+
'!=': 10,
|
|
34
|
+
'==': 10, // not supported yet in CDL or backends
|
|
35
|
+
and: 4,
|
|
36
|
+
or: 2,
|
|
37
|
+
// with second token or ternary (in the grammar, these ops have prec=10, but
|
|
38
|
+
// also assoc=none, i.e. could not be used without parens together):
|
|
39
|
+
is: 11, // is binary op here, not postfix
|
|
40
|
+
in: 13,
|
|
41
|
+
between: 13,
|
|
42
|
+
like: 13,
|
|
43
|
+
not: 15, // specially handled
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const secondTokens = {
|
|
47
|
+
// the value is the precedence of the first token listed in `binaryOperators`
|
|
48
|
+
__proto__: null,
|
|
49
|
+
any: 10, // for `=` etc
|
|
50
|
+
some: 10,
|
|
51
|
+
all: 10,
|
|
52
|
+
not: 11, // for `is`
|
|
53
|
+
between: 15, // for `not`
|
|
54
|
+
in: 15,
|
|
55
|
+
like: 15,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const naryOperators = {
|
|
59
|
+
__proto__: null,
|
|
60
|
+
'.': true, // CSN-tree really as left-assoc binary?
|
|
61
|
+
'*': true,
|
|
62
|
+
'/': true,
|
|
63
|
+
'+': true,
|
|
64
|
+
'-': true,
|
|
65
|
+
'||': true,
|
|
66
|
+
and: true,
|
|
67
|
+
or: true,
|
|
68
|
+
between: 'and',
|
|
69
|
+
like: 'escape',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class XprTree {
|
|
74
|
+
nodes; // array value of CSN property `xpr`/`where`/…
|
|
75
|
+
nodeIdx = 0;
|
|
76
|
+
args; // corresponding XSN array, with already tree-like sub expressions
|
|
77
|
+
location;
|
|
78
|
+
|
|
79
|
+
constructor( nodes, args, location ) {
|
|
80
|
+
this.nodes = nodes;
|
|
81
|
+
this.args = args;
|
|
82
|
+
this.location = location;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
tree() {
|
|
86
|
+
const args = [];
|
|
87
|
+
const { length } = this.args;
|
|
88
|
+
while (this.nodeIdx < length) {
|
|
89
|
+
const expr = this.expression( -1 );
|
|
90
|
+
if (expr)
|
|
91
|
+
args.push( expr );
|
|
92
|
+
}
|
|
93
|
+
return (args.length === 1) ? args[0] : this.create( args );
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
expression( parentPrec ) {
|
|
97
|
+
let append;
|
|
98
|
+
let naryOp;
|
|
99
|
+
let args;
|
|
100
|
+
|
|
101
|
+
// Term = ref/val or unary operator with expression as operand
|
|
102
|
+
let expr = this.args[this.nodeIdx];
|
|
103
|
+
if (!expr)
|
|
104
|
+
return expr;
|
|
105
|
+
let node = this.nodes[this.nodeIdx++];
|
|
106
|
+
if (typeof node === 'string') {
|
|
107
|
+
const prec = prefixOperators[node]; // <prec=…,prefix> in CdlGrammar
|
|
108
|
+
if (prec) {
|
|
109
|
+
const right = this.expression( prec - 1 );
|
|
110
|
+
if (!right)
|
|
111
|
+
return expr;
|
|
112
|
+
expr = this.create( [ expr, right ] );
|
|
113
|
+
}
|
|
114
|
+
else if (node === 'case') {
|
|
115
|
+
expr = this.caseWhen( [ expr ] );
|
|
116
|
+
}
|
|
117
|
+
else { // unknown token (keyword in CDL):
|
|
118
|
+
return expr; // …from fns with irregular syntax?
|
|
119
|
+
// also handles `null` as right side of `is` in `is null`
|
|
120
|
+
// TODO: `(` from CSN v0.x ?
|
|
121
|
+
// It is important not to handle binary ops after this, because otherwise
|
|
122
|
+
// we would not properly parse functions with irregular syntax
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
node = this.nodes[this.nodeIdx];
|
|
127
|
+
while (typeof node === 'string') {
|
|
128
|
+
const prec = binaryOperators[node]; // <prec=…> in CdlGrammar
|
|
129
|
+
if (!prec || parentPrec >= prec)
|
|
130
|
+
return expr;
|
|
131
|
+
|
|
132
|
+
// handle n-ary operators including ternary
|
|
133
|
+
append = (typeof naryOp === 'string')
|
|
134
|
+
? !append && naryOp // `and` after `between`, `escape` after `like`˛
|
|
135
|
+
: naryOp && node; // nary operators like `+`
|
|
136
|
+
if (node === append) {
|
|
137
|
+
args.push( this.args[this.nodeIdx++] );
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
naryOp = naryOperators[node];
|
|
141
|
+
append = false;
|
|
142
|
+
args = [ expr, this.args[this.nodeIdx++] ];
|
|
143
|
+
expr = this.create( args, naryOp === true );
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// handle second token of operator:
|
|
147
|
+
const second = this.nodes[this.nodeIdx];
|
|
148
|
+
if (typeof second === 'string' && secondTokens[second] === prec && !append) {
|
|
149
|
+
args.push( this.args[this.nodeIdx++] );
|
|
150
|
+
if (node === 'not')
|
|
151
|
+
naryOp = naryOperators[second];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// the right side
|
|
155
|
+
const right = this.expression( prec );
|
|
156
|
+
if (!right) // incomplete
|
|
157
|
+
return expr;
|
|
158
|
+
args.push( right );
|
|
159
|
+
node = this.nodes[this.nodeIdx];
|
|
160
|
+
// TODO: simplyfy between-and and like-escape with pushTokenAndExpression()
|
|
161
|
+
}
|
|
162
|
+
return expr;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
caseWhen( args ) {
|
|
166
|
+
const expr = this.create( args );
|
|
167
|
+
let node = this.nodes[this.nodeIdx];
|
|
168
|
+
if (node !== 'when') {
|
|
169
|
+
const value = this.expression( -1 );
|
|
170
|
+
if (value)
|
|
171
|
+
args.push( value );
|
|
172
|
+
node = this.nodes[this.nodeIdx];
|
|
173
|
+
}
|
|
174
|
+
while (node === 'when') {
|
|
175
|
+
node = this.pushTokenAndExpression( args );
|
|
176
|
+
if (node === 'then')
|
|
177
|
+
node = this.pushTokenAndExpression( args );
|
|
178
|
+
}
|
|
179
|
+
if (node === 'else')
|
|
180
|
+
node = this.pushTokenAndExpression( args );
|
|
181
|
+
if (node === 'end')
|
|
182
|
+
args.push( this.args[this.nodeIdx++] );
|
|
183
|
+
return expr;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
pushTokenAndExpression( args ) {
|
|
187
|
+
args.push( this.args[this.nodeIdx++] );
|
|
188
|
+
const value = this.expression( -1 );
|
|
189
|
+
if (value)
|
|
190
|
+
args.push( value );
|
|
191
|
+
return this.nodes[this.nodeIdx];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
create( args, isNary = false ) {
|
|
195
|
+
// could be adopted for CSN expression tree
|
|
196
|
+
return {
|
|
197
|
+
op: { val: (isNary ? 'nary' : 'ixpr'), location: this.location },
|
|
198
|
+
location: this.location,
|
|
199
|
+
args,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = {
|
|
205
|
+
xprAsTree: ( nodes, args, location ) => (new XprTree( nodes, args, location )).tree(),
|
|
206
|
+
};
|
package/lib/parsers/index.js
CHANGED
|
@@ -21,7 +21,7 @@ function parseCdl( source, filename = '<undefined>.cds',
|
|
|
21
21
|
options = {}, messageFunctions = null,
|
|
22
22
|
rule = 'cdl' ) {
|
|
23
23
|
const rulespec = rules[rule];
|
|
24
|
-
if (!options.newParser
|
|
24
|
+
if (!options.newParser && !options.newparser)
|
|
25
25
|
return parseWithAntlr( source, filename, options, messageFunctions, rulespec );
|
|
26
26
|
const { CdlParser } = gen;
|
|
27
27
|
if (CdlParser.tracingParser) // tracing → direct console output of message
|
package/lib/render/toCdl.js
CHANGED
|
@@ -124,7 +124,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
124
124
|
// This environment is passed down the call hierarchy, for dealing with
|
|
125
125
|
// indentation and name resolution issues
|
|
126
126
|
const env = createEnv({ path: [ 'vocabularies', name ] });
|
|
127
|
-
const sourceStr =
|
|
127
|
+
const sourceStr = renderArtifact(name, anno, env, 'annotation');
|
|
128
128
|
result += `${sourceStr}\n`;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -452,7 +452,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
452
452
|
case 'entity':
|
|
453
453
|
if (art.query || art.projection)
|
|
454
454
|
return renderView(artifactName, art, env);
|
|
455
|
-
return
|
|
455
|
+
return renderArtifact(artifactName, art, env);
|
|
456
456
|
case 'aspect':
|
|
457
457
|
return renderAspect(artifactName, art, env);
|
|
458
458
|
|
|
@@ -460,16 +460,16 @@ function csnToCdl( csn, options, msg ) {
|
|
|
460
460
|
case 'service':
|
|
461
461
|
return renderContextOrService(artifactName, art, env);
|
|
462
462
|
|
|
463
|
-
case 'type':
|
|
464
463
|
case 'annotation': // annotation in 'csn.definitions' for compiler v1 compatibility
|
|
465
|
-
return
|
|
464
|
+
return renderArtifact(artifactName, art, env, 'annotation');
|
|
466
465
|
|
|
467
466
|
case 'action':
|
|
468
467
|
case 'function':
|
|
469
468
|
return renderActionOrFunction(artifactName, art, env);
|
|
470
469
|
|
|
470
|
+
case 'type':
|
|
471
471
|
case 'event':
|
|
472
|
-
return
|
|
472
|
+
return renderArtifact(artifactName, art, env);
|
|
473
473
|
|
|
474
474
|
default:
|
|
475
475
|
throw new ModelError(`to.cdl: Unknown artifact kind: '${art.kind}' at ${JSON.stringify(env.path)}`);
|
|
@@ -477,30 +477,70 @@ function csnToCdl( csn, options, msg ) {
|
|
|
477
477
|
}
|
|
478
478
|
|
|
479
479
|
/**
|
|
480
|
+
* TODO: Also use this function for other kinds such as entities, aspects and views.
|
|
481
|
+
*
|
|
480
482
|
* @param {string} artifactName
|
|
481
483
|
* @param {CSN.Artifact} art
|
|
482
484
|
* @param {CdlRenderEnvironment} env
|
|
485
|
+
* @param {string} [overrideKind] If set, override the artifact kind.
|
|
483
486
|
*/
|
|
484
|
-
function
|
|
487
|
+
function renderArtifact( artifactName, art, env, overrideKind ) {
|
|
485
488
|
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
489
|
+
let kind = overrideKind || art.$syntax === 'aspect' && 'aspect' || art.kind;
|
|
490
|
+
if (art.abstract)
|
|
491
|
+
kind = `abstract ${ kind }`;
|
|
486
492
|
const normalizedArtifactName = renderArtifactName(artifactName, env);
|
|
487
|
-
result += `${env.indent}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
493
|
+
result += `${env.indent}${kind} ${normalizedArtifactName}`;
|
|
494
|
+
|
|
495
|
+
if (art.params)
|
|
496
|
+
result += renderParameters(art, env);
|
|
497
|
+
|
|
498
|
+
let isDirectStruct = false;
|
|
499
|
+
const isQuery = art.query || art.projection;
|
|
500
|
+
if (isQuery) {
|
|
491
501
|
result += ' : ';
|
|
492
|
-
// events (should) only support "projections"
|
|
502
|
+
// types/events (should) only support "projections"
|
|
493
503
|
result += renderQuery(getNormalizedQuery(art).query, true, 'projection',
|
|
494
504
|
env.withSubPath([ art.projection ? 'projection' : 'query' ]));
|
|
495
|
-
result += ';\n';
|
|
496
505
|
}
|
|
497
|
-
else
|
|
498
|
-
|
|
499
|
-
|
|
506
|
+
else {
|
|
507
|
+
const type = renderTypeReferenceAndProps(art, env);
|
|
508
|
+
if (type) {
|
|
509
|
+
isDirectStruct = type.startsWith('{');
|
|
510
|
+
|
|
511
|
+
if (art.includes?.length && isDirectStruct) {
|
|
512
|
+
// We can only render includes, if the type is directly structured. Otherwise, we would
|
|
513
|
+
// render e.g. `type T : Include : T2;`, which is invalid. We use `extend` in such cases.
|
|
514
|
+
result += renderIncludes(art.includes, env);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// For nicer output, no colon if unnamed structure is used.
|
|
518
|
+
result += (!art.type && art.elements) ? ` ${type}` : ` : ${type}`;
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
msg.warning('syntax-missing-type', env.path, { '#': art.kind, name: artifactName }, {
|
|
522
|
+
std: 'Missing type for definition $(NAME); can\'t be represented in CDL',
|
|
523
|
+
entity: 'Missing elements for entity $(NAME); can\'t be represented in CDL',
|
|
524
|
+
});
|
|
525
|
+
}
|
|
500
526
|
}
|
|
501
|
-
|
|
502
|
-
|
|
527
|
+
|
|
528
|
+
if (art.actions) {
|
|
529
|
+
if (!isQuery && !isDirectStruct) {
|
|
530
|
+
// If there are no elements nor query, but actions, CDL syntax requires braces.
|
|
531
|
+
result += ' { }';
|
|
532
|
+
}
|
|
533
|
+
result += renderActionsAndFunctions(art, env);
|
|
503
534
|
}
|
|
535
|
+
|
|
536
|
+
result += ';\n';
|
|
537
|
+
|
|
538
|
+
if (art.includes?.length && !isDirectStruct) {
|
|
539
|
+
// If we're not a directly structured type, render the `includes` as `extend`
|
|
540
|
+
// statements directly below the type definition.
|
|
541
|
+
result += renderExtendStatement(artifactName, { includes: art.includes }, env);
|
|
542
|
+
}
|
|
543
|
+
|
|
504
544
|
return result;
|
|
505
545
|
}
|
|
506
546
|
|
|
@@ -516,35 +556,6 @@ function csnToCdl( csn, options, msg ) {
|
|
|
516
556
|
return `${result} {};\n`;
|
|
517
557
|
}
|
|
518
558
|
|
|
519
|
-
/**
|
|
520
|
-
* Render a (non-projection, non-view) entity. Return the resulting source string.
|
|
521
|
-
*
|
|
522
|
-
* @param {string} artifactName
|
|
523
|
-
* @param {CSN.Artifact} art
|
|
524
|
-
* @param {CdlRenderEnvironment} env
|
|
525
|
-
* @return {string}
|
|
526
|
-
*/
|
|
527
|
-
function renderEntity( artifactName, art, env ) {
|
|
528
|
-
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
529
|
-
result += env.indent + (art.abstract ? 'abstract ' : '');
|
|
530
|
-
result += `entity ${renderArtifactName(artifactName, env)}`;
|
|
531
|
-
|
|
532
|
-
if (art.params)
|
|
533
|
-
result += renderParameters(art, env);
|
|
534
|
-
if (art.includes)
|
|
535
|
-
result += renderIncludes(art.includes, env);
|
|
536
|
-
|
|
537
|
-
if (art.elements)
|
|
538
|
-
result += ` ${renderElements(art, env)}`;
|
|
539
|
-
else if (art.actions)
|
|
540
|
-
// if there are no elements, but actions, CDL syntax requires braces.
|
|
541
|
-
result += ' { }';
|
|
542
|
-
|
|
543
|
-
result += `${renderActionsAndFunctions(art, env)};\n`;
|
|
544
|
-
|
|
545
|
-
return result;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
559
|
/**
|
|
549
560
|
* Render an aspect. Return the resulting source string.
|
|
550
561
|
* Behaves very similar to renderEntity, _except_ that aspects are
|
|
@@ -938,8 +949,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
938
949
|
// No expression to render for { * } as alias
|
|
939
950
|
let result = (obj.as && obj.expand && !obj.ref) ? '' : exprRenderer.renderExpr(withoutCast(obj), env);
|
|
940
951
|
|
|
952
|
+
const isAnonymousExpand = (obj.expand && !obj.ref);
|
|
953
|
+
|
|
941
954
|
// s as alias { * }
|
|
942
|
-
if (obj.as &&
|
|
955
|
+
if (obj.as && !isAnonymousExpand)
|
|
943
956
|
result += renderAlias(obj.as, env);
|
|
944
957
|
|
|
945
958
|
// We found a leaf - no further drilling
|
|
@@ -971,7 +984,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
971
984
|
result += ` excluding { ${obj.excluding.join(',')} }`;
|
|
972
985
|
|
|
973
986
|
// { * } as expand
|
|
974
|
-
if (
|
|
987
|
+
if (obj.as && isAnonymousExpand)
|
|
975
988
|
result += renderAlias(obj.as, env);
|
|
976
989
|
|
|
977
990
|
return result;
|
|
@@ -1268,47 +1281,6 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1268
1281
|
return result;
|
|
1269
1282
|
}
|
|
1270
1283
|
|
|
1271
|
-
/**
|
|
1272
|
-
* Render a type (derived or structured) or an annotation decl with name 'artifactName'.
|
|
1273
|
-
* Return the resulting source string.
|
|
1274
|
-
*
|
|
1275
|
-
* @param {string} artifactName
|
|
1276
|
-
* @param {CSN.Artifact} art
|
|
1277
|
-
* @param {CdlRenderEnvironment} env
|
|
1278
|
-
* @param {String} [artType] Used for rendering `csn.vocabularies`, as the annotations there do not have a kind.
|
|
1279
|
-
* @return {string}
|
|
1280
|
-
*/
|
|
1281
|
-
function renderTypeOrAnnotation( artifactName, art, env, artType ) {
|
|
1282
|
-
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
1283
|
-
result += `${env.indent + (artType || art.$syntax || art.kind )} ${renderArtifactName(artifactName, env)}`;
|
|
1284
|
-
|
|
1285
|
-
const type = renderTypeReferenceAndProps(art, env);
|
|
1286
|
-
const isDirectStruct = type?.startsWith('{');
|
|
1287
|
-
if (art.includes?.length && isDirectStruct)
|
|
1288
|
-
// We can only render includes, if the type is directly structured. Otherwise, we would
|
|
1289
|
-
// render e.g. `type T : Include : T2;`, which is invalid. We use `extend` in such cases.
|
|
1290
|
-
result += renderIncludes(art.includes, env);
|
|
1291
|
-
|
|
1292
|
-
if (type) {
|
|
1293
|
-
// For nicer output, no colon if unnamed structure is used.
|
|
1294
|
-
result += (!art.type && art.elements) ? ` ${type}` : ` : ${type}`;
|
|
1295
|
-
}
|
|
1296
|
-
else {
|
|
1297
|
-
msg.warning('syntax-missing-type', env.path, { name: artifactName },
|
|
1298
|
-
'Missing type for definition $(NAME); can\'t be represented in CDL');
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
result += ';\n';
|
|
1302
|
-
|
|
1303
|
-
if (art.includes?.length && !isDirectStruct) {
|
|
1304
|
-
// If we're not a directly structured type, render the `includes` as `extend`
|
|
1305
|
-
// statements directly below the type definition.
|
|
1306
|
-
result += renderExtendStatement(artifactName, { includes: art.includes }, env);
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
return result;
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
1284
|
/**
|
|
1313
1285
|
* Render a reference to a type used by 'artifact' (named or inline) and (element) properties
|
|
1314
1286
|
* such as `not null` and `default <xpr>`.
|