@sap/cds-compiler 5.8.2 → 5.9.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 +31 -0
- package/bin/cds_remove_invalid_whitespace.js +5 -3
- package/bin/cds_update_identifiers.js +9 -6
- package/bin/cdsc.js +79 -59
- package/bin/cdsse.js +14 -10
- package/bin/cdsv2m.js +3 -1
- package/lib/api/options.js +28 -6
- package/lib/base/message-registry.js +15 -4
- package/lib/checks/validator.js +3 -0
- package/lib/compiler/base.js +1 -1
- package/lib/compiler/checks.js +70 -50
- package/lib/compiler/extend.js +1 -1
- package/lib/compiler/generate.js +8 -2
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/lsp-api.js +1 -1
- package/lib/compiler/propagator.js +2 -2
- package/lib/compiler/resolve.js +78 -31
- package/lib/compiler/shared.js +3 -3
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +10 -0
- package/lib/compiler/xpr-rewrite.js +1 -1
- package/lib/edm/annotations/edmJson.js +42 -39
- package/lib/edm/annotations/genericTranslation.js +55 -55
- package/lib/edm/annotations/preprocessAnnotations.js +5 -5
- package/lib/edm/csn2edm.js +21 -16
- package/lib/edm/edm.js +62 -62
- package/lib/edm/edmAnnoPreprocessor.js +2 -2
- package/lib/edm/edmInboundChecks.js +1 -1
- package/lib/edm/edmPreprocessor.js +32 -32
- package/lib/edm/edmUtils.js +8 -8
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +77 -81
- package/lib/gen/Dictionary.json +3062 -3072
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +1238 -1236
- package/lib/json/from-csn.js +1 -1
- package/lib/json/to-csn.js +30 -3
- package/lib/language/genericAntlrParser.js +16 -0
- package/lib/main.d.ts +79 -1
- package/lib/model/csnRefs.js +12 -5
- package/lib/model/xprAsTree.js +71 -0
- package/lib/modelCompare/utils/filter.js +1 -1
- package/lib/optionProcessor.js +46 -32
- package/lib/parsers/CdlGrammar.g4 +33 -28
- package/lib/parsers/Lexer.js +1 -1
- package/lib/parsers/XprTree.js +25 -16
- package/lib/render/toCdl.js +902 -414
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +8 -0
- package/lib/render/utils/common.js +2 -2
- package/lib/render/utils/operators.js +160 -0
- package/lib/render/utils/pretty.js +337 -0
- package/lib/sql-identifier.js +7 -9
- package/lib/transform/addTenantFields.js +39 -41
- package/lib/transform/db/applyTransformations.js +4 -4
- package/lib/transform/db/assertUnique.js +6 -5
- package/lib/transform/db/associations.js +3 -3
- package/lib/transform/db/assocsToQueries/transformExists.js +13 -13
- package/lib/transform/db/assocsToQueries/utils.js +8 -0
- package/lib/transform/db/backlinks.js +19 -14
- package/lib/transform/db/constraints.js +6 -6
- package/lib/transform/db/expansion.js +1 -1
- package/lib/transform/db/flattening.js +2 -2
- package/lib/transform/db/groupByOrderBy.js +1 -1
- package/lib/transform/db/processSqlServices.js +3 -3
- package/lib/transform/db/rewriteCalculatedElements.js +2 -2
- package/lib/transform/db/temporal.js +7 -9
- package/lib/transform/db/views.js +6 -6
- package/lib/transform/draft/odata.js +2 -0
- package/lib/transform/effective/annotations.js +1 -1
- package/lib/transform/effective/associations.js +1 -1
- package/lib/transform/effective/main.js +1 -0
- package/lib/transform/effective/service.js +2 -2
- package/lib/transform/forRelationalDB.js +11 -5
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/adaptAnnotationRefs.js +10 -9
- package/lib/transform/parseExpr.js +2 -2
- package/lib/transform/transformUtils.js +9 -7
- package/lib/transform/translateAssocsToJoins.js +0 -2
- package/lib/transform/universalCsn/coreComputed.js +2 -2
- package/lib/utils/moduleResolve.js +7 -5
- package/package.json +1 -1
- package/share/messages/def-upcoming-virtual-change.md +55 -0
- package/share/messages/file-unexpected-case-mismatch.md +61 -0
- package/share/messages/message-explanations.json +2 -0
- package/lib/transform/braceExpression.js +0 -77
package/lib/render/toCdl.js
CHANGED
|
@@ -1,133 +1,623 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// to.cdl() renderer
|
|
4
|
+
//
|
|
5
|
+
// This file contains the whole to.cdl(), which takes CSN and outputs CDL.
|
|
6
|
+
// It used e.g. by `cds import`.
|
|
7
|
+
//
|
|
8
|
+
//
|
|
9
|
+
// # Development Notes
|
|
10
|
+
//
|
|
11
|
+
// ## Abbreviations used
|
|
12
|
+
// - fqn : fully qualified name, i.e. a name that is a global definition reference
|
|
13
|
+
//
|
|
14
|
+
|
|
15
|
+
|
|
3
16
|
const keywords = require('../base/keywords');
|
|
4
17
|
const { cdlNewLineRegEx } = require('../language/textUtils');
|
|
5
18
|
const { findElement, createExpressionRenderer, withoutCast } = require('./utils/common');
|
|
6
19
|
const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEscapes');
|
|
7
20
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
8
|
-
const {
|
|
21
|
+
const { normalizeTypeRef, forEachDefinition } = require('../model/csnUtils');
|
|
9
22
|
const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
|
|
10
23
|
const { isBetaEnabled } = require('../base/model');
|
|
11
|
-
const { ModelError } = require('../base/error');
|
|
24
|
+
const { ModelError, CompilerAssertion } = require('../base/error');
|
|
12
25
|
const { typeParameters, specialFunctions } = require('../compiler/builtins');
|
|
13
26
|
const { isAnnotationExpression } = require('../base/builtins');
|
|
14
27
|
const { forEach } = require('../utils/objectUtils');
|
|
15
|
-
const {
|
|
16
|
-
getNormalizedQuery,
|
|
17
|
-
} = require('../model/csnUtils');
|
|
18
28
|
const { isBuiltinType } = require('../base/builtins');
|
|
19
29
|
const { cloneFullCsn } = require('../model/cloneCsn');
|
|
20
|
-
const { getKeysDict } = require('../model/csnRefs');
|
|
30
|
+
const { getKeysDict, implicitAs } = require('../model/csnRefs');
|
|
21
31
|
const { undelimitedIdentifierRegex } = require('../parsers/identifiers');
|
|
32
|
+
const { getNormalizedQuery } = require('../model/csnUtils');
|
|
33
|
+
const {
|
|
34
|
+
line,
|
|
35
|
+
pretty,
|
|
36
|
+
nestBy,
|
|
37
|
+
bracketBlock,
|
|
38
|
+
joinDocuments,
|
|
39
|
+
} = require('./utils/pretty');
|
|
22
40
|
|
|
23
41
|
const specialFunctionKeywords = Object.create(null);
|
|
24
42
|
|
|
43
|
+
const MAX_LINE_WIDTH = 72;
|
|
44
|
+
const INDENT_SIZE = 2;
|
|
45
|
+
|
|
46
|
+
function format( document ) {
|
|
47
|
+
return pretty(document, MAX_LINE_WIDTH);
|
|
48
|
+
}
|
|
49
|
+
|
|
25
50
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
* @param {string} path
|
|
52
|
+
* @returns {string}
|
|
53
|
+
*/
|
|
54
|
+
function rootPathSegment( path ) {
|
|
55
|
+
// RegEx is at least twice as fast as .split()[0]
|
|
56
|
+
return path.match(/^[^.]+/)[0];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Path alias to be rendered as a USING statement.
|
|
61
|
+
*/
|
|
62
|
+
class UsingAlias {
|
|
63
|
+
path;
|
|
64
|
+
alias;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} path
|
|
68
|
+
* @param {string} alias
|
|
69
|
+
*/
|
|
70
|
+
constructor(path, alias) {
|
|
71
|
+
this.path = path;
|
|
72
|
+
this.alias = alias;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
requiresExplicitAlias() {
|
|
76
|
+
return this.alias && implicitAs(this.path) !== this.alias;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
class NameScopeStack {
|
|
81
|
+
/** @type {DefinitionPathTree[]} */
|
|
82
|
+
#scopes = [];
|
|
83
|
+
/** @type {Record<string, UsingAlias>} */
|
|
84
|
+
#aliasToFqn = Object.create(null);
|
|
85
|
+
/** @type {Record<string, UsingAlias>} */
|
|
86
|
+
#fqnToAlias = Object.create(null);
|
|
87
|
+
/** @type {string|null} */
|
|
88
|
+
#namespaceAlias = null;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
@param {DefinitionPathTree} root
|
|
92
|
+
@param {CSN.Model} csn
|
|
93
|
+
*/
|
|
94
|
+
setRootScope( root, csn ) {
|
|
95
|
+
root.availableRootPaths = Object.assign(Object.create(null), root.children);
|
|
96
|
+
this.#scopes = [ root ];
|
|
97
|
+
|
|
98
|
+
this.#prepareUniqueUsingsForRootPaths(csn);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {DefinitionPathTree} scope
|
|
103
|
+
*/
|
|
104
|
+
pushNameEnv(scope) {
|
|
105
|
+
const outerScope = this.#scopes.at(-1);
|
|
106
|
+
|
|
107
|
+
const isNamespace = this.#scopes.length === 1 && !scope.definition;
|
|
108
|
+
if (isNamespace)
|
|
109
|
+
this.#namespaceAlias = implicitAs(scope.name);
|
|
110
|
+
|
|
111
|
+
// Own children are always available.
|
|
112
|
+
// Root paths of the outer scope are also available in the inner scope.
|
|
113
|
+
scope.availableRootPaths = Object.assign(Object.create(null), outerScope.availableRootPaths, scope.children);
|
|
114
|
+
this.#scopes.push(scope);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
popNameEnv() {
|
|
118
|
+
const popped = this.#scopes.pop();
|
|
119
|
+
const wasNamespace = this.#scopes.length === 1 && !popped.definition;
|
|
120
|
+
if (wasNamespace)
|
|
121
|
+
this.#namespaceAlias = null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* To be able to refer to definitions outside the current scope, we need to have
|
|
126
|
+
* unique USING statements. The most stable way is to create a USING statement for
|
|
127
|
+
* root path-segments on-demand and give it an alias by having it unique in the set
|
|
128
|
+
* of all path segments of all definitions.
|
|
129
|
+
*
|
|
130
|
+
* While this will still lead to some long paths here and there, it is the most
|
|
131
|
+
* secure way to avoid ambiguities due to shadowing names.
|
|
132
|
+
*
|
|
133
|
+
* @param {CSN.Model} csn
|
|
134
|
+
*/
|
|
135
|
+
#prepareUniqueUsingsForRootPaths(csn) {
|
|
136
|
+
// We include vocabularies here, too, because their names are affected by a global "namespace".
|
|
137
|
+
const names = [
|
|
138
|
+
...Object.keys(csn.definitions || {}),
|
|
139
|
+
...Object.keys(csn.vocabularies || {}),
|
|
140
|
+
...(csn.extensions || []).map(ext => ext.extend || ext.annotate || ''),
|
|
141
|
+
];
|
|
142
|
+
const segmentedNames = names.map(name => name.split('.'));
|
|
143
|
+
this.nonRootSegments = new Set(segmentedNames.map(segments => segments.slice(1)).flat(1));
|
|
144
|
+
// Don't use `this.#scopes[0].availableRootPaths`, as that will contain unreachable paths,
|
|
145
|
+
// e.g. for a file that contains `namespace a.b`, `a` is not reachable.
|
|
146
|
+
this.rootSegments = new Set(segmentedNames.map(name => name[0]));
|
|
147
|
+
this.rootSegments.add('cds'); // builtin namespaces
|
|
148
|
+
this.rootSegments.add('hana');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {string} fqn Path for which we want to add an alias.
|
|
153
|
+
*/
|
|
154
|
+
#addUsingAlias( fqn ) {
|
|
155
|
+
const segments = fqn.split('.');
|
|
156
|
+
let aliasName = segments.at(-1);
|
|
157
|
+
// An explicit alias only needs to be used if the implicit one has the possibility of
|
|
158
|
+
// being shadowed in any scope or if there is already an alias of that name.
|
|
159
|
+
if (this.nonRootSegments.has(aliasName) || this.#aliasToFqn[aliasName]) {
|
|
160
|
+
// There is a non-root segment of the same root name, hence the need for aliases.
|
|
161
|
+
let counter = 0;
|
|
162
|
+
aliasName += '_';
|
|
163
|
+
const baseAlias = aliasName;
|
|
164
|
+
while (this.nonRootSegments.has(aliasName) || this.rootSegments.has(aliasName) || this.#aliasToFqn[aliasName]) {
|
|
165
|
+
// Alias must be unique among _all_ segments and existing USINGs.
|
|
166
|
+
aliasName = `${baseAlias}${++counter}`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Always add an alias, even if unnecessary, as we'd otherwise try to create
|
|
171
|
+
// it in #useAliasForPath() again if the same rootName is seen again.
|
|
172
|
+
if (this.#aliasToFqn[aliasName])
|
|
173
|
+
throw new CompilerAssertion(`to.cdl: Alias "${aliasName}" already exists; collision for ${fqn} and ${this.#aliasToFqn[aliasName].path}`);
|
|
174
|
+
const alias = new UsingAlias(fqn, aliasName);
|
|
175
|
+
this.#aliasToFqn[aliasName] = alias;
|
|
176
|
+
this.#fqnToAlias[fqn] = alias;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* We assume that definition names, when rendered, are always relative
|
|
181
|
+
* to the current name environment.
|
|
182
|
+
*
|
|
183
|
+
* This function must only be used for statements that _create_ definitions
|
|
184
|
+
* and not for references _to_ definitions.
|
|
185
|
+
*
|
|
186
|
+
* @param {string} fqn
|
|
187
|
+
* @returns {string}
|
|
188
|
+
*/
|
|
189
|
+
definitionName(fqn) {
|
|
190
|
+
const leaf = this.#scopes.at(-1);
|
|
191
|
+
if (!leaf?.name)
|
|
192
|
+
return fqn;
|
|
193
|
+
if (isBuiltinType(fqn)) {
|
|
194
|
+
// For e.g. `annotate` statements:
|
|
195
|
+
// - `annotate String;` is invalid
|
|
196
|
+
// - `annotate cds.String;` works
|
|
197
|
+
return fqn;
|
|
198
|
+
}
|
|
199
|
+
if (fqn.startsWith(`${leaf.name }.`))
|
|
200
|
+
return fqn.substring(leaf.name.length + 1); // '+1' => also remove '.'
|
|
201
|
+
throw new CompilerAssertion('to.cdl: Definition to be rendered is not in current name scope!');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get a relative reference to the given definition name in the current name environment.
|
|
206
|
+
*
|
|
207
|
+
* This function must only be used for references _to_ definitions and not
|
|
208
|
+
* for statements that _create_ definitions, i.e _introduce_ a new name.
|
|
209
|
+
*
|
|
210
|
+
* @param {string} fqn
|
|
211
|
+
* @returns {string}
|
|
212
|
+
*/
|
|
213
|
+
definitionReference(fqn) {
|
|
214
|
+
if (isBuiltinType(fqn)) {
|
|
215
|
+
const ref = this.builtinShorthandReference(fqn);
|
|
216
|
+
if (ref !== null)
|
|
217
|
+
return ref;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const name = rootPathSegment(fqn);
|
|
221
|
+
// Go through all scopes except the root one, since in there, paths are always absolute.
|
|
222
|
+
for (let i = this.#scopes.length - 1; i >= 1; i--) {
|
|
223
|
+
const tree = this.#scopes[i];
|
|
224
|
+
|
|
225
|
+
if (tree.name && fqn.startsWith(`${tree.name }.`)) {
|
|
226
|
+
// FQN is in current scope.
|
|
227
|
+
const relativeName = fqn.substring(tree.name.length + 1);
|
|
228
|
+
const relativeRoot = rootPathSegment(relativeName);
|
|
229
|
+
|
|
230
|
+
// Since CDS requires root path segments to be _known within a CDL document_, we
|
|
231
|
+
// need to check if the root path is _known_. If not, we need a USING statement.
|
|
232
|
+
// Example: `namespace ns; entity C : ns.D {};` -> must render alias, as 'D' would
|
|
233
|
+
// be invalid! Required for parseCdl.
|
|
234
|
+
if (!tree.children[relativeRoot])
|
|
235
|
+
return this.#useAliasForPathInScope(fqn, tree);
|
|
236
|
+
|
|
237
|
+
// Name can be used relative to scope 'tree'. We now need to check if the relative
|
|
238
|
+
// name does not collide with more inner scopes by checking for direct children.
|
|
239
|
+
for (let j = this.#scopes.length - 1; j > i; j--) {
|
|
240
|
+
if (this.#scopes[j].children[relativeRoot]) {
|
|
241
|
+
// collision; requires alias
|
|
242
|
+
return this.#useAliasForPathInScope(fqn, tree);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return relativeName;
|
|
247
|
+
}
|
|
248
|
+
else if (name in tree.children) {
|
|
249
|
+
// Name is in current scope, but it is not the artifact we're looking for.
|
|
250
|
+
// Use a global alias to avoid confusing it.
|
|
251
|
+
return this.#useAliasForPathInScope(fqn, null);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// At this point, the path is unknown and outside any non-root scope.
|
|
256
|
+
|
|
257
|
+
if (this.#namespaceAlias && (name !== 'cds' || this.#namespaceAlias === 'cds')) {
|
|
258
|
+
// There is a namespace. We need a USING for all non-builtin paths, but also for
|
|
259
|
+
// builtins if the namespace alias collides. Builtin collision, e.g.
|
|
260
|
+
// `type my.cds.String : cds.String;` with common namespace "my.cds".
|
|
261
|
+
return this.#useAliasForPathInScope(fqn, null);
|
|
262
|
+
}
|
|
263
|
+
if (name !== 'cds' && !this.#scopes[0].availableRootPaths?.[name]) {
|
|
264
|
+
// In case the non-builtin path is unknown, add a using statement. Required for parseCdl.
|
|
265
|
+
// Completely unknown: -> alias
|
|
266
|
+
return this.#useAliasForPathInScope(fqn, null);
|
|
267
|
+
}
|
|
268
|
+
// Builtin or root path is known.
|
|
269
|
+
return fqn;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Adapt the FQN to use a global alias. The alias is created for either
|
|
274
|
+
* the scope in which the FQN resides or the root path segment.
|
|
275
|
+
*
|
|
276
|
+
* @param {string} fqn
|
|
277
|
+
* @param {DefinitionPathTree} [scope]
|
|
278
|
+
* @returns {string}
|
|
279
|
+
*/
|
|
280
|
+
#useAliasForPathInScope( fqn, scope ) {
|
|
281
|
+
const path = scope?.name ? scope.name : rootPathSegment(fqn);
|
|
282
|
+
if (!this.#fqnToAlias[path]?.path)
|
|
283
|
+
this.#addUsingAlias(path);
|
|
284
|
+
|
|
285
|
+
if (this.#fqnToAlias[path].alias === path)
|
|
286
|
+
return fqn; // shortcut to avoid substring()
|
|
287
|
+
|
|
288
|
+
return this.#fqnToAlias[path].alias + fqn.substring(path.length);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Returns a shorthand reference to the builtin type if possible or
|
|
293
|
+
* null otherwise, in which case the caller must ensure that the full type
|
|
294
|
+
* can be used.
|
|
295
|
+
*
|
|
296
|
+
* Example:
|
|
297
|
+
* cds.Integer -> Integer
|
|
298
|
+
* cds.hana.NCHAR -> hana.NCHAR
|
|
299
|
+
*
|
|
300
|
+
* @param {string} type
|
|
301
|
+
* @returns {string|null}
|
|
302
|
+
*/
|
|
303
|
+
builtinShorthandReference(type) {
|
|
304
|
+
const shortHand = type.slice(4); // remove 'cds.'
|
|
305
|
+
const root = rootPathSegment(shortHand);
|
|
306
|
+
if (this.#scopes.at(-1).availableRootPaths[root])
|
|
307
|
+
return null; // there is already an artifact of the same name
|
|
308
|
+
if (this.#namespaceAlias === root)
|
|
309
|
+
return null; // alias collides with shorthand
|
|
310
|
+
return shortHand;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get a list of objects meant to be rendered as USING statements.
|
|
315
|
+
*
|
|
316
|
+
* @returns {UsingAlias[]}
|
|
317
|
+
*/
|
|
318
|
+
getUsings() {
|
|
319
|
+
const result = [];
|
|
320
|
+
for (const alias in this.#aliasToFqn)
|
|
321
|
+
result.push(this.#aliasToFqn[alias]);
|
|
322
|
+
return result;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @see createDefinitionPathTree()
|
|
328
|
+
*/
|
|
329
|
+
class DefinitionPathTree {
|
|
330
|
+
name = null;
|
|
331
|
+
/** @type {Record<string, DefinitionPathTree>} */
|
|
332
|
+
children = Object.create(null);
|
|
333
|
+
definition = null;
|
|
334
|
+
/** @type {Record<string, DefinitionPathTree>} */
|
|
335
|
+
availableRootPaths = null; // used in NameScopeStack
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* @param {string} fqn
|
|
339
|
+
*/
|
|
340
|
+
constructor(fqn) {
|
|
341
|
+
this.name = fqn;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* For a CSN model, constructs a tree of all path segments of all definitions, e.g.
|
|
347
|
+
* definitions `a.b.c.d` and `a.b.e.f` will end up in:
|
|
348
|
+
* ```
|
|
349
|
+
* a
|
|
350
|
+
* └─ b
|
|
351
|
+
* ├─ c
|
|
352
|
+
* │ └─ d (link to definition)
|
|
353
|
+
* └─ e
|
|
354
|
+
* └─ f (link to definition)
|
|
355
|
+
* ```
|
|
30
356
|
*
|
|
31
357
|
* @param {CSN.Model} csn
|
|
32
|
-
* @param {
|
|
33
|
-
* @
|
|
358
|
+
* @param {CdlOptions} options
|
|
359
|
+
* @returns {DefinitionPathTree}
|
|
34
360
|
*/
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
361
|
+
function createDefinitionPathTree( csn, options ) {
|
|
362
|
+
const tree = new DefinitionPathTree('');
|
|
363
|
+
if (!csn.definitions)
|
|
364
|
+
return tree;
|
|
365
|
+
|
|
366
|
+
const useNesting = options.renderCdlDefinitionNesting;
|
|
367
|
+
|
|
368
|
+
for (const defName in csn.definitions) {
|
|
369
|
+
const segments = defName.split('.');
|
|
370
|
+
if (!useNesting) {
|
|
371
|
+
// If we don't want nesting, don't do more work than necessary:
|
|
372
|
+
// only the first path step is relevant
|
|
373
|
+
segments.length = 1;
|
|
374
|
+
}
|
|
375
|
+
let leaf = tree;
|
|
376
|
+
for (let i = 0; i < segments.length; i++) {
|
|
377
|
+
const level = segments[i];
|
|
378
|
+
const fqn = segments.slice(0, i + 1).join('.');
|
|
379
|
+
leaf.children[level] ??= new DefinitionPathTree(fqn);
|
|
380
|
+
leaf = leaf.children[level];
|
|
381
|
+
}
|
|
382
|
+
leaf.definition = csn.definitions[defName];
|
|
42
383
|
}
|
|
384
|
+
return tree;
|
|
385
|
+
}
|
|
43
386
|
|
|
44
|
-
checkCSNVersion(csn, options);
|
|
45
387
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
388
|
+
class CsnToCdl {
|
|
389
|
+
/**
|
|
390
|
+
* @param {CSN.Model} csn
|
|
391
|
+
* @param {CdlOptions} options
|
|
392
|
+
* @param {object} msg
|
|
393
|
+
*/
|
|
394
|
+
constructor(csn, options, msg) {
|
|
395
|
+
this.csn = csn;
|
|
396
|
+
this.options = options;
|
|
397
|
+
this.msg = msg;
|
|
398
|
+
|
|
399
|
+
if (this.options.csnFlavor === 'universal' && isBetaEnabled(this.options, 'enableUniversalCsn')) {
|
|
400
|
+
// Since the expander modifies the CSN, we need to clone it first or
|
|
401
|
+
// toCdl can't guarantee that the input CSN is not modified.
|
|
402
|
+
this.csn = cloneFullCsn(this.csn, this.options);
|
|
403
|
+
enrichUniversalCsn(this.csn, this.options);
|
|
404
|
+
}
|
|
63
405
|
|
|
64
|
-
|
|
65
|
-
cdlResult.model = '';
|
|
406
|
+
checkCSNVersion(this.csn, this.options);
|
|
66
407
|
|
|
67
|
-
|
|
408
|
+
this.exprRenderer = this.createCdlExpressionRenderer();
|
|
409
|
+
this.subelementAnnotates = [];
|
|
410
|
+
}
|
|
68
411
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
412
|
+
render() {
|
|
413
|
+
const cdlResult = Object.create(null);
|
|
414
|
+
cdlResult.model = '';
|
|
72
415
|
|
|
73
|
-
|
|
74
|
-
cdlResult.model += renderVocabularies(csn.vocabularies);
|
|
75
|
-
if (csn.extensions)
|
|
76
|
-
cdlResult.model += renderExtensions(csn.extensions, createEnv());
|
|
416
|
+
const env = createEnv();
|
|
77
417
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
418
|
+
const useNesting = !!this.options.renderCdlDefinitionNesting;
|
|
419
|
+
this.definitionTree = createDefinitionPathTree(this.csn, this.options);
|
|
420
|
+
this.commonNamespace = this.getCommonNamespace();
|
|
421
|
+
|
|
422
|
+
env.nameEnvStack.setRootScope(this.definitionTree, this.csn);
|
|
423
|
+
|
|
424
|
+
const useNamespace = this.commonNamespace !== this.definitionTree;
|
|
425
|
+
if (useNamespace)
|
|
426
|
+
env.nameEnvStack.pushNameEnv(this.commonNamespace);
|
|
427
|
+
|
|
428
|
+
cdlResult.model += useNesting
|
|
429
|
+
? this.renderNestedDefinitions(env)
|
|
430
|
+
: this.renderDefinitions(env);
|
|
431
|
+
// sub-element annotations that can't be written directly.
|
|
432
|
+
cdlResult.model += this.renderExtensions(this.subelementAnnotates, env);
|
|
433
|
+
|
|
434
|
+
if (this.csn.vocabularies)
|
|
435
|
+
cdlResult.model += this.renderVocabularies(this.csn.vocabularies, env);
|
|
436
|
+
if (this.csn.extensions)
|
|
437
|
+
cdlResult.model += this.renderExtensions(this.csn.extensions, env);
|
|
438
|
+
|
|
439
|
+
if (useNamespace)
|
|
440
|
+
env.nameEnvStack.popNameEnv();
|
|
441
|
+
|
|
442
|
+
cdlResult.model = this.renderUsingAliases(env.nameEnvStack.getUsings(), env) + cdlResult.model;
|
|
443
|
+
if (this.csn.requires) {
|
|
444
|
+
let usingsStr = this.csn.requires.map(req => `using from '${req}';`).join('\n');
|
|
445
|
+
usingsStr += '\n\n';
|
|
446
|
+
cdlResult.model = usingsStr + cdlResult.model;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (this.commonNamespace.name)
|
|
450
|
+
cdlResult.model = `namespace ${this.renderArtifactName(this.commonNamespace.name, env)};\n\n${cdlResult.model}`;
|
|
451
|
+
|
|
452
|
+
if (this.csn.namespace) {
|
|
453
|
+
cdlResult.namespace = `namespace ${this.renderArtifactName(this.csn.namespace, createEnv())};\n`;
|
|
454
|
+
cdlResult.namespace += 'using from \'./model.cds\';';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
this.msg.throwWithError();
|
|
458
|
+
return cdlResult;
|
|
81
459
|
}
|
|
82
460
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
461
|
+
/**
|
|
462
|
+
* Determine a common namespace along all definitions.
|
|
463
|
+
* Returns this.definitionTree if there is no common namespace.
|
|
464
|
+
*
|
|
465
|
+
* @returns {DefinitionPathTree}
|
|
466
|
+
*/
|
|
467
|
+
getCommonNamespace() {
|
|
468
|
+
let root = this.definitionTree;
|
|
469
|
+
if (!this.options.renderCdlDefinitionNesting || !this.options.renderCdlCommonNamespace)
|
|
470
|
+
return root; // User does not want common namespace.
|
|
471
|
+
|
|
472
|
+
if (this.csn.vocabularies) {
|
|
473
|
+
// TODO: With vocabularies, we don't search for a common namespace.
|
|
474
|
+
// Reason being that `namespace` statements affect vocabularies, but
|
|
475
|
+
// we don't create definition trees for them.
|
|
476
|
+
return root;
|
|
477
|
+
}
|
|
478
|
+
if (this.csn.extensions?.length > 0) {
|
|
479
|
+
// TODO: Check for the case of `entity Unknown.E {}; annotate Unknown;`
|
|
480
|
+
// by going through all extensions.
|
|
481
|
+
return root;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
while (root) {
|
|
485
|
+
const keys = Object.keys(root.children);
|
|
486
|
+
if (keys.length !== 1 || root.children[keys[0]].definition) {
|
|
487
|
+
// There is either more than one sibling path, or the path is a definition.
|
|
488
|
+
// We MUST NOT create a common namespace for `entity A {}; entity A.A {}`!
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
if (keys[0] === 'cds') {
|
|
492
|
+
// Don't use 'cds' as common namespace _anywhere_, not even in `namespace foo.cds.bar;`
|
|
493
|
+
// While our code _does_ handle such cases, as it also needs to do so for `String`, etc.,
|
|
494
|
+
// it would make reading to.cdl() output worse.
|
|
495
|
+
return this.definitionTree;
|
|
496
|
+
}
|
|
497
|
+
root = root.children[keys[0]];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return root;
|
|
88
501
|
}
|
|
89
502
|
|
|
90
|
-
|
|
91
|
-
|
|
503
|
+
/**
|
|
504
|
+
* @param {UsingAlias[]} aliases
|
|
505
|
+
* @param {CdlRenderEnvironment} env
|
|
506
|
+
* @returns {string}
|
|
507
|
+
*/
|
|
508
|
+
renderUsingAliases(aliases, env) {
|
|
509
|
+
if (!this.options.renderCdlDefinitionNesting) {
|
|
510
|
+
// openAPI importer searches for a single USING statement and replaces it.
|
|
511
|
+
// Let's try to be backward compatible.
|
|
512
|
+
return aliases.length > 0 ? `using { ${aliases.map(entry => (entry.requiresExplicitAlias()
|
|
513
|
+
? `${this.quotePathIfRequired(entry.path, env)} as ${this.quoteNonIdentifierOrKeyword(entry.alias, env)}`
|
|
514
|
+
: entry.path)).join(', ')} };\n\n` : '';
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
let result = '';
|
|
518
|
+
for (const entry of aliases) {
|
|
519
|
+
if (entry.requiresExplicitAlias())
|
|
520
|
+
result += `using { ${this.quotePathIfRequired(entry.path, env)} as ${this.quoteNonIdentifierOrKeyword(entry.alias, env)} };\n`;
|
|
521
|
+
else
|
|
522
|
+
result += `using { ${entry.path } };\n`;
|
|
523
|
+
}
|
|
524
|
+
return result !== '' ? `${ result }\n` : result;
|
|
525
|
+
}
|
|
92
526
|
|
|
93
527
|
/**
|
|
94
|
-
* Render
|
|
95
|
-
* Returns an empty string if nothing is rendered.
|
|
528
|
+
* Render definitions in a flat list, i.e. without nesting.
|
|
96
529
|
*
|
|
97
|
-
* @
|
|
530
|
+
* @param {CdlRenderEnvironment} env
|
|
531
|
+
* @returns {string}
|
|
98
532
|
*/
|
|
99
|
-
|
|
533
|
+
renderDefinitions(env) {
|
|
100
534
|
let result = '';
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const sourceStr = renderDefinition(artifactName, artifact, env);
|
|
535
|
+
forEachDefinition(this.csn, (artifact, artifactName) => {
|
|
536
|
+
const sourceStr = this.renderDefinition(artifactName, artifact, env);
|
|
104
537
|
if (sourceStr !== '')
|
|
105
538
|
result += `${sourceStr}\n`;
|
|
106
539
|
});
|
|
107
540
|
return result;
|
|
108
541
|
}
|
|
109
542
|
|
|
543
|
+
/**
|
|
544
|
+
* Render entries from the `csn.definitions` dictionary.
|
|
545
|
+
* Returns an empty string if nothing is rendered.
|
|
546
|
+
*
|
|
547
|
+
* @return {string}
|
|
548
|
+
*/
|
|
549
|
+
renderNestedDefinitions(env) {
|
|
550
|
+
const that = this;
|
|
551
|
+
let result = '';
|
|
552
|
+
renderTree(this.definitionTree);
|
|
553
|
+
return result;
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* @param {DefinitionPathTree} tree
|
|
557
|
+
*/
|
|
558
|
+
function renderTree( tree ) {
|
|
559
|
+
for (const name in tree.children) {
|
|
560
|
+
const entry = tree.children[name];
|
|
561
|
+
const def = entry.definition;
|
|
562
|
+
|
|
563
|
+
if (def?.kind === 'service' || def?.kind === 'context') {
|
|
564
|
+
// Render service/context with nested definitions.
|
|
565
|
+
env.path = [ 'definitions', entry.name ];
|
|
566
|
+
result += that.renderAnnotationAssignmentsAndDocComment(def, env);
|
|
567
|
+
result += `${env.indent}${def.kind} ${ that.renderArtifactName(entry.name, env) } {\n`;
|
|
568
|
+
env.increaseIndent();
|
|
569
|
+
env.nameEnvStack.pushNameEnv(entry);
|
|
570
|
+
if (entry.children)
|
|
571
|
+
renderTree(entry);
|
|
572
|
+
env.nameEnvStack.popNameEnv();
|
|
573
|
+
env.decreaseIndent();
|
|
574
|
+
if (result.at(-1) === '\n' && result.at(-2) === '\n')
|
|
575
|
+
result = result.substring(0, result.length - 1); // to get the closing brace on the next line after a definition, remove one linebreak
|
|
576
|
+
result += `${env.indent}};\n\n`;
|
|
577
|
+
}
|
|
578
|
+
else if (def) {
|
|
579
|
+
const sourceStr = that.renderDefinition(entry.name, def, env);
|
|
580
|
+
if (sourceStr !== '')
|
|
581
|
+
result += `${sourceStr}\n`;
|
|
582
|
+
if (entry.children)
|
|
583
|
+
renderTree(entry);
|
|
584
|
+
}
|
|
585
|
+
else if (entry.children) {
|
|
586
|
+
renderTree(entry);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
110
592
|
/**
|
|
111
593
|
* Render annotation definitions, i.e. entries from csn.vocabularies.
|
|
112
594
|
* Returns an empty string if there isn't anything to render.
|
|
113
595
|
*
|
|
114
596
|
* @param {object} vocabularies
|
|
597
|
+
* @param {CdlRenderEnvironment} env
|
|
115
598
|
* @return {string}
|
|
116
599
|
*/
|
|
117
|
-
|
|
600
|
+
renderVocabularies( vocabularies, env ) {
|
|
118
601
|
let result = '';
|
|
119
|
-
|
|
602
|
+
for (const key in vocabularies)
|
|
603
|
+
result += this.renderVocabulariesEntry(key, vocabularies[key], env);
|
|
120
604
|
return result;
|
|
605
|
+
}
|
|
121
606
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
607
|
+
/**
|
|
608
|
+
* @param {string} name
|
|
609
|
+
* @param anno
|
|
610
|
+
* @param {CdlRenderEnvironment} env
|
|
611
|
+
* @returns {string}
|
|
612
|
+
*/
|
|
613
|
+
renderVocabulariesEntry( name, anno, env ) {
|
|
614
|
+
if (anno.$ignore)
|
|
615
|
+
return '';
|
|
616
|
+
// This environment is passed down the call hierarchy, for dealing with
|
|
617
|
+
// indentation and name resolution issues
|
|
618
|
+
env.path = [ 'vocabularies', name ];
|
|
619
|
+
const sourceStr = this.renderArtifact(name, anno, env, 'annotation');
|
|
620
|
+
return `${sourceStr}\n`;
|
|
131
621
|
}
|
|
132
622
|
|
|
133
623
|
/**
|
|
@@ -139,10 +629,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
139
629
|
* @param {CdlRenderEnvironment} env
|
|
140
630
|
* @return {string}
|
|
141
631
|
*/
|
|
142
|
-
|
|
632
|
+
renderExtensions( extensions, env ) {
|
|
143
633
|
if (!env.path)
|
|
144
634
|
env = env.cloneWith({ path: [ 'extensions' ] });
|
|
145
|
-
return extensions.map((ext, index) => renderExtension(ext, env.withSubPath([ index ]))).join('\n');
|
|
635
|
+
return extensions.map((ext, index) => this.renderExtension(ext, env.withSubPath([ index ]))).join('\n');
|
|
146
636
|
}
|
|
147
637
|
|
|
148
638
|
/**
|
|
@@ -152,10 +642,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
152
642
|
* @param {CdlRenderEnvironment} env
|
|
153
643
|
* @return {string}
|
|
154
644
|
*/
|
|
155
|
-
|
|
645
|
+
renderExtension( ext, env ) {
|
|
156
646
|
if (ext.extend)
|
|
157
|
-
return renderExtendStatement(ext.extend, ext, env);
|
|
158
|
-
return renderAnnotateStatement(ext, env);
|
|
647
|
+
return this.renderExtendStatement(ext.extend, ext, env);
|
|
648
|
+
return this.renderAnnotateStatement(ext, env);
|
|
159
649
|
}
|
|
160
650
|
|
|
161
651
|
/**
|
|
@@ -168,21 +658,21 @@ function csnToCdl( csn, options, msg ) {
|
|
|
168
658
|
* @param {CdlRenderEnvironment} env
|
|
169
659
|
* @return {string}
|
|
170
660
|
*/
|
|
171
|
-
|
|
661
|
+
renderExtendStatement( extName, ext, env ) {
|
|
172
662
|
// Element extensions have `kind` set. Don't use for enum extension.
|
|
173
663
|
const isElementExtend = (ext.kind === 'extend' && !ext.enum);
|
|
174
|
-
let result = renderAnnotationAssignmentsAndDocComment(ext, env);
|
|
175
|
-
extName =
|
|
664
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(ext, env);
|
|
665
|
+
extName = this.renderArtifactName(extName, env);
|
|
176
666
|
|
|
177
667
|
if (ext.includes && ext.includes.length > 0) {
|
|
178
668
|
// Includes can't be combined with anything in braces {}.
|
|
179
669
|
const affix = isElementExtend ? 'element ' : '';
|
|
180
|
-
const includes = ext.includes.map((inc, i) => renderDefinitionReference(inc, env.withSubPath([ 'includes', i ]))).join(', ');
|
|
670
|
+
const includes = ext.includes.map((inc, i) => this.renderDefinitionReference(inc, env.withSubPath([ 'includes', i ]))).join(', ');
|
|
181
671
|
result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
|
|
182
672
|
return result;
|
|
183
673
|
}
|
|
184
674
|
|
|
185
|
-
const typeParams = renderTypeParameters(ext, true);
|
|
675
|
+
const typeParams = this.renderTypeParameters(ext, true);
|
|
186
676
|
if (typeParams) {
|
|
187
677
|
result += `${env.indent}extend ${extName} with ${typeParams};\n`;
|
|
188
678
|
return result;
|
|
@@ -201,22 +691,22 @@ function csnToCdl( csn, options, msg ) {
|
|
|
201
691
|
// If there are actions, check if there are also elements/columns, and if so, use the prefix notation.
|
|
202
692
|
const usePrefixNotation = ext.actions && (ext.columns || ext.elements);
|
|
203
693
|
if (usePrefixNotation)
|
|
204
|
-
result += `${env.indent}extend ${getExtendPrefixVariant(ext)} ${extName} with {\n`;
|
|
694
|
+
result += `${env.indent}extend ${this.getExtendPrefixVariant(ext)} ${extName} with {\n`;
|
|
205
695
|
else
|
|
206
|
-
result += `${env.indent}extend ${extName} with ${getExtendPostfixVariant(ext)}{\n`;
|
|
696
|
+
result += `${env.indent}extend ${extName} with ${this.getExtendPostfixVariant(ext)}{\n`;
|
|
207
697
|
|
|
208
698
|
if (ext.columns)
|
|
209
|
-
result += renderViewColumns(ext, env.withIncreasedIndent());
|
|
699
|
+
result += this.renderViewColumns(ext, env.withIncreasedIndent());
|
|
210
700
|
|
|
211
701
|
else if (ext.elements || ext.enum)
|
|
212
|
-
result += renderExtendStatementElements(ext, env);
|
|
702
|
+
result += this.renderExtendStatementElements(ext, env);
|
|
213
703
|
|
|
214
704
|
// Not part of if/else cascade, because it may be in postfix notation.
|
|
215
705
|
if (ext.actions) {
|
|
216
706
|
const childEnv = env.withIncreasedIndent();
|
|
217
707
|
let actions = '';
|
|
218
708
|
forEach(ext.actions, (actionName, action) => {
|
|
219
|
-
actions += renderActionOrFunction(actionName, action, childEnv.withSubPath([ 'actions', actionName ]));
|
|
709
|
+
actions += this.renderActionOrFunction(actionName, action, childEnv.withSubPath([ 'actions', actionName ]), true);
|
|
220
710
|
});
|
|
221
711
|
if (!usePrefixNotation)
|
|
222
712
|
result += actions;
|
|
@@ -234,7 +724,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
234
724
|
* @param {object} ext
|
|
235
725
|
* @return {string}
|
|
236
726
|
*/
|
|
237
|
-
|
|
727
|
+
getExtendPrefixVariant( ext ) {
|
|
238
728
|
if (ext.kind === 'extend')
|
|
239
729
|
return 'element'; // element extensions inside an `extend`
|
|
240
730
|
if (ext.columns)
|
|
@@ -250,7 +740,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
250
740
|
* @param {CSN.Extension} ext
|
|
251
741
|
* @return {string}
|
|
252
742
|
*/
|
|
253
|
-
|
|
743
|
+
getExtendPostfixVariant( ext ) {
|
|
254
744
|
if (ext.columns)
|
|
255
745
|
return 'columns ';
|
|
256
746
|
if (ext.actions)
|
|
@@ -274,17 +764,17 @@ function csnToCdl( csn, options, msg ) {
|
|
|
274
764
|
* @param {CdlRenderEnvironment} env
|
|
275
765
|
* @return {string}
|
|
276
766
|
*/
|
|
277
|
-
|
|
767
|
+
renderExtendStatementElements( ext, env ) {
|
|
278
768
|
let result = '';
|
|
279
769
|
const prop = ext.elements ? 'elements' : 'enum';
|
|
280
770
|
forEach(ext[prop] || {}, (elemName, element) => {
|
|
281
771
|
const childEnv = env.withIncreasedIndent().withSubPath([ 'elements', elemName ]);
|
|
282
772
|
if (element.kind === 'extend')
|
|
283
|
-
result += renderExtendStatement(elemName, element, childEnv);
|
|
773
|
+
result += this.renderExtendStatement(elemName, element, childEnv);
|
|
284
774
|
else
|
|
285
775
|
// As soon as we are inside an element, nested `extend` are not possible,
|
|
286
776
|
// since we can't extend an existing element of a new one.
|
|
287
|
-
result += renderElement(elemName, element, childEnv.withSubPath([ prop, elemName ]));
|
|
777
|
+
result += this.renderElement(elemName, element, childEnv.withSubPath([ prop, elemName ]));
|
|
288
778
|
});
|
|
289
779
|
return result;
|
|
290
780
|
}
|
|
@@ -296,7 +786,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
296
786
|
* @param {CdlRenderEnvironment} env
|
|
297
787
|
* @return {string}
|
|
298
788
|
*/
|
|
299
|
-
|
|
789
|
+
renderAnnotateStatement( ext, env ) {
|
|
300
790
|
// Special case: Super annotate has both "returns" and "elements".
|
|
301
791
|
// Render as separate `annotate`s, but keep the order.
|
|
302
792
|
if (ext.elements && ext.returns) {
|
|
@@ -304,27 +794,27 @@ function csnToCdl( csn, options, msg ) {
|
|
|
304
794
|
|
|
305
795
|
// The first of 'elements' or 'returns' gets all other properties as well.
|
|
306
796
|
// The second only gets one property (itself).
|
|
307
|
-
let result = renderAnnotateStatement({ ...ext, [second]: undefined }, env);
|
|
308
|
-
result += renderAnnotateStatement({ annotate: ext.annotate, [second]: ext[second] }, env);
|
|
797
|
+
let result = this.renderAnnotateStatement({ ...ext, [second]: undefined }, env);
|
|
798
|
+
result += this.renderAnnotateStatement({ annotate: ext.annotate, [second]: ext[second] }, env);
|
|
309
799
|
|
|
310
800
|
return result;
|
|
311
801
|
}
|
|
312
802
|
|
|
313
803
|
// Top-level annotations of the artifact
|
|
314
|
-
let result = renderAnnotationAssignmentsAndDocComment(ext, env);
|
|
804
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(ext, env);
|
|
315
805
|
// Note: Not renderDefinitionReference, because we don't care if there
|
|
316
806
|
// are annotations to unknown things. That's allowed!
|
|
317
|
-
result += `${env.indent}annotate ${renderArtifactName(ext.annotate, env)}`;
|
|
807
|
+
result += `${env.indent}annotate ${this.renderArtifactName(ext.annotate, env)}`;
|
|
318
808
|
|
|
319
809
|
if (ext.params)
|
|
320
|
-
result += renderAnnotateParamsInParentheses(ext, env);
|
|
810
|
+
result += this.renderAnnotateParamsInParentheses(ext, env);
|
|
321
811
|
|
|
322
812
|
// Element extensions and annotations (possibly nested)
|
|
323
813
|
if (ext.elements || ext.enum)
|
|
324
|
-
result += ` ${renderAnnotateStatementElements(ext, env)}`;
|
|
814
|
+
result += ` ${this.renderAnnotateStatementElements(ext, env)}`;
|
|
325
815
|
|
|
326
816
|
else if (ext.returns)
|
|
327
|
-
result += renderAnnotateReturns(ext, env);
|
|
817
|
+
result += this.renderAnnotateReturns(ext, env);
|
|
328
818
|
|
|
329
819
|
|
|
330
820
|
if (ext.actions) { // Bound action annotations
|
|
@@ -334,12 +824,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
334
824
|
for (const name in ext.actions) {
|
|
335
825
|
env.path[env.path.length - 1] = name;
|
|
336
826
|
const action = ext.actions[name];
|
|
337
|
-
result += renderAnnotationAssignmentsAndDocComment(action, env) + env.indent + quoteNonIdentifierOrKeyword(name, env);
|
|
827
|
+
result += this.renderAnnotationAssignmentsAndDocComment(action, env) + env.indent + this.quoteNonIdentifierOrKeyword(name, env);
|
|
338
828
|
// Action parameter annotations
|
|
339
829
|
if (action.params)
|
|
340
|
-
result += renderAnnotateParamsInParentheses(action, env);
|
|
830
|
+
result += this.renderAnnotateParamsInParentheses(action, env);
|
|
341
831
|
if (action.returns)
|
|
342
|
-
result += renderAnnotateReturns(action, env);
|
|
832
|
+
result += this.renderAnnotateReturns(action, env);
|
|
343
833
|
|
|
344
834
|
result = removeTrailingNewline(result);
|
|
345
835
|
result += ';\n';
|
|
@@ -362,7 +852,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
362
852
|
* @param {CdlRenderEnvironment} env
|
|
363
853
|
* @return {string}
|
|
364
854
|
*/
|
|
365
|
-
|
|
855
|
+
renderAnnotateStatementElements( ext, env ) {
|
|
366
856
|
const elements = ext.enum ? ext.enum : ext.elements;
|
|
367
857
|
let result = '{\n';
|
|
368
858
|
env.increaseIndent();
|
|
@@ -370,16 +860,16 @@ function csnToCdl( csn, options, msg ) {
|
|
|
370
860
|
for (const name in elements) {
|
|
371
861
|
env.path[env.path.length - 1] = name;
|
|
372
862
|
const elem = elements[name];
|
|
373
|
-
result += renderAnnotationAssignmentsAndDocComment(elem, env);
|
|
374
|
-
result += env.indent + quoteNonIdentifierOrKeyword(name, env);
|
|
863
|
+
result += this.renderAnnotationAssignmentsAndDocComment(elem, env);
|
|
864
|
+
result += env.indent + this.quoteNonIdentifierOrKeyword(name, env);
|
|
375
865
|
if (elem.elements) {
|
|
376
866
|
env.path.push('elements');
|
|
377
|
-
result += ` ${renderAnnotateStatementElements(elem, env)}`;
|
|
867
|
+
result += ` ${this.renderAnnotateStatementElements(elem, env)}`;
|
|
378
868
|
env.path.pop();
|
|
379
869
|
}
|
|
380
870
|
else if (elem.enum) {
|
|
381
871
|
env.path.push('enum');
|
|
382
|
-
result += ` ${renderAnnotateStatementElements(elem, env)}`;
|
|
872
|
+
result += ` ${this.renderAnnotateStatementElements(elem, env)}`;
|
|
383
873
|
env.path.pop();
|
|
384
874
|
}
|
|
385
875
|
|
|
@@ -399,18 +889,18 @@ function csnToCdl( csn, options, msg ) {
|
|
|
399
889
|
* @param {CdlRenderEnvironment} env
|
|
400
890
|
* @return {string}
|
|
401
891
|
*/
|
|
402
|
-
|
|
892
|
+
renderAnnotateReturns( ext, env ) {
|
|
403
893
|
env = env.withSubPath([ 'returns', 'elements' ]);
|
|
404
894
|
let result = ' returns';
|
|
405
895
|
|
|
406
|
-
const returnAnnos = renderAnnotationAssignmentsAndDocComment(ext.returns, env.withIncreasedIndent());
|
|
896
|
+
const returnAnnos = this.renderAnnotationAssignmentsAndDocComment(ext.returns, env.withIncreasedIndent());
|
|
407
897
|
if (returnAnnos)
|
|
408
898
|
result += `\n${returnAnnos}`;
|
|
409
899
|
|
|
410
900
|
if (ext.returns.elements) {
|
|
411
901
|
// Annotations are on separate lines: Have it aligned nicely
|
|
412
902
|
result += returnAnnos ? `${env.indent}` : ' ';
|
|
413
|
-
result += renderAnnotateStatementElements(ext.returns, env);
|
|
903
|
+
result += this.renderAnnotateStatementElements(ext.returns, env);
|
|
414
904
|
}
|
|
415
905
|
return result;
|
|
416
906
|
}
|
|
@@ -422,13 +912,13 @@ function csnToCdl( csn, options, msg ) {
|
|
|
422
912
|
* @param {CdlRenderEnvironment} env
|
|
423
913
|
* @return {string}
|
|
424
914
|
*/
|
|
425
|
-
|
|
915
|
+
renderAnnotateParamsInParentheses( ext, env ) {
|
|
426
916
|
const childEnv = env.withIncreasedIndent();
|
|
427
917
|
let result = '(\n';
|
|
428
918
|
const paramAnnotations = [];
|
|
429
919
|
forEach(ext.params, (paramName, param) => {
|
|
430
|
-
const annos = renderAnnotationAssignmentsAndDocComment(param, childEnv);
|
|
431
|
-
const name = quoteNonIdentifierOrKeyword(paramName, childEnv);
|
|
920
|
+
const annos = this.renderAnnotationAssignmentsAndDocComment(param, childEnv);
|
|
921
|
+
const name = this.quoteNonIdentifierOrKeyword(paramName, childEnv);
|
|
432
922
|
// Not supported, yet (#13052)
|
|
433
923
|
// const sub = (param.elements || param.enum) ? ` ${renderAnnotateStatementElements(param, childEnv)}` : '';
|
|
434
924
|
paramAnnotations.push( annos + childEnv.indent + name);
|
|
@@ -444,32 +934,32 @@ function csnToCdl( csn, options, msg ) {
|
|
|
444
934
|
* @param {CSN.Artifact} art
|
|
445
935
|
* @param {CdlRenderEnvironment} env
|
|
446
936
|
*/
|
|
447
|
-
|
|
937
|
+
renderDefinition( artifactName, art, env ) {
|
|
448
938
|
env = env.cloneWith({ path: [ 'definitions', artifactName ] });
|
|
449
939
|
|
|
450
940
|
const kind = art.kind || 'type'; // the default kind is "type".
|
|
451
941
|
switch (kind) {
|
|
452
942
|
case 'entity':
|
|
453
943
|
if (art.query || art.projection)
|
|
454
|
-
return renderView(artifactName, art, env);
|
|
455
|
-
return renderArtifact(artifactName, art, env);
|
|
944
|
+
return this.renderView(artifactName, art, env);
|
|
945
|
+
return this.renderArtifact(artifactName, art, env);
|
|
456
946
|
case 'aspect':
|
|
457
|
-
return renderAspect(artifactName, art, env);
|
|
947
|
+
return this.renderAspect(artifactName, art, env);
|
|
458
948
|
|
|
459
949
|
case 'context':
|
|
460
950
|
case 'service':
|
|
461
|
-
return renderContextOrService(artifactName, art, env);
|
|
951
|
+
return this.renderContextOrService(artifactName, art, env);
|
|
462
952
|
|
|
463
953
|
case 'annotation': // annotation in 'csn.definitions' for compiler v1 compatibility
|
|
464
|
-
return renderArtifact(artifactName, art, env, 'annotation');
|
|
954
|
+
return this.renderArtifact(artifactName, art, env, 'annotation');
|
|
465
955
|
|
|
466
956
|
case 'action':
|
|
467
957
|
case 'function':
|
|
468
|
-
return renderActionOrFunction(artifactName, art, env);
|
|
958
|
+
return this.renderActionOrFunction(artifactName, art, env, false);
|
|
469
959
|
|
|
470
960
|
case 'type':
|
|
471
961
|
case 'event':
|
|
472
|
-
return renderArtifact(artifactName, art, env);
|
|
962
|
+
return this.renderArtifact(artifactName, art, env);
|
|
473
963
|
|
|
474
964
|
default:
|
|
475
965
|
throw new ModelError(`to.cdl: Unknown artifact kind: '${art.kind}' at ${JSON.stringify(env.path)}`);
|
|
@@ -477,48 +967,49 @@ function csnToCdl( csn, options, msg ) {
|
|
|
477
967
|
}
|
|
478
968
|
|
|
479
969
|
/**
|
|
480
|
-
* TODO: Also use this function for other kinds such as entities, aspects and views.
|
|
481
|
-
*
|
|
482
970
|
* @param {string} artifactName
|
|
483
971
|
* @param {CSN.Artifact} art
|
|
484
972
|
* @param {CdlRenderEnvironment} env
|
|
485
973
|
* @param {string} [overrideKind] If set, override the artifact kind.
|
|
486
974
|
*/
|
|
487
|
-
|
|
488
|
-
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
975
|
+
renderArtifact( artifactName, art, env, overrideKind ) {
|
|
976
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
|
|
489
977
|
let kind = overrideKind || art.$syntax === 'aspect' && 'aspect' || art.kind;
|
|
490
978
|
if (art.abstract)
|
|
491
979
|
kind = `abstract ${ kind }`;
|
|
492
|
-
|
|
980
|
+
// Vocabularies are in a separate name environment. We can't shorten them.
|
|
981
|
+
const normalizedArtifactName = kind !== 'annotation'
|
|
982
|
+
? this.renderArtifactName(artifactName, env)
|
|
983
|
+
: this.quotePathIfRequired(artifactName, env);
|
|
493
984
|
result += `${env.indent}${kind} ${normalizedArtifactName}`;
|
|
494
985
|
|
|
495
986
|
if (art.params)
|
|
496
|
-
result += renderParameters(art, env);
|
|
987
|
+
result += this.renderParameters(art, env);
|
|
497
988
|
|
|
498
989
|
let isDirectStruct = false;
|
|
499
990
|
const isQuery = art.query || art.projection;
|
|
500
991
|
if (isQuery) {
|
|
501
992
|
result += ' : ';
|
|
502
993
|
// types/events (should) only support "projections"
|
|
503
|
-
result += renderQuery(getNormalizedQuery(art).query, true, 'projection',
|
|
504
|
-
|
|
994
|
+
result += this.renderQuery(getNormalizedQuery(art).query, true, 'projection',
|
|
995
|
+
env.withSubPath([ art.projection ? 'projection' : 'query' ]));
|
|
505
996
|
}
|
|
506
997
|
else {
|
|
507
|
-
const type = renderTypeReferenceAndProps(art, env);
|
|
998
|
+
const type = this.renderTypeReferenceAndProps(art, env);
|
|
508
999
|
if (type) {
|
|
509
1000
|
isDirectStruct = type.startsWith('{');
|
|
510
1001
|
|
|
511
1002
|
if (art.includes?.length && isDirectStruct) {
|
|
512
1003
|
// We can only render includes, if the type is directly structured. Otherwise, we would
|
|
513
1004
|
// render e.g. `type T : Include : T2;`, which is invalid. We use `extend` in such cases.
|
|
514
|
-
result += renderIncludes(art.includes, env);
|
|
1005
|
+
result += this.renderIncludes(art.includes, env);
|
|
515
1006
|
}
|
|
516
1007
|
|
|
517
1008
|
// For nicer output, no colon if unnamed structure is used.
|
|
518
1009
|
result += (!art.type && art.elements) ? ` ${type}` : ` : ${type}`;
|
|
519
1010
|
}
|
|
520
1011
|
else {
|
|
521
|
-
msg.warning('syntax-missing-type', env.path, { '#': art.kind, name: artifactName }, {
|
|
1012
|
+
this.msg.warning('syntax-missing-type', env.path, { '#': art.kind, name: artifactName }, {
|
|
522
1013
|
std: 'Missing type for definition $(NAME); can\'t be represented in CDL',
|
|
523
1014
|
entity: 'Missing elements for entity $(NAME); can\'t be represented in CDL',
|
|
524
1015
|
});
|
|
@@ -530,7 +1021,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
530
1021
|
// If there are no elements nor query, but actions, CDL syntax requires braces.
|
|
531
1022
|
result += ' { }';
|
|
532
1023
|
}
|
|
533
|
-
result +=
|
|
1024
|
+
result += this.renderBoundActionsAndFunctions(art, env);
|
|
534
1025
|
}
|
|
535
1026
|
|
|
536
1027
|
result += ';\n';
|
|
@@ -538,7 +1029,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
538
1029
|
if (art.includes?.length && !isDirectStruct) {
|
|
539
1030
|
// If we're not a directly structured type, render the `includes` as `extend`
|
|
540
1031
|
// statements directly below the type definition.
|
|
541
|
-
result += renderExtendStatement(artifactName, { includes: art.includes }, env);
|
|
1032
|
+
result += this.renderExtendStatement(artifactName, { includes: art.includes }, env);
|
|
542
1033
|
}
|
|
543
1034
|
|
|
544
1035
|
return result;
|
|
@@ -550,9 +1041,9 @@ function csnToCdl( csn, options, msg ) {
|
|
|
550
1041
|
* @param {CdlRenderEnvironment} env
|
|
551
1042
|
* @returns {string}
|
|
552
1043
|
*/
|
|
553
|
-
|
|
554
|
-
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
555
|
-
result += `${env.indent}${art.kind} ${renderArtifactName(artifactName, env)}`;
|
|
1044
|
+
renderContextOrService( artifactName, art, env ) {
|
|
1045
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
|
|
1046
|
+
result += `${env.indent}${art.kind} ${this.renderArtifactName(artifactName, env)}`;
|
|
556
1047
|
return `${result} {};\n`;
|
|
557
1048
|
}
|
|
558
1049
|
|
|
@@ -566,19 +1057,19 @@ function csnToCdl( csn, options, msg ) {
|
|
|
566
1057
|
* @param {CdlRenderEnvironment} env
|
|
567
1058
|
* @return {string}
|
|
568
1059
|
*/
|
|
569
|
-
|
|
570
|
-
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
571
|
-
result += `${env.indent}aspect ${renderArtifactName(artifactName, env)}`;
|
|
1060
|
+
renderAspect( artifactName, art, env ) {
|
|
1061
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
|
|
1062
|
+
result += `${env.indent}aspect ${this.renderArtifactName(artifactName, env)}`;
|
|
572
1063
|
if (art.includes)
|
|
573
|
-
result += renderIncludes(art.includes, env);
|
|
1064
|
+
result += this.renderIncludes(art.includes, env);
|
|
574
1065
|
|
|
575
1066
|
if (art.elements)
|
|
576
|
-
result += ` ${renderElements(art, env)}`;
|
|
1067
|
+
result += ` ${this.renderElements(art, env)}`;
|
|
577
1068
|
else if (art.actions)
|
|
578
1069
|
// if there are no elements, but actions, CDL syntax requires braces.
|
|
579
1070
|
result += ' { }';
|
|
580
1071
|
|
|
581
|
-
result += `${
|
|
1072
|
+
result += `${this.renderBoundActionsAndFunctions(art, env)};\n`;
|
|
582
1073
|
return result;
|
|
583
1074
|
}
|
|
584
1075
|
|
|
@@ -589,11 +1080,11 @@ function csnToCdl( csn, options, msg ) {
|
|
|
589
1080
|
* @param {CdlRenderEnvironment} env
|
|
590
1081
|
* @return {string}
|
|
591
1082
|
*/
|
|
592
|
-
|
|
1083
|
+
renderElements( artifact, env ) {
|
|
593
1084
|
let elements = '';
|
|
594
1085
|
const childEnv = env.withIncreasedIndent();
|
|
595
1086
|
for (const name in artifact.elements)
|
|
596
|
-
elements += renderElement(name, artifact.elements[name], childEnv.withSubPath([ 'elements', name ]));
|
|
1087
|
+
elements += this.renderElement(name, artifact.elements[name], childEnv.withSubPath([ 'elements', name ]));
|
|
597
1088
|
|
|
598
1089
|
return (elements === '') ? '{ }' : `{\n${elements}${env.indent}}`;
|
|
599
1090
|
}
|
|
@@ -607,19 +1098,19 @@ function csnToCdl( csn, options, msg ) {
|
|
|
607
1098
|
* @param {CSN.Element} element
|
|
608
1099
|
* @param {CdlRenderEnvironment} env
|
|
609
1100
|
*/
|
|
610
|
-
|
|
1101
|
+
renderElement( elementName, element, env ) {
|
|
611
1102
|
const isCalcElement = (element.value !== undefined);
|
|
612
|
-
let result = renderAnnotationAssignmentsAndDocComment(element, env);
|
|
1103
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(element, env);
|
|
613
1104
|
result += env.indent;
|
|
614
1105
|
result += element.virtual ? 'virtual ' : '';
|
|
615
1106
|
result += element.key ? 'key ' : '';
|
|
616
1107
|
result += element.masked ? 'masked ' : '';
|
|
617
|
-
result += quoteNonIdentifierOrKeyword(elementName, env);
|
|
1108
|
+
result += this.quoteNonIdentifierOrKeyword(elementName, env);
|
|
618
1109
|
if (element['#'] !== undefined) { // enum symbol reference
|
|
619
1110
|
result += ` = #${element['#']}`;
|
|
620
1111
|
}
|
|
621
1112
|
else if (element.val !== undefined) { // enum value
|
|
622
|
-
result += ` = ${exprRenderer.renderExpr(element, env)}`;
|
|
1113
|
+
result += ` = ${this.exprRenderer.renderExpr(element, env)}`;
|
|
623
1114
|
}
|
|
624
1115
|
else if (!isCalcElement || !isDirectAssocOrComp(element.type) && !element.$filtered && !element.$enclosed) {
|
|
625
1116
|
// If the element is a calculated element _and_ a direct association or
|
|
@@ -627,7 +1118,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
627
1118
|
// would alter the ON-condition.
|
|
628
1119
|
// If it is a calculated element _and_ an indirect association (via type chain),
|
|
629
1120
|
// we'd get a cast to an association.
|
|
630
|
-
const props = renderTypeReferenceAndProps(element, env);
|
|
1121
|
+
const props = this.renderTypeReferenceAndProps(element, env);
|
|
631
1122
|
if (props !== '')
|
|
632
1123
|
result += ` : ${props}`;
|
|
633
1124
|
}
|
|
@@ -637,8 +1128,8 @@ function csnToCdl( csn, options, msg ) {
|
|
|
637
1128
|
env.path.push('value');
|
|
638
1129
|
const isSubExpr = (element.value.xpr && xprContainsCondition(element.value.xpr));
|
|
639
1130
|
result += isSubExpr
|
|
640
|
-
? exprRenderer.renderSubExpr(element.value, env)
|
|
641
|
-
: exprRenderer.renderExpr(element.value, env);
|
|
1131
|
+
? this.exprRenderer.renderSubExpr(element.value, env)
|
|
1132
|
+
: this.exprRenderer.renderExpr(element.value, env);
|
|
642
1133
|
if (element.value.stored === true)
|
|
643
1134
|
result += ' stored';
|
|
644
1135
|
env.path.length -= 1;
|
|
@@ -661,10 +1152,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
661
1152
|
* @param {CdlRenderEnvironment} env
|
|
662
1153
|
* @return {string}
|
|
663
1154
|
*/
|
|
664
|
-
|
|
665
|
-
const annotate = collectAnnotationsOfElementsAndEnum(art, env);
|
|
1155
|
+
renderQueryElementAndEnumAnnotations( art, env ) {
|
|
1156
|
+
const annotate = this.collectAnnotationsOfElementsAndEnum(art, env);
|
|
666
1157
|
if (annotate)
|
|
667
|
-
return renderExtensions([ annotate ], env);
|
|
1158
|
+
return this.renderExtensions([ annotate ], env);
|
|
668
1159
|
return '';
|
|
669
1160
|
}
|
|
670
1161
|
|
|
@@ -676,7 +1167,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
676
1167
|
* @param {CdlRenderEnvironment} env
|
|
677
1168
|
* @return {CSN.Extension|null}
|
|
678
1169
|
*/
|
|
679
|
-
|
|
1170
|
+
collectAnnotationsOfElementsAndEnum( artifact, env ) {
|
|
680
1171
|
// Array, which may be annotated as well.
|
|
681
1172
|
if (artifact.items) {
|
|
682
1173
|
env = env.withSubPath([ 'items' ]);
|
|
@@ -763,13 +1254,13 @@ function csnToCdl( csn, options, msg ) {
|
|
|
763
1254
|
* @param {CdlRenderEnvironment} env
|
|
764
1255
|
* @return {string}
|
|
765
1256
|
*/
|
|
766
|
-
|
|
1257
|
+
renderViewSource( source, env ) {
|
|
767
1258
|
// Sub-SELECT
|
|
768
1259
|
if (source.SELECT || source.SET) {
|
|
769
1260
|
const subEnv = env.withIncreasedIndent();
|
|
770
|
-
let result = `(\n${subEnv.indent}${renderQuery(source, false, 'view', subEnv)}\n${env.indent})`;
|
|
1261
|
+
let result = `(\n${subEnv.indent}${this.renderQuery(source, false, 'view', subEnv)}\n${env.indent})`;
|
|
771
1262
|
if (source.as)
|
|
772
|
-
result += renderAlias(source.as, env);
|
|
1263
|
+
result += this.renderAlias(source.as, env);
|
|
773
1264
|
|
|
774
1265
|
return result;
|
|
775
1266
|
}
|
|
@@ -777,17 +1268,17 @@ function csnToCdl( csn, options, msg ) {
|
|
|
777
1268
|
else if (source.join) {
|
|
778
1269
|
// One join operation, possibly with ON-condition
|
|
779
1270
|
env.path.push('args', 0);
|
|
780
|
-
let result = `(${renderViewSource(source.args[0], env)}`;
|
|
1271
|
+
let result = `(${this.renderViewSource(source.args[0], env)}`;
|
|
781
1272
|
for (let i = 1; i < source.args.length; i++) {
|
|
782
1273
|
env.path[env.path.length - 1] = i;
|
|
783
1274
|
result += ` ${source.join} `;
|
|
784
|
-
result += renderJoinCardinality(source.cardinality);
|
|
785
|
-
result += `join ${renderViewSource(source.args[i], env)}`;
|
|
1275
|
+
result += this.renderJoinCardinality(source.cardinality);
|
|
1276
|
+
result += `join ${this.renderViewSource(source.args[i], env)}`;
|
|
786
1277
|
}
|
|
787
1278
|
env.path.length -= 2;
|
|
788
1279
|
if (source.on) {
|
|
789
1280
|
env.path.push('on');
|
|
790
|
-
result += ` on ${exprRenderer.renderExpr(source.on, env.withSubPath([ 'on' ]))}`;
|
|
1281
|
+
result += ` on ${this.exprRenderer.renderExpr(source.on, env.withSubPath([ 'on' ]))}`;
|
|
791
1282
|
env.path.length -= 1;
|
|
792
1283
|
}
|
|
793
1284
|
result += ')';
|
|
@@ -795,10 +1286,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
795
1286
|
}
|
|
796
1287
|
// Ordinary path, possibly with an alias
|
|
797
1288
|
|
|
798
|
-
return renderAbsolutePathWithAlias(source, env);
|
|
1289
|
+
return this.renderAbsolutePathWithAlias(source, env);
|
|
799
1290
|
}
|
|
800
1291
|
|
|
801
|
-
|
|
1292
|
+
renderJoinCardinality( card ) {
|
|
802
1293
|
let result = '';
|
|
803
1294
|
if (card) {
|
|
804
1295
|
if (card.srcmin && card.srcmin === 1)
|
|
@@ -822,7 +1313,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
822
1313
|
* @param {CdlRenderEnvironment} env
|
|
823
1314
|
* @return {string}
|
|
824
1315
|
*/
|
|
825
|
-
|
|
1316
|
+
renderAbsolutePath( path, env ) {
|
|
826
1317
|
// Sanity checks
|
|
827
1318
|
if (!path.ref)
|
|
828
1319
|
throw new ModelError(`Expecting ref in path: ${JSON.stringify(path)}`);
|
|
@@ -831,19 +1322,19 @@ function csnToCdl( csn, options, msg ) {
|
|
|
831
1322
|
const firstArtifactName = path.ref[0].id || path.ref[0];
|
|
832
1323
|
|
|
833
1324
|
// Render the first path step (absolute name, with different quoting/naming ..)
|
|
834
|
-
let result = renderDefinitionReference(firstArtifactName, env);
|
|
1325
|
+
let result = this.renderDefinitionReference(firstArtifactName, env);
|
|
835
1326
|
|
|
836
1327
|
// Even the first step might have parameters and/or a filter
|
|
837
1328
|
env.path.push('ref', 0);
|
|
838
1329
|
if (path.ref[0].args)
|
|
839
|
-
result += `(${renderArguments(path.ref[0], ':', env)})`;
|
|
1330
|
+
result += `(${this.renderArguments(path.ref[0], ':', env)})`;
|
|
840
1331
|
if (path.ref[0].where)
|
|
841
|
-
result += renderFilterAndCardinality(path.ref[0], env);
|
|
1332
|
+
result += this.renderFilterAndCardinality(path.ref[0], env);
|
|
842
1333
|
env.path.length -= 2;
|
|
843
1334
|
|
|
844
1335
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
845
1336
|
if (path.ref.length > 1)
|
|
846
|
-
result += `:${exprRenderer.renderExpr({ ref: path.ref.slice(1) }, env)}`;
|
|
1337
|
+
result += `:${this.exprRenderer.renderExpr({ ref: path.ref.slice(1) }, env)}`;
|
|
847
1338
|
|
|
848
1339
|
return result;
|
|
849
1340
|
}
|
|
@@ -859,11 +1350,24 @@ function csnToCdl( csn, options, msg ) {
|
|
|
859
1350
|
* @param {CdlRenderEnvironment} env
|
|
860
1351
|
* @return {string}
|
|
861
1352
|
*/
|
|
862
|
-
|
|
863
|
-
|
|
1353
|
+
renderAbsolutePathWithAlias( path, env ) {
|
|
1354
|
+
// We may have changed the implicit alias due to renderAbsolutePath() and renderDefinitionReference()
|
|
1355
|
+
// introducing USING statements. We need to ensure that the implicit alias stays the same.
|
|
1356
|
+
const isElementRef = path.ref.length > 1;
|
|
1357
|
+
const alias = path.as || implicitAs(path.ref);
|
|
1358
|
+
|
|
1359
|
+
let result = this.renderAbsolutePath(path, env);
|
|
864
1360
|
if (path.as) {
|
|
865
1361
|
// Source had an alias - render it
|
|
866
|
-
result += renderAlias(path.as, env);
|
|
1362
|
+
result += this.renderAlias(path.as, env);
|
|
1363
|
+
}
|
|
1364
|
+
else if (!isElementRef) {
|
|
1365
|
+
const defName = path.ref[0].id || path.ref[0];
|
|
1366
|
+
const sourcePath = env.nameEnvStack.definitionReference(defName);
|
|
1367
|
+
// Source did not have an alias, but we add one as we'd
|
|
1368
|
+
// otherwise have a different implicit alias.
|
|
1369
|
+
if (sourcePath.split('.').at(-1) !== alias)
|
|
1370
|
+
result += this.renderAlias(alias, env);
|
|
867
1371
|
}
|
|
868
1372
|
return result;
|
|
869
1373
|
}
|
|
@@ -871,16 +1375,16 @@ function csnToCdl( csn, options, msg ) {
|
|
|
871
1375
|
/**
|
|
872
1376
|
* Render the given columns.
|
|
873
1377
|
*
|
|
874
|
-
* @param {CSN.
|
|
1378
|
+
* @param {CSN.Extension | CSN.QuerySelect} art
|
|
875
1379
|
* @param {object} elements
|
|
876
1380
|
* @param {CdlRenderEnvironment} env
|
|
877
1381
|
* @return {string}
|
|
878
1382
|
*/
|
|
879
|
-
|
|
1383
|
+
renderViewColumns( art, env, elements = Object.create(null) ) {
|
|
880
1384
|
env.path.push( 'columns', -1 );
|
|
881
1385
|
const result = art.columns.map((col, i) => {
|
|
882
1386
|
env.path[env.path.length - 1] = i;
|
|
883
|
-
return renderViewColumn(col, env, findElement(elements, col));
|
|
1387
|
+
return this.renderViewColumn(col, env, findElement(elements, col));
|
|
884
1388
|
}).join(',\n');
|
|
885
1389
|
env.path.length -= 2;
|
|
886
1390
|
return `${result}\n`;
|
|
@@ -895,15 +1399,15 @@ function csnToCdl( csn, options, msg ) {
|
|
|
895
1399
|
* @param {CdlRenderEnvironment} env
|
|
896
1400
|
* @param {CSN.Element} element Element corresponding to the column. Generated by the compiler.
|
|
897
1401
|
*/
|
|
898
|
-
|
|
1402
|
+
renderViewColumn( col, env, element ) {
|
|
899
1403
|
// Annotations and column
|
|
900
1404
|
let result = '';
|
|
901
1405
|
if (!col.doc) {
|
|
902
1406
|
// TODO: In contrast to annotations, we do not render the doc comment as part
|
|
903
1407
|
// of an `annotate` statement. That may change in the future.
|
|
904
|
-
result += renderDocComment(element, env);
|
|
1408
|
+
result += this.renderDocComment(element, env);
|
|
905
1409
|
}
|
|
906
|
-
result += renderAnnotationAssignmentsAndDocComment(col, env);
|
|
1410
|
+
result += this.renderAnnotationAssignmentsAndDocComment(col, env);
|
|
907
1411
|
result += env.indent;
|
|
908
1412
|
|
|
909
1413
|
// only if column is virtual, keyword virtual was present in the source text
|
|
@@ -912,26 +1416,26 @@ function csnToCdl( csn, options, msg ) {
|
|
|
912
1416
|
|
|
913
1417
|
// Use special rendering for .expand/.inline - renderExpr cannot easily handle some cases
|
|
914
1418
|
if (col.expand || col.inline)
|
|
915
|
-
result += renderInlineExpand(col, env);
|
|
1419
|
+
result += this.renderInlineExpand(col, env);
|
|
916
1420
|
else if (col.xpr && xprContainsCondition(col.xpr))
|
|
917
|
-
result += exprRenderer.renderSubExpr(withoutCast(col), env);
|
|
1421
|
+
result += this.exprRenderer.renderSubExpr(withoutCast(col), env);
|
|
918
1422
|
else
|
|
919
|
-
result += exprRenderer.renderExpr(withoutCast(col), env);
|
|
1423
|
+
result += this.exprRenderer.renderExpr(withoutCast(col), env);
|
|
920
1424
|
|
|
921
1425
|
// Alias for inline/expand is already handled by renderInlineExpand
|
|
922
1426
|
// A new association (cast with `type` and `target`) uses `as` as its primary name, not alias.
|
|
923
1427
|
const isNewAssociation = col.cast?.type && col.cast.target;
|
|
924
1428
|
if (!isNewAssociation && col.as && !col.inline && !col.expand)
|
|
925
|
-
result += renderAlias(col.as, env);
|
|
1429
|
+
result += this.renderAlias(col.as, env);
|
|
926
1430
|
|
|
927
1431
|
// Explicit type provided for the view element?
|
|
928
1432
|
if (col.cast) {
|
|
929
1433
|
env.path.push('cast');
|
|
930
1434
|
// Special case: Explicit association type is actually a redirect
|
|
931
1435
|
if (col.cast.target && !col.cast.type)
|
|
932
|
-
result += ` : ${renderRedirectedTo(col.cast, env)}`;
|
|
1436
|
+
result += ` : ${this.renderRedirectedTo(col.cast, env)}`;
|
|
933
1437
|
else
|
|
934
|
-
result += ` : ${renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true })}`;
|
|
1438
|
+
result += ` : ${this.renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true })}`;
|
|
935
1439
|
env.path.length -= 1;
|
|
936
1440
|
}
|
|
937
1441
|
return result;
|
|
@@ -945,23 +1449,23 @@ function csnToCdl( csn, options, msg ) {
|
|
|
945
1449
|
* @param {CdlRenderEnvironment} env
|
|
946
1450
|
* @returns {string}
|
|
947
1451
|
*/
|
|
948
|
-
|
|
1452
|
+
renderInlineExpand( obj, env ) {
|
|
949
1453
|
// No expression to render for { * } as alias
|
|
950
|
-
let result = (obj.as && obj.expand && !obj.ref) ? '' : exprRenderer.renderExpr(withoutCast(obj), env);
|
|
1454
|
+
let result = (obj.as && obj.expand && !obj.ref) ? '' : this.exprRenderer.renderExpr(withoutCast(obj), env);
|
|
951
1455
|
|
|
952
1456
|
const isAnonymousExpand = (obj.expand && !obj.ref);
|
|
953
1457
|
|
|
954
1458
|
// s as alias { * }
|
|
955
1459
|
if (obj.as && !isAnonymousExpand)
|
|
956
|
-
result += renderAlias(obj.as, env);
|
|
1460
|
+
result += this.renderAlias(obj.as, env);
|
|
957
1461
|
|
|
958
1462
|
// We found a leaf - no further drilling
|
|
959
1463
|
if (!obj.inline && !obj.expand) {
|
|
960
1464
|
env.path.push('cast');
|
|
961
1465
|
if (obj.cast && obj.cast.type)
|
|
962
|
-
result += ` : ${renderTypeReferenceAndProps(obj.cast, env, { noAnnoCollect: true })}`;
|
|
1466
|
+
result += ` : ${this.renderTypeReferenceAndProps(obj.cast, env, { noAnnoCollect: true })}`;
|
|
963
1467
|
else if (obj.cast && obj.cast.target) // test tbd
|
|
964
|
-
result += ` : ${renderRedirectedTo(obj.cast, env)}`;
|
|
1468
|
+
result += ` : ${this.renderRedirectedTo(obj.cast, env)}`;
|
|
965
1469
|
env.path.length -= 1;
|
|
966
1470
|
return result;
|
|
967
1471
|
}
|
|
@@ -975,7 +1479,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
975
1479
|
const childEnv = env.withIncreasedIndent();
|
|
976
1480
|
const expandInline = obj.expand || obj.inline;
|
|
977
1481
|
result += expandInline //
|
|
978
|
-
.map(elm => renderAnnotationAssignmentsAndDocComment(elm, childEnv) + childEnv.indent + renderInlineExpand(elm, childEnv))
|
|
1482
|
+
.map(elm => this.renderAnnotationAssignmentsAndDocComment(elm, childEnv) + childEnv.indent + this.renderInlineExpand(elm, childEnv))
|
|
979
1483
|
.join(',\n');
|
|
980
1484
|
result += `\n${env.indent}}`;
|
|
981
1485
|
|
|
@@ -985,7 +1489,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
985
1489
|
|
|
986
1490
|
// { * } as expand
|
|
987
1491
|
if (obj.as && isAnonymousExpand)
|
|
988
|
-
result += renderAlias(obj.as, env);
|
|
1492
|
+
result += this.renderAlias(obj.as, env);
|
|
989
1493
|
|
|
990
1494
|
return result;
|
|
991
1495
|
}
|
|
@@ -997,7 +1501,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
997
1501
|
* @param {CdlRenderEnvironment} env
|
|
998
1502
|
* @returns {String}
|
|
999
1503
|
*/
|
|
1000
|
-
|
|
1504
|
+
renderDocComment( obj, env ) {
|
|
1001
1505
|
if (!obj || obj && obj.doc === undefined)
|
|
1002
1506
|
return '';
|
|
1003
1507
|
else if (obj && obj.doc === null) // empty doc comment needs to be rendered
|
|
@@ -1012,7 +1516,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1012
1516
|
if (!obj.doc.includes('\n') && !/(^\s)|(\s$)/.test(obj.doc))
|
|
1013
1517
|
return `${env.indent}/** ${doc} */\n`;
|
|
1014
1518
|
|
|
1015
|
-
const comment = doc.split('\n').map(
|
|
1519
|
+
const comment = doc.split('\n').map(l => `${env.indent} * ${l}`).join('\n');
|
|
1016
1520
|
return `${env.indent}/**\n${comment}\n${env.indent} */\n`;
|
|
1017
1521
|
}
|
|
1018
1522
|
|
|
@@ -1021,20 +1525,20 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1021
1525
|
* @param {CSN.Artifact} art
|
|
1022
1526
|
* @param {CdlRenderEnvironment} env
|
|
1023
1527
|
*/
|
|
1024
|
-
|
|
1528
|
+
renderView( artifactName, art, env ) {
|
|
1025
1529
|
const syntax = (art.projection) ? 'projection' : 'entity';
|
|
1026
|
-
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
1027
|
-
result += `${env.indent}entity ${renderArtifactName(artifactName, env)}`;
|
|
1530
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(art, env);
|
|
1531
|
+
result += `${env.indent}entity ${this.renderArtifactName(artifactName, env)}`;
|
|
1028
1532
|
if (art.params)
|
|
1029
|
-
result += renderParameters(art, env);
|
|
1533
|
+
result += this.renderParameters(art, env);
|
|
1030
1534
|
result += ' as ';
|
|
1031
|
-
result += renderQuery(getNormalizedQuery(art).query, true, syntax, env.withSubPath([ art.projection ? 'projection' : 'query' ]), art.elements);
|
|
1535
|
+
result += this.renderQuery(getNormalizedQuery(art).query, true, syntax, env.withSubPath([ art.projection ? 'projection' : 'query' ]), art.elements);
|
|
1032
1536
|
if (art.actions) // Views/Projections also allow actions. Just the VIEW keyword variant did not.
|
|
1033
|
-
result +=
|
|
1537
|
+
result += this.renderBoundActionsAndFunctions(art, env);
|
|
1034
1538
|
result += ';\n';
|
|
1035
|
-
result += renderQueryElementAndEnumAnnotations(art, env);
|
|
1539
|
+
result += this.renderQueryElementAndEnumAnnotations(art, env);
|
|
1036
1540
|
if (art.includes)
|
|
1037
|
-
result += renderExtension({ extend: artifactName, includes: art.includes }, env);
|
|
1541
|
+
result += this.renderExtension({ extend: artifactName, includes: art.includes }, env);
|
|
1038
1542
|
return result;
|
|
1039
1543
|
}
|
|
1040
1544
|
|
|
@@ -1050,7 +1554,8 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1050
1554
|
* @param {CdlRenderEnvironment} env
|
|
1051
1555
|
* @param {object} [elements]
|
|
1052
1556
|
*/
|
|
1053
|
-
|
|
1557
|
+
renderQuery( query, isLeadingQuery, syntax, env, elements = query.elements || Object.create(null) ) {
|
|
1558
|
+
const that = this;
|
|
1054
1559
|
if (query.SET) {
|
|
1055
1560
|
// Set operator, such as UNION, INTERSECT, or EXCEPT...
|
|
1056
1561
|
return renderQuerySet();
|
|
@@ -1067,7 +1572,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1067
1572
|
result += (syntax === 'projection') ? 'projection on ' : 'select from ';
|
|
1068
1573
|
|
|
1069
1574
|
env.path.push('from');
|
|
1070
|
-
result += renderViewSource(select.from, env);
|
|
1575
|
+
result += this.renderViewSource(select.from, env);
|
|
1071
1576
|
env.path.length -= 1;
|
|
1072
1577
|
|
|
1073
1578
|
if (select.mixin) {
|
|
@@ -1076,7 +1581,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1076
1581
|
env.increaseIndent();
|
|
1077
1582
|
forEach(select.mixin, (name, mixin) => {
|
|
1078
1583
|
env.path[env.path.length - 1] = name;
|
|
1079
|
-
elems += renderElement(name, mixin, env);
|
|
1584
|
+
elems += this.renderElement(name, mixin, env);
|
|
1080
1585
|
});
|
|
1081
1586
|
env.decreaseIndent();
|
|
1082
1587
|
env.path.length -= 2;
|
|
@@ -1090,35 +1595,35 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1090
1595
|
if (select.columns) {
|
|
1091
1596
|
result += ' {\n';
|
|
1092
1597
|
env.increaseIndent();
|
|
1093
|
-
result += renderViewColumns(select, env, elements);
|
|
1598
|
+
result += this.renderViewColumns(select, env, elements);
|
|
1094
1599
|
env.decreaseIndent();
|
|
1095
1600
|
result += `${env.indent}}`;
|
|
1096
1601
|
}
|
|
1097
1602
|
|
|
1098
1603
|
const childEnv = env.withIncreasedIndent();
|
|
1099
1604
|
if (select.excluding) {
|
|
1100
|
-
const excludes = select.excluding.map(id => `${childEnv.indent}${quoteNonIdentifierOrKeyword(id, env)}`).join(',\n');
|
|
1605
|
+
const excludes = select.excluding.map(id => `${childEnv.indent}${this.quoteNonIdentifierOrKeyword(id, env)}`).join(',\n');
|
|
1101
1606
|
result += ` excluding {\n${excludes}\n`;
|
|
1102
1607
|
result += `${env.indent}}`;
|
|
1103
1608
|
}
|
|
1104
1609
|
|
|
1105
1610
|
if (isLeadingQuery && query.actions)
|
|
1106
|
-
result +=
|
|
1611
|
+
result += this.renderBoundActionsAndFunctions(query, env);
|
|
1107
1612
|
|
|
1108
1613
|
if (select.where)
|
|
1109
|
-
result += `${continueIndent(result, env)}where ${exprRenderer.renderExpr(select.where, env.withSubPath([ 'where' ]))}`;
|
|
1614
|
+
result += `${continueIndent(result, env)}where ${this.exprRenderer.renderExpr(select.where, env.withSubPath([ 'where' ]))}`;
|
|
1110
1615
|
|
|
1111
1616
|
if (select.groupBy)
|
|
1112
|
-
result += `${continueIndent(result, env)}group by ${select.groupBy.map((expr, i) => exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1617
|
+
result += `${continueIndent(result, env)}group by ${select.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1113
1618
|
|
|
1114
1619
|
if (select.having)
|
|
1115
|
-
result += `${continueIndent(result, env)}having ${exprRenderer.renderExpr(select.having, env.withSubPath([ 'having' ]))}`;
|
|
1620
|
+
result += `${continueIndent(result, env)}having ${this.exprRenderer.renderExpr(select.having, env.withSubPath([ 'having' ]))}`;
|
|
1116
1621
|
|
|
1117
1622
|
if (select.orderBy)
|
|
1118
|
-
result += `${continueIndent(result, env)}order by ${select.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1623
|
+
result += `${continueIndent(result, env)}order by ${select.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1119
1624
|
|
|
1120
1625
|
if (select.limit)
|
|
1121
|
-
result += `${continueIndent(result, env)}${renderLimit(select.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1626
|
+
result += `${continueIndent(result, env)}${this.renderLimit(select.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1122
1627
|
|
|
1123
1628
|
return result;
|
|
1124
1629
|
|
|
@@ -1147,7 +1652,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1147
1652
|
const subQueries = query.SET.args.map((arg, i) => {
|
|
1148
1653
|
// First arg may be leading query
|
|
1149
1654
|
const subEnv = env.withSubPath([ 'SET', 'args', i ]);
|
|
1150
|
-
const subQuery = renderQuery(arg, isLeadingQuery && (i === 0), 'view', subEnv, elements);
|
|
1655
|
+
const subQuery = that.renderQuery(arg, isLeadingQuery && (i === 0), 'view', subEnv, elements);
|
|
1151
1656
|
return `(${subQuery})`;
|
|
1152
1657
|
});
|
|
1153
1658
|
|
|
@@ -1155,10 +1660,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1155
1660
|
// Set operation may also have an ORDER BY and LIMIT/OFFSET (in contrast to the ones belonging to
|
|
1156
1661
|
// each SELECT)
|
|
1157
1662
|
if (query.SET.orderBy)
|
|
1158
|
-
setResult += `${continueIndent(setResult, env)}order by ${query.SET.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'SET', 'orderBy', i ]))).join(', ')}`;
|
|
1663
|
+
setResult += `${continueIndent(setResult, env)}order by ${query.SET.orderBy.map((entry, i) => that.renderOrderByEntry(entry, env.withSubPath([ 'SET', 'orderBy', i ]))).join(', ')}`;
|
|
1159
1664
|
|
|
1160
1665
|
if (query.SET.limit)
|
|
1161
|
-
setResult += `${continueIndent(setResult, env)}${renderLimit(query.SET.limit, env.withSubPath([ 'SET', 'limit' ]))}`;
|
|
1666
|
+
setResult += `${continueIndent(setResult, env)}${that.renderLimit(query.SET.limit, env.withSubPath([ 'SET', 'limit' ]))}`;
|
|
1162
1667
|
return setResult;
|
|
1163
1668
|
}
|
|
1164
1669
|
}
|
|
@@ -1171,8 +1676,8 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1171
1676
|
* @param {CdlRenderEnvironment} env
|
|
1172
1677
|
* @return {string}
|
|
1173
1678
|
*/
|
|
1174
|
-
|
|
1175
|
-
let result = renderAnnotationAssignmentsAndDocComment(entry, env) + exprRenderer.renderExpr(entry, env);
|
|
1679
|
+
renderOrderByEntry( entry, env ) {
|
|
1680
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(entry, env) + this.exprRenderer.renderExpr(entry, env);
|
|
1176
1681
|
if (entry.sort)
|
|
1177
1682
|
result += ` ${entry.sort}`;
|
|
1178
1683
|
|
|
@@ -1189,14 +1694,14 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1189
1694
|
* @param {CdlRenderEnvironment} limitEnv
|
|
1190
1695
|
* @return {string}
|
|
1191
1696
|
*/
|
|
1192
|
-
|
|
1697
|
+
renderLimit( limit, limitEnv ) {
|
|
1193
1698
|
let limitStr = '';
|
|
1194
1699
|
if (limit.rows !== undefined)
|
|
1195
|
-
limitStr += `limit ${exprRenderer.renderExpr(limit.rows, limitEnv.withSubPath([ 'rows' ]))}`;
|
|
1700
|
+
limitStr += `limit ${this.exprRenderer.renderExpr(limit.rows, limitEnv.withSubPath([ 'rows' ]))}`;
|
|
1196
1701
|
|
|
1197
1702
|
if (limit.offset !== undefined) {
|
|
1198
1703
|
const offsetIndent = (limitStr === '') ? '' : `\n${limitEnv.withIncreasedIndent().indent}`;
|
|
1199
|
-
limitStr += `${offsetIndent}offset ${exprRenderer.renderExpr(limit.offset, limitEnv.withSubPath([ 'offset' ]))}`;
|
|
1704
|
+
limitStr += `${offsetIndent}offset ${this.exprRenderer.renderExpr(limit.offset, limitEnv.withSubPath([ 'offset' ]))}`;
|
|
1200
1705
|
}
|
|
1201
1706
|
return limitStr;
|
|
1202
1707
|
}
|
|
@@ -1211,12 +1716,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1211
1716
|
* @param {CdlRenderEnvironment} env
|
|
1212
1717
|
* @return {string}
|
|
1213
1718
|
*/
|
|
1214
|
-
|
|
1719
|
+
renderBoundActionsAndFunctions( art, env ) {
|
|
1215
1720
|
let result = '';
|
|
1216
1721
|
if (art.actions) {
|
|
1217
1722
|
const childEnv = env.withIncreasedIndent();
|
|
1218
1723
|
for (const name in art.actions)
|
|
1219
|
-
result += renderActionOrFunction(name, art.actions[name], childEnv.withSubPath([ 'actions', name ]));
|
|
1724
|
+
result += this.renderActionOrFunction(name, art.actions[name], childEnv.withSubPath([ 'actions', name ]), true);
|
|
1220
1725
|
result = (result === '')
|
|
1221
1726
|
? ' actions { }'
|
|
1222
1727
|
: ` actions {\n${result}${env.indent}}`;
|
|
@@ -1230,18 +1735,25 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1230
1735
|
* @param {string} actionName
|
|
1231
1736
|
* @param {CSN.Action} act
|
|
1232
1737
|
* @param {CdlRenderEnvironment} env
|
|
1738
|
+
* @param {boolean} isBound
|
|
1233
1739
|
* @return {string}
|
|
1234
1740
|
*/
|
|
1235
|
-
|
|
1236
|
-
let result = renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
|
|
1237
|
-
|
|
1238
|
-
|
|
1741
|
+
renderActionOrFunction( actionName, act, env, isBound ) {
|
|
1742
|
+
let result = this.renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
|
|
1743
|
+
if (isBound) {
|
|
1744
|
+
// for bound actions, paths are not global
|
|
1745
|
+
result += ` ${this.quotePathIfRequired(actionName, env)}`;
|
|
1746
|
+
}
|
|
1747
|
+
else {
|
|
1748
|
+
result += ` ${this.renderArtifactName(actionName, env)}`;
|
|
1749
|
+
}
|
|
1750
|
+
result += this.renderParameters(act, env);
|
|
1239
1751
|
if (act.returns) {
|
|
1240
1752
|
let actEnv = env.withSubPath([ 'returns' ]);
|
|
1241
|
-
const annos = renderAnnotationAssignmentsAndDocComment(act.returns, actEnv.withIncreasedIndent());
|
|
1753
|
+
const annos = this.renderAnnotationAssignmentsAndDocComment(act.returns, actEnv.withIncreasedIndent());
|
|
1242
1754
|
if (annos) // if `returns` has annotations, increase indent for nicer aligned output
|
|
1243
1755
|
actEnv = actEnv.withIncreasedIndent();
|
|
1244
|
-
const type = renderTypeReferenceAndProps(act.returns, actEnv);
|
|
1756
|
+
const type = this.renderTypeReferenceAndProps(act.returns, actEnv);
|
|
1245
1757
|
result += ` returns${annos ? '\n' : ' '}${annos}${annos ? actEnv.indent : ''}${type}`;
|
|
1246
1758
|
}
|
|
1247
1759
|
|
|
@@ -1258,9 +1770,9 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1258
1770
|
* @param {CdlRenderEnvironment} env
|
|
1259
1771
|
* @returns {string}
|
|
1260
1772
|
*/
|
|
1261
|
-
|
|
1773
|
+
renderParameters( art, env ) {
|
|
1262
1774
|
const childEnv = env.withIncreasedIndent();
|
|
1263
|
-
const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv));
|
|
1775
|
+
const parameters = Object.keys(art.params || {}).map(name => this.renderParameter(name, art.params[name], childEnv));
|
|
1264
1776
|
if (parameters.length === 0)
|
|
1265
1777
|
return '()';
|
|
1266
1778
|
return `(\n${parameters.join(',\n')}\n${env.indent})`;
|
|
@@ -1274,10 +1786,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1274
1786
|
* @param {CdlRenderEnvironment} env
|
|
1275
1787
|
* @return {string}
|
|
1276
1788
|
*/
|
|
1277
|
-
|
|
1789
|
+
renderParameter( parName, par, env ) {
|
|
1278
1790
|
env = env.withSubPath( [ 'params', parName ]);
|
|
1279
|
-
let result = `${renderAnnotationAssignmentsAndDocComment(par, env)}${env.indent}`;
|
|
1280
|
-
result += `${quoteNonIdentifierOrKeyword(parName, env)} : ${renderTypeReferenceAndProps(par, env)}`;
|
|
1791
|
+
let result = `${this.renderAnnotationAssignmentsAndDocComment(par, env)}${env.indent}`;
|
|
1792
|
+
result += `${this.quoteNonIdentifierOrKeyword(parName, env)} : ${this.renderTypeReferenceAndProps(par, env)}`;
|
|
1281
1793
|
return result;
|
|
1282
1794
|
}
|
|
1283
1795
|
|
|
@@ -1293,7 +1805,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1293
1805
|
* @param {boolean} [config.noAnnoCollect] Do not collect annotations of sub-elements.
|
|
1294
1806
|
* @return {string}
|
|
1295
1807
|
*/
|
|
1296
|
-
|
|
1808
|
+
renderTypeReferenceAndProps( artifact, env, config = {} ) {
|
|
1297
1809
|
let result = '';
|
|
1298
1810
|
const { typeRefOnly, noAnnoCollect } = config;
|
|
1299
1811
|
|
|
@@ -1307,7 +1819,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1307
1819
|
// "not null". Keep a reference to the outer artifact.
|
|
1308
1820
|
const origArtifact = artifact;
|
|
1309
1821
|
if (!artifact.type && artifact.items) {
|
|
1310
|
-
checkArrayedArtifact(artifact, env);
|
|
1822
|
+
this.checkArrayedArtifact(artifact, env);
|
|
1311
1823
|
result += 'many '; // alternative: 'array of'; but not used
|
|
1312
1824
|
artifact = artifact.items;
|
|
1313
1825
|
env = env.withSubPath([ 'items' ]);
|
|
@@ -1316,8 +1828,8 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1316
1828
|
const type = normalizeTypeRef(artifact.type);
|
|
1317
1829
|
|
|
1318
1830
|
if (!type && artifact.elements) {
|
|
1319
|
-
result += renderElements(artifact, env);
|
|
1320
|
-
result += renderNullability(artifact.notNull);
|
|
1831
|
+
result += this.renderElements(artifact, env);
|
|
1832
|
+
result += this.renderNullability(artifact.notNull);
|
|
1321
1833
|
// structured default not possible at the moment
|
|
1322
1834
|
return result;
|
|
1323
1835
|
}
|
|
@@ -1330,7 +1842,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1330
1842
|
const isComp = type === 'cds.Composition';
|
|
1331
1843
|
// Type, cardinality and target; CAPire uses CamelCase
|
|
1332
1844
|
result += isComp ? 'Composition' : 'Association';
|
|
1333
|
-
result += renderCardinality(artifact);
|
|
1845
|
+
result += this.renderCardinality(artifact);
|
|
1334
1846
|
|
|
1335
1847
|
// `targetAspect` may be set by the core compiler and refers to the original named or unnamed aspect.
|
|
1336
1848
|
// In parseCdl, `target` may still be an object containing elements. This would be replaced
|
|
@@ -1338,12 +1850,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1338
1850
|
// If a name exists (either in target or targetAspect), prefer it over rendering elements.
|
|
1339
1851
|
const elements = artifact.target?.elements || artifact.targetAspect?.elements;
|
|
1340
1852
|
if (typeof artifact.target === 'string' || typeof artifact.targetAspect === 'string') {
|
|
1341
|
-
result += renderAbsolutePath({ ref: [ artifact.target || artifact.targetAspect ] },
|
|
1342
|
-
|
|
1853
|
+
result += this.renderAbsolutePath({ ref: [ artifact.target || artifact.targetAspect ] },
|
|
1854
|
+
{ ...env, additionalKeywords: [ 'MANY', 'ONE' ] });
|
|
1343
1855
|
}
|
|
1344
1856
|
else if (elements) {
|
|
1345
1857
|
// anonymous aspect, either parseCdl or client CSN.
|
|
1346
|
-
result += renderElements({ elements }, env.withSubPath([ artifact.target?.elements ? 'target' : 'targetAspect' ]));
|
|
1858
|
+
result += this.renderElements({ elements }, env.withSubPath([ artifact.target?.elements ? 'target' : 'targetAspect' ]));
|
|
1347
1859
|
}
|
|
1348
1860
|
else {
|
|
1349
1861
|
throw new ModelError('Association/Composition is missing its target! Throwing exception to trigger recompilation.');
|
|
@@ -1351,16 +1863,16 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1351
1863
|
|
|
1352
1864
|
// ON-condition (if any)
|
|
1353
1865
|
if (artifact.on)
|
|
1354
|
-
result += ` on ${exprRenderer.renderExpr(artifact.on, env.withSubPath([ 'on' ]))}`;
|
|
1866
|
+
result += ` on ${this.exprRenderer.renderExpr(artifact.on, env.withSubPath([ 'on' ]))}`;
|
|
1355
1867
|
|
|
1356
1868
|
// Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
|
|
1357
1869
|
if (artifact.keys && !artifact.on)
|
|
1358
|
-
result += ` ${ renderForeignKeys(artifact, env) }`;
|
|
1870
|
+
result += ` ${ this.renderForeignKeys(artifact, env) }`;
|
|
1359
1871
|
|
|
1360
1872
|
if (!artifact.on) {
|
|
1361
1873
|
// unmanaged associations can't be followed by "not null" or "default"
|
|
1362
|
-
result += renderNullability(notNull);
|
|
1363
|
-
result += renderDefaultExpr(defaultValue, env.withSubPath([ 'default' ]));
|
|
1874
|
+
result += this.renderNullability(notNull);
|
|
1875
|
+
result += this.renderDefaultExpr(defaultValue, env.withSubPath([ 'default' ]));
|
|
1364
1876
|
}
|
|
1365
1877
|
return result;
|
|
1366
1878
|
}
|
|
@@ -1372,28 +1884,28 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1372
1884
|
// We only extract annotations of enums, if "typeRefOnly" is true. Otherwise, since
|
|
1373
1885
|
// the full enum is rendered below, we would have unnecessary annotations.
|
|
1374
1886
|
if (!noAnnoCollect && (!artifact.enum || typeRefOnly)) {
|
|
1375
|
-
const annotate = collectAnnotationsOfElementsAndEnum(artifact, env);
|
|
1887
|
+
const annotate = this.collectAnnotationsOfElementsAndEnum(artifact, env);
|
|
1376
1888
|
if (annotate)
|
|
1377
|
-
subelementAnnotates.push(annotate);
|
|
1889
|
+
this.subelementAnnotates.push(annotate);
|
|
1378
1890
|
}
|
|
1379
1891
|
|
|
1380
1892
|
// Reference to another artifact
|
|
1381
1893
|
if (typeof type === 'string') {
|
|
1382
1894
|
// If we get here, it must be a named type
|
|
1383
|
-
result += renderNamedTypeWithParameters(artifact, env);
|
|
1895
|
+
result += this.renderNamedTypeWithParameters(artifact, env);
|
|
1384
1896
|
}
|
|
1385
1897
|
else if (type?.ref) {
|
|
1386
|
-
result += renderAbsolutePath(artifact.type, env);
|
|
1898
|
+
result += this.renderAbsolutePath(artifact.type, env);
|
|
1387
1899
|
}
|
|
1388
1900
|
|
|
1389
1901
|
if (artifact.enum && !typeRefOnly)
|
|
1390
|
-
result += renderEnum(artifact.enum, env);
|
|
1902
|
+
result += this.renderEnum(artifact.enum, env);
|
|
1391
1903
|
|
|
1392
|
-
result += renderNullability(notNull);
|
|
1904
|
+
result += this.renderNullability(notNull);
|
|
1393
1905
|
// If there is a default value, and it's a calculated element, do not
|
|
1394
1906
|
// render the default (because it's not supported for calc elements).
|
|
1395
1907
|
if (defaultValue !== undefined && !artifact.value)
|
|
1396
|
-
result += renderDefaultExpr(defaultValue, env.withSubPath([ 'default' ]));
|
|
1908
|
+
result += this.renderDefaultExpr(defaultValue, env.withSubPath([ 'default' ]));
|
|
1397
1909
|
|
|
1398
1910
|
return result;
|
|
1399
1911
|
}
|
|
@@ -1405,12 +1917,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1405
1917
|
* @param {CdlRenderEnvironment} env
|
|
1406
1918
|
* @return {string}
|
|
1407
1919
|
*/
|
|
1408
|
-
|
|
1409
|
-
let result = `redirected to ${renderDefinitionReference(art.target, env)}`;
|
|
1920
|
+
renderRedirectedTo( art, env ) {
|
|
1921
|
+
let result = `redirected to ${this.renderDefinitionReference(art.target, env)}`;
|
|
1410
1922
|
if (art.on)
|
|
1411
|
-
result += ` on ${exprRenderer.renderExpr(art.on, env.withSubPath([ 'on' ]))}`;
|
|
1923
|
+
result += ` on ${this.exprRenderer.renderExpr(art.on, env.withSubPath([ 'on' ]))}`;
|
|
1412
1924
|
else if (art.keys)
|
|
1413
|
-
result += ` ${ renderForeignKeys(art, env) }`;
|
|
1925
|
+
result += ` ${ this.renderForeignKeys(art, env) }`;
|
|
1414
1926
|
return result;
|
|
1415
1927
|
}
|
|
1416
1928
|
|
|
@@ -1421,26 +1933,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1421
1933
|
* @param {CdlRenderEnvironment} env
|
|
1422
1934
|
* @return {string}
|
|
1423
1935
|
*/
|
|
1424
|
-
|
|
1936
|
+
renderNamedTypeWithParameters( artWithType, env ) {
|
|
1425
1937
|
const type = normalizeTypeRef(artWithType.type);
|
|
1426
|
-
let result =
|
|
1427
|
-
|
|
1428
|
-
if (isBuiltinType(type)) {
|
|
1429
|
-
// If there is a user-defined type that starts with the same short name
|
|
1430
|
-
// (cds.Integer -> Integer), we render the full name, including the leading "cds."
|
|
1431
|
-
const shortHand = type.slice(4);
|
|
1432
|
-
if (shortHand.startsWith('hana.') && hanaRequiresAbsolutePath)
|
|
1433
|
-
result += type;
|
|
1434
|
-
else if (usings.available.includes(shortHand))
|
|
1435
|
-
result += type;
|
|
1436
|
-
else
|
|
1437
|
-
result += shortHand;
|
|
1438
|
-
}
|
|
1439
|
-
else {
|
|
1440
|
-
result += renderDefinitionReference(type, env);
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
result += renderTypeParameters(artWithType);
|
|
1938
|
+
let result = this.renderDefinitionReference(type, env);
|
|
1939
|
+
result += this.renderTypeParameters(artWithType);
|
|
1444
1940
|
return result;
|
|
1445
1941
|
}
|
|
1446
1942
|
|
|
@@ -1452,11 +1948,11 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1452
1948
|
* @param {CdlRenderEnvironment} env
|
|
1453
1949
|
* @return {string}
|
|
1454
1950
|
*/
|
|
1455
|
-
|
|
1951
|
+
renderEnum( enumPart, env ) {
|
|
1456
1952
|
let result = ' enum {\n';
|
|
1457
1953
|
const childEnv = env.withIncreasedIndent();
|
|
1458
1954
|
for (const name in enumPart)
|
|
1459
|
-
result += renderElement(name, enumPart[name], childEnv.withSubPath([ 'enum', name ]));
|
|
1955
|
+
result += this.renderElement(name, enumPart[name], childEnv.withSubPath([ 'enum', name ]));
|
|
1460
1956
|
result += `${env.indent}}`;
|
|
1461
1957
|
return result;
|
|
1462
1958
|
}
|
|
@@ -1469,14 +1965,14 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1469
1965
|
* @param {any} annoValue
|
|
1470
1966
|
* @param {CdlRenderEnvironment} env
|
|
1471
1967
|
*/
|
|
1472
|
-
|
|
1968
|
+
renderAnnotationValue( annoValue, env ) {
|
|
1473
1969
|
if (isAnnotationExpression(annoValue)) {
|
|
1474
1970
|
// Once inside an expression, we stay there.
|
|
1475
|
-
const xpr = exprRenderer.renderExpr(annoValue, env);
|
|
1971
|
+
const xpr = this.exprRenderer.renderExpr(annoValue, env);
|
|
1476
1972
|
return `( ${xpr} )`;
|
|
1477
1973
|
}
|
|
1478
1974
|
else if (Array.isArray(annoValue)) {
|
|
1479
|
-
return renderAnnotationArrayValue( annoValue, env );
|
|
1975
|
+
return this.renderAnnotationArrayValue( annoValue, env );
|
|
1480
1976
|
}
|
|
1481
1977
|
else if (typeof annoValue === 'object' && annoValue !== null) {
|
|
1482
1978
|
// Enum symbol
|
|
@@ -1486,26 +1982,24 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1486
1982
|
// Shorthand for absolute path (as string)
|
|
1487
1983
|
else if (annoValue['='] !== undefined) {
|
|
1488
1984
|
if (annoValue['='].startsWith('@'))
|
|
1489
|
-
return quoteAnnotationPathIfRequired(annoValue['='], env);
|
|
1490
|
-
return quotePathIfRequired(annoValue['='], env);
|
|
1985
|
+
return this.quoteAnnotationPathIfRequired(annoValue['='], env);
|
|
1986
|
+
return this.quotePathIfRequired(annoValue['='], env);
|
|
1491
1987
|
}
|
|
1492
1988
|
// Shorthand for ellipsis: `... up to <val>`
|
|
1493
1989
|
else if (annoValue['...'] !== undefined) {
|
|
1494
1990
|
if (annoValue['...'] === true)
|
|
1495
1991
|
return '...';
|
|
1496
|
-
return `... up to ${renderAnnotationValue(annoValue['...'], env)}`;
|
|
1992
|
+
return `... up to ${this.renderAnnotationValue(annoValue['...'], env)}`;
|
|
1497
1993
|
}
|
|
1498
1994
|
|
|
1499
1995
|
// Struct value (can currently only occur within an array)
|
|
1500
1996
|
// Render as one-liner if there is at most one key. Render as multi-line
|
|
1501
1997
|
// struct if there are more and use nicer indentation.
|
|
1502
1998
|
const keys = Object.keys(annoValue);
|
|
1503
|
-
const childEnv =
|
|
1504
|
-
const values = keys.map(key => `${quoteAnnotationPathIfRequired(key, env)}: ${renderAnnotationValue(annoValue[key], childEnv.withSubPath([ key ]))}`);
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
const valueList = values.join(`,\n${childEnv.indent}`);
|
|
1508
|
-
return `{\n${childEnv.indent}${valueList}\n${env.indent}}`;
|
|
1999
|
+
const childEnv = env.withIncreasedIndent();
|
|
2000
|
+
const values = keys.map(key => `${this.quoteAnnotationPathIfRequired(key, env)}: ${this.renderAnnotationValue(annoValue[key], childEnv.withSubPath([ key ]))}`);
|
|
2001
|
+
const result = joinDocuments(values, [ ',', line() ]);
|
|
2002
|
+
return format(nestBy(env.indent.length, bracketBlock(INDENT_SIZE, '{', result, '}') ));
|
|
1509
2003
|
}
|
|
1510
2004
|
// Null
|
|
1511
2005
|
else if (annoValue === null) {
|
|
@@ -1527,25 +2021,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1527
2021
|
* @param {CdlRenderEnvironment} env
|
|
1528
2022
|
* @return {string}
|
|
1529
2023
|
*/
|
|
1530
|
-
|
|
2024
|
+
renderAnnotationArrayValue( annoValue, env ) {
|
|
1531
2025
|
const childEnv = env.withIncreasedIndent();
|
|
1532
2026
|
// Render array parts as values.
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
const result = renderAnnotationValue(item, childEnv.withSubPath([ i ]));
|
|
1537
|
-
length += result.length + 2; // just a heuristic; add 2 for `, `.
|
|
1538
|
-
if (!hasLineBreak && result.includes('\n'))
|
|
1539
|
-
hasLineBreak = true;
|
|
1540
|
-
return result;
|
|
1541
|
-
});
|
|
1542
|
-
|
|
1543
|
-
if (!hasLineBreak && (length + env.indent.length) < 100) {
|
|
1544
|
-
// Spaces required if last array value is a delimited identifier.
|
|
1545
|
-
return `[ ${items.join(', ')} ]`;
|
|
1546
|
-
}
|
|
1547
|
-
const renderedItems = items.join(`,\n${childEnv.indent}`);
|
|
1548
|
-
return `[\n${childEnv.indent}${renderedItems}\n${env.indent}]`;
|
|
2027
|
+
const items = annoValue.map((item, i) => this.renderAnnotationValue(item, childEnv.withSubPath([ i ])));
|
|
2028
|
+
const result = joinDocuments(items, [ ',', line() ]);
|
|
2029
|
+
return format(nestBy(env.indent.length, bracketBlock(INDENT_SIZE, '[', result, ']')));
|
|
1549
2030
|
}
|
|
1550
2031
|
|
|
1551
2032
|
/**
|
|
@@ -1556,14 +2037,14 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1556
2037
|
* @param {object} env
|
|
1557
2038
|
* @returns {string}
|
|
1558
2039
|
*/
|
|
1559
|
-
|
|
2040
|
+
renderPathStep( s, idx, env ) {
|
|
1560
2041
|
// Simple id or absolute name
|
|
1561
2042
|
if (typeof s === 'string') {
|
|
1562
2043
|
// In first path position, do not quote $projection and magic $-variables like CURRENT_DATE, $now etc.
|
|
1563
2044
|
// FIXME: We should rather explicitly recognize quoting somehow
|
|
1564
2045
|
if (idx === 0 && s.startsWith('$'))
|
|
1565
2046
|
return s;
|
|
1566
|
-
return quoteNonIdentifierOrKeyword(s, env);
|
|
2047
|
+
return this.quoteNonIdentifierOrKeyword(s, env);
|
|
1567
2048
|
}
|
|
1568
2049
|
// ID with filters or parameters
|
|
1569
2050
|
else if (typeof s === 'object') {
|
|
@@ -1573,16 +2054,16 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1573
2054
|
|
|
1574
2055
|
// Not really a path step but an object-like function call
|
|
1575
2056
|
if (s.func)
|
|
1576
|
-
return `${s.func}(${renderArguments(s, '=>', env)})`;
|
|
2057
|
+
return `${s.func}(${this.renderArguments(s, '=>', env)})`;
|
|
1577
2058
|
|
|
1578
2059
|
// Path step, possibly with view parameters and/or filters
|
|
1579
|
-
let result = `${quoteNonIdentifierOrKeyword(s.id, env)}`;
|
|
2060
|
+
let result = `${this.quoteNonIdentifierOrKeyword(s.id, env)}`;
|
|
1580
2061
|
if (s.args) {
|
|
1581
2062
|
// View parameters
|
|
1582
|
-
result += `(${renderArguments(s, ':', env)})`;
|
|
2063
|
+
result += `(${this.renderArguments(s, ':', env)})`;
|
|
1583
2064
|
}
|
|
1584
2065
|
|
|
1585
|
-
result += renderFilterAndCardinality(s, env);
|
|
2066
|
+
result += this.renderFilterAndCardinality(s, env);
|
|
1586
2067
|
|
|
1587
2068
|
return result;
|
|
1588
2069
|
}
|
|
@@ -1599,13 +2080,13 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1599
2080
|
* @param {CdlRenderEnvironment} env
|
|
1600
2081
|
* @returns {string}
|
|
1601
2082
|
*/
|
|
1602
|
-
|
|
2083
|
+
renderArguments( node, sep, env ) {
|
|
1603
2084
|
if (!node.args)
|
|
1604
2085
|
return '';
|
|
1605
2086
|
else if (Array.isArray(node.args))
|
|
1606
|
-
return renderPositionalArguments(node, env);
|
|
2087
|
+
return this.renderPositionalArguments(node, env);
|
|
1607
2088
|
else if (typeof node.args === 'object')
|
|
1608
|
-
return renderNamedArguments(node, sep, env);
|
|
2089
|
+
return this.renderNamedArguments(node, sep, env);
|
|
1609
2090
|
throw new ModelError(`Unknown args: ${JSON.stringify(node.args)}; expected array/object`);
|
|
1610
2091
|
}
|
|
1611
2092
|
|
|
@@ -1618,9 +2099,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1618
2099
|
* @param {CdlRenderEnvironment} env
|
|
1619
2100
|
* @returns {string}
|
|
1620
2101
|
*/
|
|
1621
|
-
|
|
2102
|
+
renderNamedArguments( node, separator, env ) {
|
|
2103
|
+
const that = this;
|
|
1622
2104
|
return Object.keys(node.args).map(function renderNamedArgument(key) {
|
|
1623
|
-
return `${quoteNonIdentifierOrKeyword(key, env)} ${separator} ${renderArgument(node.args[key], env.withSubPath([ 'args', key ]))}`;
|
|
2105
|
+
return `${that.quoteNonIdentifierOrKeyword(key, env)} ${separator} ${that.renderArgument(node.args[key], env.withSubPath([ 'args', key ]))}`;
|
|
1624
2106
|
}).join(', ');
|
|
1625
2107
|
}
|
|
1626
2108
|
|
|
@@ -1631,16 +2113,17 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1631
2113
|
* @param {CdlRenderEnvironment} env
|
|
1632
2114
|
* @returns {string}
|
|
1633
2115
|
*/
|
|
1634
|
-
|
|
2116
|
+
renderPositionalArguments( node, env ) {
|
|
1635
2117
|
if (!node.args)
|
|
1636
2118
|
return '';
|
|
1637
2119
|
const func = node.func?.toUpperCase();
|
|
2120
|
+
const that = this;
|
|
1638
2121
|
if (func) {
|
|
1639
2122
|
return node.args.map(function renderFunctionArg(arg, i) {
|
|
1640
|
-
return renderArgument(arg, env.withSubPath([ 'args', i ]), getKeywordsForSpecialFunctionArgument(func, i));
|
|
2123
|
+
return that.renderArgument(arg, env.withSubPath([ 'args', i ]), getKeywordsForSpecialFunctionArgument(func, i));
|
|
1641
2124
|
}).join(', ');
|
|
1642
2125
|
}
|
|
1643
|
-
return node.args.map((arg, i) => renderArgument(arg, env.withSubPath([ 'args', i ]))).join(', ');
|
|
2126
|
+
return node.args.map((arg, i) => this.renderArgument(arg, env.withSubPath([ 'args', i ]))).join(', ');
|
|
1644
2127
|
}
|
|
1645
2128
|
|
|
1646
2129
|
/**
|
|
@@ -1652,13 +2135,13 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1652
2135
|
* @param {string[]} additionalKeywords
|
|
1653
2136
|
* @return {string}
|
|
1654
2137
|
*/
|
|
1655
|
-
|
|
2138
|
+
renderArgument( arg, env, additionalKeywords = [] ) {
|
|
1656
2139
|
// If the argument is a xpr with e.g. `=`, it may require parentheses.
|
|
1657
2140
|
// For nested xpr, `exprRenderer.renderExpr()` will already add parentheses.
|
|
1658
2141
|
env = env.cloneWith({ additionalKeywords });
|
|
1659
2142
|
if (isSimpleFunctionExpression(arg && arg.xpr, additionalKeywords))
|
|
1660
|
-
return exprRenderer.renderExpr(arg, env);
|
|
1661
|
-
return exprRenderer.renderSubExpr(arg, env);
|
|
2143
|
+
return this.exprRenderer.renderExpr(arg, env);
|
|
2144
|
+
return this.exprRenderer.renderSubExpr(arg, env);
|
|
1662
2145
|
}
|
|
1663
2146
|
|
|
1664
2147
|
/**
|
|
@@ -1667,10 +2150,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1667
2150
|
* @param artifact
|
|
1668
2151
|
* @returns {string}
|
|
1669
2152
|
*/
|
|
1670
|
-
|
|
1671
|
-
if (isSimpleCardinality(artifact.cardinality))
|
|
1672
|
-
return renderSimpleCardinality(artifact);
|
|
1673
|
-
return renderBracketCardinality(artifact);
|
|
2153
|
+
renderCardinality( artifact ) {
|
|
2154
|
+
if (this.isSimpleCardinality(artifact.cardinality))
|
|
2155
|
+
return this.renderSimpleCardinality(artifact);
|
|
2156
|
+
return this.renderBracketCardinality(artifact);
|
|
1674
2157
|
}
|
|
1675
2158
|
|
|
1676
2159
|
/**
|
|
@@ -1679,7 +2162,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1679
2162
|
* @param {CSN.Artifact} art
|
|
1680
2163
|
* @return {string}
|
|
1681
2164
|
*/
|
|
1682
|
-
|
|
2165
|
+
renderBracketCardinality( art ) {
|
|
1683
2166
|
const isComp = normalizeTypeRef(art.type) === 'cds.Composition';
|
|
1684
2167
|
const suffix = (isComp ? ' of ' : ' to ');
|
|
1685
2168
|
const card = art.cardinality;
|
|
@@ -1705,7 +2188,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1705
2188
|
* @param {CSN.Cardinality} cardinality
|
|
1706
2189
|
* @return {boolean}
|
|
1707
2190
|
*/
|
|
1708
|
-
|
|
2191
|
+
isSimpleCardinality( cardinality ) {
|
|
1709
2192
|
return !cardinality || (
|
|
1710
2193
|
cardinality.min === undefined &&
|
|
1711
2194
|
cardinality.src === undefined &&
|
|
@@ -1721,7 +2204,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1721
2204
|
* @param {CSN.Element} elem
|
|
1722
2205
|
* @return {string}
|
|
1723
2206
|
*/
|
|
1724
|
-
|
|
2207
|
+
renderSimpleCardinality( elem ) {
|
|
1725
2208
|
let result = (normalizeTypeRef(elem.type) === 'cds.Association' ? ' to ' : ' of ');
|
|
1726
2209
|
if (!elem.cardinality)
|
|
1727
2210
|
return result;
|
|
@@ -1732,24 +2215,23 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1732
2215
|
return result;
|
|
1733
2216
|
}
|
|
1734
2217
|
|
|
1735
|
-
|
|
1736
|
-
function renderFilterAndCardinality( s, env ) {
|
|
2218
|
+
renderFilterAndCardinality( s, env ) {
|
|
1737
2219
|
let result = '';
|
|
1738
2220
|
const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
|
|
1739
2221
|
let filter = '';
|
|
1740
2222
|
|
|
1741
2223
|
// TODO: Unify with other filter rendering for SELECT
|
|
1742
2224
|
if (s.groupBy)
|
|
1743
|
-
filter += ` group by ${s.groupBy.map((expr, i) => exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
2225
|
+
filter += ` group by ${s.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1744
2226
|
if (s.having)
|
|
1745
|
-
filter += ` having ${exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ]))}`;
|
|
2227
|
+
filter += ` having ${this.exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ]))}`;
|
|
1746
2228
|
if (s.orderBy)
|
|
1747
|
-
filter += ` order by ${s.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
2229
|
+
filter += ` order by ${s.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1748
2230
|
if (s.limit)
|
|
1749
|
-
filter += ` ${renderLimit(s.limit, env.withSubPath([ 'limit' ]))}`;
|
|
2231
|
+
filter += ` ${this.renderLimit(s.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1750
2232
|
|
|
1751
2233
|
if (s.where) {
|
|
1752
|
-
let where = exprRenderer.renderExpr(s.where, env.withSubPath([ 'where' ]));
|
|
2234
|
+
let where = this.exprRenderer.renderExpr(s.where, env.withSubPath([ 'where' ]));
|
|
1753
2235
|
// Special rules in CDS parser: If filter starts with one of these SQL keywords, WHERE is mandatory.
|
|
1754
2236
|
if (filter || /^(?:group|having|order|limit)\s/i.test(where))
|
|
1755
2237
|
where = ` where ${where}`;
|
|
@@ -1767,19 +2249,19 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1767
2249
|
return result;
|
|
1768
2250
|
}
|
|
1769
2251
|
|
|
1770
|
-
|
|
2252
|
+
renderDefaultExpr( defaultValue, env ) {
|
|
1771
2253
|
if (!defaultValue)
|
|
1772
2254
|
return '';
|
|
1773
2255
|
let result = ' default ';
|
|
1774
2256
|
if (defaultValue.xpr && xprContainsCondition( defaultValue.xpr))
|
|
1775
|
-
result += exprRenderer.renderSubExpr(withoutCast(defaultValue), env);
|
|
2257
|
+
result += this.exprRenderer.renderSubExpr(withoutCast(defaultValue), env);
|
|
1776
2258
|
else
|
|
1777
|
-
result += exprRenderer.renderExpr(withoutCast(defaultValue), env);
|
|
2259
|
+
result += this.exprRenderer.renderExpr(withoutCast(defaultValue), env);
|
|
1778
2260
|
return result;
|
|
1779
2261
|
}
|
|
1780
2262
|
|
|
1781
2263
|
// Render the nullability of an element or parameter (can be unset, true, or false)
|
|
1782
|
-
|
|
2264
|
+
renderNullability( notNull /* , env */) {
|
|
1783
2265
|
if (notNull === undefined) {
|
|
1784
2266
|
// Attribute not set at all
|
|
1785
2267
|
return '';
|
|
@@ -1794,7 +2276,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1794
2276
|
* @param {CdlRenderEnvironment} env
|
|
1795
2277
|
* @return {string}
|
|
1796
2278
|
*/
|
|
1797
|
-
|
|
2279
|
+
renderForeignKeys( art, env ) {
|
|
1798
2280
|
const renderedKeys = [];
|
|
1799
2281
|
let hasAnnotations = false;
|
|
1800
2282
|
env = env.withSubPath([ 'keys', -1 ]);
|
|
@@ -1804,14 +2286,14 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1804
2286
|
env.path[env.path.length - 1] = i;
|
|
1805
2287
|
const fKey = art.keys[i];
|
|
1806
2288
|
|
|
1807
|
-
const annos = renderAnnotationAssignmentsAndDocComment(fKey, env).trim();
|
|
2289
|
+
const annos = this.renderAnnotationAssignmentsAndDocComment(fKey, env).trim();
|
|
1808
2290
|
if (annos) {
|
|
1809
2291
|
hasAnnotations = true;
|
|
1810
2292
|
renderedKeys.push(annos);
|
|
1811
2293
|
}
|
|
1812
2294
|
|
|
1813
|
-
const alias = fKey.as ? renderAlias(fKey.as, env) : '';
|
|
1814
|
-
const key = exprRenderer.renderExpr(fKey, env);
|
|
2295
|
+
const alias = fKey.as ? this.renderAlias(fKey.as, env) : '';
|
|
2296
|
+
const key = this.exprRenderer.renderExpr(fKey, env);
|
|
1815
2297
|
renderedKeys.push(`${key}${alias},`);
|
|
1816
2298
|
}
|
|
1817
2299
|
|
|
@@ -1834,8 +2316,8 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1834
2316
|
* @param {CdlRenderEnvironment} env
|
|
1835
2317
|
* @return {string}
|
|
1836
2318
|
*/
|
|
1837
|
-
|
|
1838
|
-
return ` as ${quoteNonIdentifierOrKeyword(alias, env)}`;
|
|
2319
|
+
renderAlias( alias, env ) {
|
|
2320
|
+
return ` as ${this.quoteNonIdentifierOrKeyword(alias, env)}`;
|
|
1839
2321
|
}
|
|
1840
2322
|
|
|
1841
2323
|
/**
|
|
@@ -1847,7 +2329,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1847
2329
|
* for length instead of `(length: 10)`.
|
|
1848
2330
|
* @returns {string}
|
|
1849
2331
|
*/
|
|
1850
|
-
|
|
2332
|
+
renderTypeParameters( artWithType, noShortVersion = false ) {
|
|
1851
2333
|
const params = typeParameters.list.filter(param => artWithType[param] !== undefined);
|
|
1852
2334
|
if (params.length === 0)
|
|
1853
2335
|
return '';
|
|
@@ -1876,11 +2358,11 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1876
2358
|
* @param {{parentheses: boolean}} [config] Config for renderAnnotationAssignment()
|
|
1877
2359
|
* @return {string}
|
|
1878
2360
|
*/
|
|
1879
|
-
|
|
1880
|
-
let result = renderDocComment(obj, env);
|
|
2361
|
+
renderAnnotationAssignmentsAndDocComment( obj, env, config ) {
|
|
2362
|
+
let result = this.renderDocComment(obj, env);
|
|
1881
2363
|
for (const name in obj) {
|
|
1882
2364
|
if (name.startsWith('@'))
|
|
1883
|
-
result += renderAnnotationAssignment(obj[name], name, env.withSubPath([ name ]), config);
|
|
2365
|
+
result += this.renderAnnotationAssignment(obj[name], name, env.withSubPath([ name ]), config);
|
|
1884
2366
|
}
|
|
1885
2367
|
return result;
|
|
1886
2368
|
}
|
|
@@ -1896,7 +2378,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1896
2378
|
* @param {object} [config] parentheses: Whether the annotation assignment must be surrounded by parentheses.
|
|
1897
2379
|
* @return {string} Rendered annotation, possibly quoted: `@![A.B.C#foo.C]: value`
|
|
1898
2380
|
*/
|
|
1899
|
-
|
|
2381
|
+
renderAnnotationAssignment( anno, name, env, config = { parentheses: false } ) {
|
|
1900
2382
|
name = name.substring(1);
|
|
1901
2383
|
// Take the annotation assignment apart into <nameBeforeVariant>#<variantAndRest>
|
|
1902
2384
|
const parts = name.split('#');
|
|
@@ -1912,14 +2394,14 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1912
2394
|
if (variant === '')
|
|
1913
2395
|
nameBeforeVariant += '#';
|
|
1914
2396
|
|
|
1915
|
-
result += quoteAnnotationPathIfRequired(nameBeforeVariant, env);
|
|
2397
|
+
result += this.quoteAnnotationPathIfRequired(nameBeforeVariant, env);
|
|
1916
2398
|
|
|
1917
2399
|
if (variant !== undefined && variant !== '') {
|
|
1918
2400
|
// Unfortunately, the compiler does not allow `.@` after the first variant identifier,
|
|
1919
2401
|
// nor multiple `#`, so we're back at simple paths that are possibly quoted.
|
|
1920
|
-
result += `#${quotePathIfRequired(variant, env)}`;
|
|
2402
|
+
result += `#${this.quotePathIfRequired(variant, env)}`;
|
|
1921
2403
|
}
|
|
1922
|
-
result += ` : ${renderAnnotationValue(anno, env)}`;
|
|
2404
|
+
result += ` : ${this.renderAnnotationValue(anno, env)}`;
|
|
1923
2405
|
|
|
1924
2406
|
if (parentheses)
|
|
1925
2407
|
result += ')';
|
|
@@ -1933,8 +2415,8 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1933
2415
|
* @param {CdlRenderEnvironment} env
|
|
1934
2416
|
* @return {string} Artifact name ready for rendering
|
|
1935
2417
|
*/
|
|
1936
|
-
|
|
1937
|
-
return quotePathIfRequired(artifactName, env);
|
|
2418
|
+
renderArtifactName( artifactName, env ) {
|
|
2419
|
+
return this.quotePathIfRequired(env.nameEnvStack.definitionName(artifactName), env);
|
|
1938
2420
|
}
|
|
1939
2421
|
|
|
1940
2422
|
/**
|
|
@@ -1945,9 +2427,11 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1945
2427
|
* @param {CdlRenderEnvironment} env
|
|
1946
2428
|
* @return {string}
|
|
1947
2429
|
*/
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
2430
|
+
renderDefinitionReference( name, env ) {
|
|
2431
|
+
if (name === '$self' && !this.csn.definitions.$self)
|
|
2432
|
+
return '$self';
|
|
2433
|
+
name = env.nameEnvStack.definitionReference(name);
|
|
2434
|
+
return this.quotePathIfRequired(name, env);
|
|
1951
2435
|
}
|
|
1952
2436
|
|
|
1953
2437
|
/**
|
|
@@ -1955,17 +2439,18 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1955
2439
|
* @param {CdlRenderEnvironment} env
|
|
1956
2440
|
* @return {string}
|
|
1957
2441
|
*/
|
|
1958
|
-
|
|
1959
|
-
return ` : ${includes.map((name, i) => renderDefinitionReference(name, env.withSubPath([ 'includes', i ]))).join(', ')}`;
|
|
2442
|
+
renderIncludes( includes, env ) {
|
|
2443
|
+
return ` : ${includes.map((name, i) => this.renderDefinitionReference(name, env.withSubPath([ 'includes', i ]))).join(', ')}`;
|
|
1960
2444
|
}
|
|
1961
2445
|
|
|
1962
|
-
|
|
2446
|
+
createCdlExpressionRenderer() {
|
|
2447
|
+
const that = this;
|
|
1963
2448
|
return createExpressionRenderer({
|
|
1964
2449
|
finalize: x => x,
|
|
1965
2450
|
typeCast(x) {
|
|
1966
|
-
const typeRef = renderTypeReferenceAndProps(x.cast, this.env.withSubPath([ 'cast' ]), { typeRefOnly: true, noAnnoCollect: true });
|
|
2451
|
+
const typeRef = that.renderTypeReferenceAndProps(x.cast, this.env.withSubPath([ 'cast' ]), { typeRefOnly: true, noAnnoCollect: true });
|
|
1967
2452
|
const arg = { ...x, cast: null }; // "arg" without cast to avoid recursion.
|
|
1968
|
-
return `cast(${renderArgument(arg, this.env)} as ${typeRef})`;
|
|
2453
|
+
return `cast(${that.renderArgument(arg, this.env)} as ${typeRef})`;
|
|
1969
2454
|
},
|
|
1970
2455
|
val(x) {
|
|
1971
2456
|
// Literal value, possibly with explicit 'literal' property
|
|
@@ -1992,7 +2477,7 @@ function csnToCdl( csn, options, msg ) {
|
|
|
1992
2477
|
aliasOnly: x => x.as,
|
|
1993
2478
|
enum: x => `#${x['#']}`,
|
|
1994
2479
|
ref(x) {
|
|
1995
|
-
return `${x.param ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.')}`;
|
|
2480
|
+
return `${x.param ? ':' : ''}${x.ref.map((step, index) => that.renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.')}`;
|
|
1996
2481
|
},
|
|
1997
2482
|
windowFunction(x) {
|
|
1998
2483
|
const funcDef = this.func(x);
|
|
@@ -2001,10 +2486,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2001
2486
|
func(x) {
|
|
2002
2487
|
if (keywords.cdl_functions.includes(x.func.toUpperCase()) && !x.args)
|
|
2003
2488
|
return x.func;
|
|
2004
|
-
const name = quoteFunctionIfRequired(x.func, this.env);
|
|
2489
|
+
const name = that.quoteFunctionIfRequired(x.func, this.env);
|
|
2005
2490
|
if (!x.args) // e.g. for methods without arguments, `args` is not set at all.
|
|
2006
2491
|
return `${name}`;
|
|
2007
|
-
return `${name}(${renderArguments( x, '=>', this.env )})`;
|
|
2492
|
+
return `${name}(${that.renderArguments( x, '=>', this.env )})`;
|
|
2008
2493
|
},
|
|
2009
2494
|
xpr(x) {
|
|
2010
2495
|
const xprEnv = this.env.withSubPath([ 'xpr' ]);
|
|
@@ -2017,10 +2502,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2017
2502
|
// For example: `select from E where id in (select from E union select from E);`:
|
|
2018
2503
|
// Without parentheses, it would be different query.
|
|
2019
2504
|
SET(x) {
|
|
2020
|
-
return `(${renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
2505
|
+
return `(${that.renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
2021
2506
|
},
|
|
2022
2507
|
SELECT(x) {
|
|
2023
|
-
return `(${renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
2508
|
+
return `(${that.renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
2024
2509
|
},
|
|
2025
2510
|
});
|
|
2026
2511
|
}
|
|
@@ -2040,22 +2525,22 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2040
2525
|
* @param {CSN.Artifact} art
|
|
2041
2526
|
* @param {CdlRenderEnvironment} env
|
|
2042
2527
|
*/
|
|
2043
|
-
|
|
2528
|
+
checkArrayedArtifact( art, env ) {
|
|
2044
2529
|
if (!art.items)
|
|
2045
2530
|
return;
|
|
2046
2531
|
if (art.notNull !== undefined) {
|
|
2047
|
-
msg.warning('def-unexpected-nullability', env.path, { prop: 'not null', otherprop: 'items' },
|
|
2048
|
-
|
|
2532
|
+
this.msg.warning('def-unexpected-nullability', env.path, { prop: 'not null', otherprop: 'items' },
|
|
2533
|
+
'Property $(PROP) not rendered, because it can only be rendered inside $(OTHERPROP) for arrayed artifacts');
|
|
2049
2534
|
}
|
|
2050
2535
|
|
|
2051
2536
|
if (art.items.items && !art.items.type)
|
|
2052
|
-
msg.message('type-invalid-items', [ ...env.path, 'items' ], { '#': 'nested', prop: 'items' } );
|
|
2537
|
+
this.msg.message('type-invalid-items', [ ...env.path, 'items' ], { '#': 'nested', prop: 'items' } );
|
|
2053
2538
|
|
|
2054
2539
|
const type = art.items.type && normalizeTypeRef(art.items.type);
|
|
2055
2540
|
if (type === 'cds.Association' || type === 'cds.Composition') {
|
|
2056
2541
|
// check for `art.items.target` not sufficient; could be indirect type reference
|
|
2057
2542
|
const isComp = type === 'cds.Composition';
|
|
2058
|
-
msg.message('type-invalid-items', [ ...env.path, 'items' ], { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
2543
|
+
this.msg.message('type-invalid-items', [ ...env.path, 'items' ], { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
2059
2544
|
}
|
|
2060
2545
|
}
|
|
2061
2546
|
|
|
@@ -2073,12 +2558,12 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2073
2558
|
* @param {CdlRenderEnvironment} env
|
|
2074
2559
|
* @returns {string}
|
|
2075
2560
|
*/
|
|
2076
|
-
|
|
2561
|
+
quotePathIfRequired( path, env ) {
|
|
2077
2562
|
return path.split('.').map((step, index) => {
|
|
2078
2563
|
if (index === 0)
|
|
2079
|
-
return quoteNonIdentifierOrKeyword(step, env);
|
|
2564
|
+
return this.quoteNonIdentifierOrKeyword(step, env);
|
|
2080
2565
|
else if (!undelimitedIdentifierRegex.test(step))
|
|
2081
|
-
return delimitedId(step, env);
|
|
2566
|
+
return this.delimitedId(step, env);
|
|
2082
2567
|
return step;
|
|
2083
2568
|
}).join('.');
|
|
2084
2569
|
}
|
|
@@ -2095,10 +2580,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2095
2580
|
* @param {CdlRenderEnvironment} env
|
|
2096
2581
|
* @return {string}
|
|
2097
2582
|
*/
|
|
2098
|
-
|
|
2583
|
+
quoteNonIdentifierOrKeyword( id, env ) {
|
|
2099
2584
|
// Quote if required for CDL
|
|
2100
2585
|
if (requiresQuotingForCdl(id, env?.additionalKeywords || []))
|
|
2101
|
-
return delimitedId(id, env);
|
|
2586
|
+
return this.delimitedId(id, env);
|
|
2102
2587
|
return id;
|
|
2103
2588
|
}
|
|
2104
2589
|
|
|
@@ -2114,9 +2599,9 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2114
2599
|
* @param {CdlRenderEnvironment} env
|
|
2115
2600
|
* @return {string}
|
|
2116
2601
|
*/
|
|
2117
|
-
|
|
2602
|
+
quoteNonIdentifier( id, env ) {
|
|
2118
2603
|
if (!undelimitedIdentifierRegex.test(id))
|
|
2119
|
-
return delimitedId(id, env);
|
|
2604
|
+
return this.delimitedId(id, env);
|
|
2120
2605
|
return id;
|
|
2121
2606
|
}
|
|
2122
2607
|
|
|
@@ -2127,10 +2612,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2127
2612
|
* @param {CdlRenderEnvironment} env
|
|
2128
2613
|
* @return {string}
|
|
2129
2614
|
*/
|
|
2130
|
-
|
|
2615
|
+
quoteFunctionIfRequired( funcName, env ) {
|
|
2131
2616
|
if (cdlNewLineRegEx.test(funcName)) {
|
|
2132
|
-
msg.error('name-invalid-identifier', env.path, {},
|
|
2133
|
-
|
|
2617
|
+
this.msg.error('name-invalid-identifier', env.path, {},
|
|
2618
|
+
'An identifier can\'t contain newline characters in CDL');
|
|
2134
2619
|
}
|
|
2135
2620
|
return apiSmartFunctionId(funcName);
|
|
2136
2621
|
}
|
|
@@ -2145,11 +2630,11 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2145
2630
|
* @param {CdlRenderEnvironment} env
|
|
2146
2631
|
* @returns {string}
|
|
2147
2632
|
*/
|
|
2148
|
-
|
|
2633
|
+
quoteAnnotationPathIfRequired( anno, env ) {
|
|
2149
2634
|
return anno.split('.').map((segment) => {
|
|
2150
2635
|
if (segment.startsWith('@'))
|
|
2151
|
-
return `@${quoteNonIdentifier(segment.slice(1), env)}`;
|
|
2152
|
-
return quoteNonIdentifier(segment, env);
|
|
2636
|
+
return `@${this.quoteNonIdentifier(segment.slice(1), env)}`;
|
|
2637
|
+
return this.quoteNonIdentifier(segment, env);
|
|
2153
2638
|
}).join('.');
|
|
2154
2639
|
}
|
|
2155
2640
|
|
|
@@ -2161,10 +2646,10 @@ function csnToCdl( csn, options, msg ) {
|
|
|
2161
2646
|
* @param {CdlRenderEnvironment} env
|
|
2162
2647
|
* @return {string}
|
|
2163
2648
|
*/
|
|
2164
|
-
|
|
2649
|
+
delimitedId( id, env ) {
|
|
2165
2650
|
if (cdlNewLineRegEx.test(id)) {
|
|
2166
|
-
msg.error('name-invalid-identifier', env.path, {},
|
|
2167
|
-
|
|
2651
|
+
this.msg.error('name-invalid-identifier', env.path, {},
|
|
2652
|
+
'An identifier can\'t contain newline characters in CDL');
|
|
2168
2653
|
}
|
|
2169
2654
|
return apiDelimitedId(id);
|
|
2170
2655
|
}
|
|
@@ -2175,6 +2660,7 @@ class CdlRenderEnvironment {
|
|
|
2175
2660
|
path = null;
|
|
2176
2661
|
elementName = null;
|
|
2177
2662
|
additionalKeywords = [];
|
|
2663
|
+
nameEnvStack = new NameScopeStack();
|
|
2178
2664
|
|
|
2179
2665
|
constructor(values) {
|
|
2180
2666
|
Object.assign(this, values);
|
|
@@ -2184,10 +2670,11 @@ class CdlRenderEnvironment {
|
|
|
2184
2670
|
this.indent = ` ${this.indent}`;
|
|
2185
2671
|
}
|
|
2186
2672
|
decreaseIndent() {
|
|
2187
|
-
this.indent = this.indent.substring(0, this.indent.length -
|
|
2673
|
+
this.indent = this.indent.substring(0, this.indent.length - INDENT_SIZE);
|
|
2188
2674
|
}
|
|
2189
2675
|
withIncreasedIndent() {
|
|
2190
|
-
|
|
2676
|
+
const indent = ' '.repeat(this.indent.length + INDENT_SIZE);
|
|
2677
|
+
return new CdlRenderEnvironment({ ...this, indent });
|
|
2191
2678
|
}
|
|
2192
2679
|
withSubPath(path) {
|
|
2193
2680
|
return new CdlRenderEnvironment({ ...this, path: [ ...this.path, ...path ] });
|
|
@@ -2258,8 +2745,8 @@ const conditionOperators = [
|
|
|
2258
2745
|
// Antlr rule 'condition', 'conditionAnd'
|
|
2259
2746
|
'AND', 'OR',
|
|
2260
2747
|
|
|
2261
|
-
//
|
|
2262
|
-
'=', '<>', '>', '>=', '<', '<=', '!=',
|
|
2748
|
+
// redepage CdlGrammar.g4 rule 'expression'
|
|
2749
|
+
'=', '<>', '>', '>=', '<', '<=', '==', '!=',
|
|
2263
2750
|
// These are not forbidden, since they must be preceded by one of the comparators above.
|
|
2264
2751
|
// 'any', 'some', 'all',
|
|
2265
2752
|
|
|
@@ -2435,20 +2922,6 @@ function isSimpleString( str ) {
|
|
|
2435
2922
|
!hasUnpairedUnicodeSurrogate(str));
|
|
2436
2923
|
}
|
|
2437
2924
|
|
|
2438
|
-
/**
|
|
2439
|
-
* Get a list of top-level artifact names, which are not in contexts/usings/, i.e. those
|
|
2440
|
-
* before the first dot ('.'). For example for `S.E.F`, `S` is used.
|
|
2441
|
-
*
|
|
2442
|
-
* @param {CSN.Model} csn
|
|
2443
|
-
* @return {string[]}
|
|
2444
|
-
*/
|
|
2445
|
-
function availableFirstPathSteps( csn ) {
|
|
2446
|
-
if (!csn.definitions)
|
|
2447
|
-
return [];
|
|
2448
|
-
const unique = new Set(Object.keys(csn.definitions).map(name => name.split('.')[0]));
|
|
2449
|
-
return Array.from(unique);
|
|
2450
|
-
}
|
|
2451
|
-
|
|
2452
2925
|
/**
|
|
2453
2926
|
* Quotes the identifier using CDL-style ![]-quotes.
|
|
2454
2927
|
*
|
|
@@ -2503,6 +2976,21 @@ function apiSmartFunctionId( funcName ) {
|
|
|
2503
2976
|
return funcName;
|
|
2504
2977
|
}
|
|
2505
2978
|
|
|
2979
|
+
/**
|
|
2980
|
+
* Render the CSN model 'model' to CDS source text.
|
|
2981
|
+
* Returned object has the following properties:
|
|
2982
|
+
* - `model`: CSN model rendered as CDL (string).
|
|
2983
|
+
* - `namespace`: Namespace statement + `using from './model.cds'.
|
|
2984
|
+
*
|
|
2985
|
+
* @param {CSN.Model} csn
|
|
2986
|
+
* @param {CdlOptions} options
|
|
2987
|
+
* @param {object} msg Message Functions
|
|
2988
|
+
*/
|
|
2989
|
+
function csnToCdl( csn, options, msg ) {
|
|
2990
|
+
const renderer = new CsnToCdl(csn, options, msg);
|
|
2991
|
+
return renderer.render();
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2506
2994
|
|
|
2507
2995
|
module.exports = {
|
|
2508
2996
|
csnToCdl,
|