@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/render/toCdl.js
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const keywords = require('../base/keywords');
|
|
4
|
-
const { getLastPartOf } = require('../model/csnUtils');
|
|
5
4
|
const { isBuiltinType, generatedByCompilerVersion, getNormalizedQuery } = require('../model/csnUtils');
|
|
6
|
-
const {
|
|
5
|
+
const { findElement, getExpressionRenderer } = require('./utils/common');
|
|
7
6
|
const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEscapes');
|
|
8
7
|
const { checkCSNVersion } = require('../json/csnVersion');
|
|
9
8
|
const { timetrace } = require('../utils/timetrace');
|
|
10
|
-
const { csnRefs } = require('../model/csnRefs');
|
|
11
9
|
const { forEachDefinition } = require('../model/csnUtils');
|
|
12
10
|
const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
|
|
13
11
|
const { isBetaEnabled } = require('../base/model');
|
|
14
12
|
const { ModelError } = require('../base/error');
|
|
15
|
-
const { typeParameters } = require('../compiler/builtins');
|
|
13
|
+
const { typeParameters, specialFunctions } = require('../compiler/builtins');
|
|
16
14
|
const { forEach } = require('../utils/objectUtils');
|
|
17
15
|
|
|
16
|
+
const identifierRegex = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
|
|
17
|
+
|
|
18
18
|
/**
|
|
19
19
|
* Render the CSN model 'model' to CDS source text.
|
|
20
20
|
* Returned object has the following properties:
|
|
21
21
|
* - `model`: CSN model rendered as CDL (string).
|
|
22
|
-
* -
|
|
22
|
+
* - `namespace`: Namespace statement + `using from './model.cds'.
|
|
23
23
|
* - `unappliedExtensions`: Annotations / Extensions from the `csn.extensions` array.
|
|
24
24
|
*
|
|
25
25
|
* @param {CSN.Model} csn
|
|
26
26
|
* @param {CSN.Options} [options]
|
|
27
27
|
*/
|
|
28
|
-
function
|
|
28
|
+
function csnToCdl(csn, options) {
|
|
29
29
|
timetrace.start('CDL rendering');
|
|
30
|
-
const { artifactRef } = csnRefs(csn);
|
|
31
30
|
let _renderExpr = null;
|
|
32
31
|
|
|
33
32
|
if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
|
|
@@ -48,8 +47,8 @@ function toCdsSourceCsn(csn, options) {
|
|
|
48
47
|
cdlResult.model += renderVocabularies(csn.vocabularies);
|
|
49
48
|
|
|
50
49
|
if (csn.namespace) {
|
|
51
|
-
cdlResult
|
|
52
|
-
cdlResult
|
|
50
|
+
cdlResult.namespace = `namespace ${renderArtifactName(csn.namespace)};\n`;
|
|
51
|
+
cdlResult.namespace += 'using from \'./model.cds\';';
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
// If there are extensions, such as 'extend' and 'annotate' statements, render them separately.
|
|
@@ -93,7 +92,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
93
92
|
if (!anno._ignore) {
|
|
94
93
|
// This environment is passed down the call hierarchy, for dealing with
|
|
95
94
|
// indentation and name resolution issues
|
|
96
|
-
const env = createEnv();
|
|
95
|
+
const env = envNewPath(createEnv(), [ 'vocabularies', name ]);
|
|
97
96
|
const sourceStr = renderTypeOrAnnotation(name, anno, env, 'annotation');
|
|
98
97
|
result += `${sourceStr}\n`;
|
|
99
98
|
}
|
|
@@ -143,11 +142,10 @@ function toCdsSourceCsn(csn, options) {
|
|
|
143
142
|
let result = renderAnnotationAssignmentsAndDocComment(ext, env);
|
|
144
143
|
|
|
145
144
|
if (ext.includes && ext.includes.length > 0) {
|
|
146
|
-
// Includes can't be combined with anything in braces {}.
|
|
147
|
-
// are possible through CSN, but in CDL, only one include at once is possible.
|
|
145
|
+
// Includes can't be combined with anything in braces {}.
|
|
148
146
|
const affix = isElementExtend ? 'element ' : '';
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
const includes = ext.includes.map(inc => quotePathIfRequired(inc)).join(', ');
|
|
148
|
+
result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
|
|
151
149
|
return result;
|
|
152
150
|
}
|
|
153
151
|
|
|
@@ -293,7 +291,6 @@ function toCdsSourceCsn(csn, options) {
|
|
|
293
291
|
result += `${env.indent}}`;
|
|
294
292
|
}
|
|
295
293
|
|
|
296
|
-
|
|
297
294
|
result += ';\n';
|
|
298
295
|
return result;
|
|
299
296
|
}
|
|
@@ -348,31 +345,33 @@ function toCdsSourceCsn(csn, options) {
|
|
|
348
345
|
* @param {CdlRenderEnvironment} env
|
|
349
346
|
*/
|
|
350
347
|
function renderArtifact(artifactName, art, env) {
|
|
351
|
-
|
|
352
|
-
env.path = [ 'definitions', artifactName ];
|
|
348
|
+
env = envNewPath(env, [ 'definitions', artifactName ]);
|
|
353
349
|
env.artifactName = artifactName;
|
|
354
350
|
|
|
355
351
|
switch (art.kind) {
|
|
356
352
|
case 'entity':
|
|
357
353
|
if (art.query || art.projection)
|
|
358
354
|
return renderView(artifactName, art, env);
|
|
359
|
-
|
|
360
355
|
return renderEntity(artifactName, art, env);
|
|
361
356
|
|
|
362
357
|
case 'context':
|
|
363
358
|
case 'service':
|
|
364
|
-
return
|
|
359
|
+
return renderContextOrService(artifactName, art, env);
|
|
360
|
+
|
|
365
361
|
case 'type':
|
|
366
362
|
case 'aspect':
|
|
367
|
-
case 'annotation':
|
|
368
|
-
return renderTypeOrAnnotation(artifactName, art, env
|
|
363
|
+
case 'annotation': // annotation in 'csn.definitions' for compiler v1 compatibility
|
|
364
|
+
return renderTypeOrAnnotation(artifactName, art, env);
|
|
365
|
+
|
|
369
366
|
case 'action':
|
|
370
367
|
case 'function':
|
|
371
368
|
return renderActionOrFunction(artifactName, art, env);
|
|
369
|
+
|
|
372
370
|
case 'event':
|
|
373
371
|
return renderEvent(artifactName, art, env);
|
|
372
|
+
|
|
374
373
|
default:
|
|
375
|
-
throw new ModelError(`Unknown artifact kind: ${art.kind}`);
|
|
374
|
+
throw new ModelError(`to.cdl: Unknown artifact kind: ${art.kind}`);
|
|
376
375
|
}
|
|
377
376
|
}
|
|
378
377
|
|
|
@@ -389,16 +388,14 @@ function toCdsSourceCsn(csn, options) {
|
|
|
389
388
|
if (art.includes)
|
|
390
389
|
result += renderIncludes(art.includes);
|
|
391
390
|
if (art.query || art.projection) {
|
|
392
|
-
env._artifact = art;
|
|
393
391
|
result += ' : ';
|
|
394
392
|
result += renderQuery(getNormalizedQuery(art).query, true, 'projection', env,
|
|
395
393
|
[ 'definitions', artifactName, 'query' ]);
|
|
396
394
|
result += ';\n';
|
|
397
|
-
delete env._artifact;
|
|
398
395
|
}
|
|
399
396
|
else if (art.type) {
|
|
400
397
|
// Derived type or annotation with non-anonymous type
|
|
401
|
-
result += ` : ${
|
|
398
|
+
result += ` : ${renderTypeReferenceAndProps(art, env)};\n`;
|
|
402
399
|
}
|
|
403
400
|
else if (art.elements) {
|
|
404
401
|
result += ' {\n';
|
|
@@ -411,18 +408,14 @@ function toCdsSourceCsn(csn, options) {
|
|
|
411
408
|
}
|
|
412
409
|
|
|
413
410
|
/**
|
|
414
|
-
* Render a context or service. Return the resulting source string.
|
|
415
|
-
*
|
|
416
411
|
* @param {string} artifactName
|
|
417
412
|
* @param {CSN.Artifact} art
|
|
418
413
|
* @param {CdlRenderEnvironment} env
|
|
414
|
+
* @returns {string}
|
|
419
415
|
*/
|
|
420
|
-
function
|
|
416
|
+
function renderContextOrService(artifactName, art, env) {
|
|
421
417
|
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
422
|
-
result += `${env.indent
|
|
423
|
-
if (art.includes)
|
|
424
|
-
result += renderIncludes(art.includes);
|
|
425
|
-
|
|
418
|
+
result += `${env.indent}${art.kind} ${renderArtifactName(artifactName)}`;
|
|
426
419
|
return `${result} {};\n`;
|
|
427
420
|
}
|
|
428
421
|
|
|
@@ -436,28 +429,18 @@ function toCdsSourceCsn(csn, options) {
|
|
|
436
429
|
*/
|
|
437
430
|
function renderEntity(artifactName, art, env) {
|
|
438
431
|
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
432
|
+
result += env.indent + (art.abstract ? 'abstract ' : '');
|
|
433
|
+
result += `entity ${renderArtifactName(artifactName)}`;
|
|
434
|
+
|
|
435
|
+
if (art.params)
|
|
436
|
+
result += renderParameters(art, env);
|
|
437
|
+
|
|
444
438
|
if (art.includes)
|
|
445
439
|
result += renderIncludes(art.includes);
|
|
446
440
|
result += ' {\n';
|
|
441
|
+
const childEnv = increaseIndent(env);
|
|
447
442
|
for (const name in art.elements) {
|
|
448
443
|
const element = art.elements[name];
|
|
449
|
-
// For subelement annotations, this seems to be a pattern to recognize them
|
|
450
|
-
// plus some other stuff unfortunately...
|
|
451
|
-
if (element.type && element.elements) {
|
|
452
|
-
subelementAnnotates.push({
|
|
453
|
-
annotate: artifactName,
|
|
454
|
-
elements: {
|
|
455
|
-
[name]: {
|
|
456
|
-
elements: element.elements,
|
|
457
|
-
},
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
444
|
result += renderElement(name, element, childEnv);
|
|
462
445
|
}
|
|
463
446
|
|
|
@@ -476,19 +459,15 @@ function toCdsSourceCsn(csn, options) {
|
|
|
476
459
|
* @param {Boolean} [isSubElement]
|
|
477
460
|
*/
|
|
478
461
|
function renderElement(elementName, elm, env, isSubElement) {
|
|
479
|
-
env
|
|
462
|
+
env = envAddPath(env, [ 'elements', elementName ]);
|
|
480
463
|
let result = renderAnnotationAssignmentsAndDocComment(elm, env);
|
|
481
|
-
|
|
482
|
-
result +=
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (elm.default)
|
|
489
|
-
result += ` default ${renderExpr(elm.default, env)}`;
|
|
490
|
-
|
|
491
|
-
delete env.elementName;
|
|
464
|
+
result += env.indent;
|
|
465
|
+
result += elm.virtual ? 'virtual ' : '';
|
|
466
|
+
result += elm.key && !isSubElement ? 'key ' : '';
|
|
467
|
+
// TODO(v4): Remove once deprecated flag for `masked` is removed.
|
|
468
|
+
result += elm.masked ? 'masked ' : '';
|
|
469
|
+
result += `${quoteIdIfRequired(elementName)} : ${renderTypeReferenceAndProps(elm, env)}`;
|
|
470
|
+
|
|
492
471
|
return `${result};\n`;
|
|
493
472
|
}
|
|
494
473
|
|
|
@@ -529,7 +508,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
529
508
|
* @return {string}
|
|
530
509
|
*/
|
|
531
510
|
function renderQueryElementAnnotations(artifactName, art, env) {
|
|
532
|
-
const annotate = collectAnnotationsOfElements(artifactName,
|
|
511
|
+
const annotate = collectAnnotationsOfElements(art, { artifactName, path: env.path });
|
|
533
512
|
if (annotate)
|
|
534
513
|
return renderExtensions([ annotate ], env);
|
|
535
514
|
return '';
|
|
@@ -539,13 +518,40 @@ function toCdsSourceCsn(csn, options) {
|
|
|
539
518
|
* Create an "annotate" statement as a CSN extension for all annotations of (sub-)elements.
|
|
540
519
|
* If no annotation was found, we return `null`.
|
|
541
520
|
*
|
|
542
|
-
* @param {string} artifactName
|
|
543
521
|
* @param {CSN.Artifact} artWithElements
|
|
522
|
+
* @param {CdlRenderEnvironment} env
|
|
544
523
|
* @return {CSN.Extension|null}
|
|
545
524
|
*/
|
|
546
|
-
function collectAnnotationsOfElements(
|
|
547
|
-
|
|
548
|
-
|
|
525
|
+
function collectAnnotationsOfElements(artWithElements, env) {
|
|
526
|
+
// Array of structures, which may be annotated as well.
|
|
527
|
+
if (!artWithElements.elements && artWithElements.items) {
|
|
528
|
+
env = envAddPath(env, [ 'items' ]);
|
|
529
|
+
artWithElements = artWithElements.items;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const annotate = { annotate: env.path[1] };
|
|
533
|
+
|
|
534
|
+
// Based on the current path, create a correctly nested structure
|
|
535
|
+
// of elements for which we collect annotations.
|
|
536
|
+
// TODO: More properties?
|
|
537
|
+
let obj = annotate;
|
|
538
|
+
for (let i = 2; i < env.path.length; ++i) {
|
|
539
|
+
const key = env.path[i];
|
|
540
|
+
if (key === 'elements' || key === 'actions') {
|
|
541
|
+
obj[key] = Object.create(null);
|
|
542
|
+
const elem = env.path[i + 1];
|
|
543
|
+
obj[key][elem] = {};
|
|
544
|
+
obj = obj[key][elem];
|
|
545
|
+
}
|
|
546
|
+
else if (key === 'returns') {
|
|
547
|
+
obj.returns = {};
|
|
548
|
+
obj = obj.returns;
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
// ignore others, e.g. 'items'
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return collectAnnos(obj, artWithElements) ? annotate : null;
|
|
549
555
|
|
|
550
556
|
/**
|
|
551
557
|
* Recursive function to collect annotations. `annotateObj` will get an `elements`
|
|
@@ -663,8 +669,11 @@ function toCdsSourceCsn(csn, options) {
|
|
|
663
669
|
if (path.ref[0].args)
|
|
664
670
|
result += `(${renderArgs(path.ref[0], ':', env)})`;
|
|
665
671
|
|
|
666
|
-
if (path.ref[0].where)
|
|
667
|
-
|
|
672
|
+
if (path.ref[0].where) {
|
|
673
|
+
const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
|
|
674
|
+
const expr = renderExpr(path.ref[0].where, env, true, true);
|
|
675
|
+
result += `[${cardinality}${expr}]`;
|
|
676
|
+
}
|
|
668
677
|
|
|
669
678
|
// Add any path steps (possibly with parameters and filters) that may follow after that
|
|
670
679
|
if (path.ref.length > 1)
|
|
@@ -749,7 +758,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
749
758
|
if (col.cast.target && !col.cast.type)
|
|
750
759
|
result += ` : ${renderRedirectedTo(col.cast, env)}`;
|
|
751
760
|
else
|
|
752
|
-
result += ` : ${
|
|
761
|
+
result += ` : ${renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true })}`;
|
|
753
762
|
}
|
|
754
763
|
return result;
|
|
755
764
|
}
|
|
@@ -758,58 +767,51 @@ function toCdsSourceCsn(csn, options) {
|
|
|
758
767
|
* For the current column, render a (nested) inline/expand. If the current column
|
|
759
768
|
* does not have an .expand/.inline, '' is returned
|
|
760
769
|
*
|
|
761
|
-
* @param {object}
|
|
762
|
-
* @param {CdlRenderEnvironment}
|
|
770
|
+
* @param {object} obj Thing with .expand or .inline
|
|
771
|
+
* @param {CdlRenderEnvironment} env
|
|
763
772
|
* @returns {string}
|
|
764
773
|
*/
|
|
765
|
-
function renderInlineExpand(
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
if (obj.cast && obj.cast.type)
|
|
782
|
-
result += ` : ${renderTypeReference(obj.cast, createEnv())}`;
|
|
783
|
-
else if (obj.cast && obj.cast.target) // test tbd
|
|
784
|
-
result += ` : ${renderRedirectedTo(obj.cast, env)}`;
|
|
785
|
-
return result;
|
|
786
|
-
}
|
|
774
|
+
function renderInlineExpand(obj, env) {
|
|
775
|
+
// No expression to render for { * } as alias
|
|
776
|
+
let result = (obj.as && obj.expand && !obj.ref) ? '' : renderExpr(obj, env);
|
|
777
|
+
|
|
778
|
+
// s as alias { * }
|
|
779
|
+
if (obj.as && (obj.ref || obj.xpr || obj.val !== undefined || obj.func !== undefined))
|
|
780
|
+
result += ` as ${obj.as}`;
|
|
781
|
+
|
|
782
|
+
// We found a leaf - no further drilling
|
|
783
|
+
if (!obj.inline && !obj.expand) {
|
|
784
|
+
if (obj.cast && obj.cast.type)
|
|
785
|
+
result += ` : ${renderTypeReferenceAndProps(obj.cast, createEnv(), { noAnnoCollect: true })}`;
|
|
786
|
+
else if (obj.cast && obj.cast.target) // test tbd
|
|
787
|
+
result += ` : ${renderRedirectedTo(obj.cast, env)}`;
|
|
788
|
+
return result;
|
|
789
|
+
}
|
|
787
790
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
791
|
+
if (obj.inline)
|
|
792
|
+
result += '.{\n';
|
|
793
|
+
else
|
|
794
|
+
result += result !== '' ? ' {\n' : '{\n';
|
|
792
795
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
796
|
+
// Drill down and render children of the expand/inline
|
|
797
|
+
const childEnv = increaseIndent(env);
|
|
798
|
+
const expandInline = obj.expand || obj.inline;
|
|
799
|
+
expandInline.forEach((elm, i) => {
|
|
800
|
+
result += `${childEnv.indent}${renderInlineExpand(elm, childEnv)}`;
|
|
801
|
+
if (i < expandInline.length - 1)
|
|
802
|
+
result += ',\n';
|
|
803
|
+
});
|
|
804
|
+
result += `\n${env.indent}}`;
|
|
802
805
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
+
// Don't forget about the .excluding
|
|
807
|
+
if (obj.excluding)
|
|
808
|
+
result += ` excluding { ${obj.excluding.join(',')} }`;
|
|
806
809
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
+
// { * } as expand
|
|
811
|
+
if (!obj.ref && obj.as)
|
|
812
|
+
result += ` as ${obj.as}`;
|
|
810
813
|
|
|
811
|
-
|
|
812
|
-
}
|
|
814
|
+
return result;
|
|
813
815
|
}
|
|
814
816
|
|
|
815
817
|
/**
|
|
@@ -847,15 +849,9 @@ function toCdsSourceCsn(csn, options) {
|
|
|
847
849
|
const syntax = (art.projection) ? 'projection' : 'entity';
|
|
848
850
|
let result = renderAnnotationAssignmentsAndDocComment(art, env);
|
|
849
851
|
result += `${env.indent}${art.abstract ? 'abstract ' : ''}${syntax === 'projection' ? 'entity' : syntax} ${renderArtifactName(artifactName)}`;
|
|
850
|
-
if (art.params)
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
result += `(\n${parameters}\n${env.indent}) as `;
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
result += ' as ';
|
|
857
|
-
}
|
|
858
|
-
env._artifact = art;
|
|
852
|
+
if (art.params)
|
|
853
|
+
result += renderParameters(art, env);
|
|
854
|
+
result += ' as ';
|
|
859
855
|
result += renderQuery(getNormalizedQuery(art).query, true, syntax, env, [ 'definitions', artifactName, 'query' ], art.elements);
|
|
860
856
|
result += ';\n';
|
|
861
857
|
result += renderQueryElementAnnotations(artifactName, art, env);
|
|
@@ -955,7 +951,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
955
951
|
}
|
|
956
952
|
|
|
957
953
|
/**
|
|
958
|
-
* Render a query's LIMIT clause, which may
|
|
954
|
+
* Render a query's LIMIT clause, which may also have OFFSET.
|
|
959
955
|
*
|
|
960
956
|
* @param {CSN.QueryLimit} limit
|
|
961
957
|
* @param {CdlRenderEnvironment} limitEnv
|
|
@@ -966,9 +962,10 @@ function toCdsSourceCsn(csn, options) {
|
|
|
966
962
|
if (limit.rows !== undefined)
|
|
967
963
|
limitStr += `limit ${renderExpr(limit.rows, limitEnv)}`;
|
|
968
964
|
|
|
969
|
-
if (limit.offset !== undefined)
|
|
970
|
-
|
|
971
|
-
|
|
965
|
+
if (limit.offset !== undefined) {
|
|
966
|
+
const offsetIndent = (limitStr === '') ? '' : `\n${increaseIndent(limitEnv).indent}`;
|
|
967
|
+
limitStr += `${offsetIndent}offset ${renderExpr(limit.offset, limitEnv)}`;
|
|
968
|
+
}
|
|
972
969
|
return limitStr;
|
|
973
970
|
}
|
|
974
971
|
|
|
@@ -1029,7 +1026,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1029
1026
|
let result = '';
|
|
1030
1027
|
const childEnv = increaseIndent(env);
|
|
1031
1028
|
for (const name in art.actions)
|
|
1032
|
-
result += renderActionOrFunction(name, art.actions[name], childEnv);
|
|
1029
|
+
result += renderActionOrFunction(name, art.actions[name], envAddPath(childEnv, [ 'actions', name ]));
|
|
1033
1030
|
|
|
1034
1031
|
// Even if we have seen actions/functions, they might all have been ignored
|
|
1035
1032
|
if (result !== '')
|
|
@@ -1047,30 +1044,35 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1047
1044
|
* @return {string}
|
|
1048
1045
|
*/
|
|
1049
1046
|
function renderActionOrFunction(actionName, act, env) {
|
|
1050
|
-
let result =
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
|
|
1047
|
+
let result = renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
|
|
1048
|
+
result += ` ${renderArtifactName(actionName)}`;
|
|
1049
|
+
result += renderParameters(act, env);
|
|
1054
1050
|
if (act.returns) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
const isBoundAction = (env.artifactName !== actionName);
|
|
1058
|
-
const anno = {
|
|
1059
|
-
annotate: env.artifactName || actionName,
|
|
1060
|
-
};
|
|
1061
|
-
if (isBoundAction)
|
|
1062
|
-
anno.actions = { [actionName]: { returns: { elements: act.returns.elements } } };
|
|
1063
|
-
else
|
|
1064
|
-
anno.returns = { elements: act.returns.elements };
|
|
1065
|
-
subelementAnnotates.push(anno);
|
|
1066
|
-
}
|
|
1067
|
-
result += ` returns ${renderTypeReference(act.returns, env)}${renderNullability(act.returns)}`;
|
|
1051
|
+
const actEnv = envAddPath(env, [ 'returns' ]);
|
|
1052
|
+
result += ` returns ${renderTypeReferenceAndProps(act.returns, actEnv)}`;
|
|
1068
1053
|
}
|
|
1069
1054
|
|
|
1070
1055
|
result += ';\n';
|
|
1071
1056
|
return result;
|
|
1072
1057
|
}
|
|
1073
1058
|
|
|
1059
|
+
/**
|
|
1060
|
+
* Render art.params, i.e. list of parameter in parentheses. If there is only one
|
|
1061
|
+
* parameter, a single line is used, otherwise an indented list is used.
|
|
1062
|
+
* If there are no params, an empty list `()` is returned.
|
|
1063
|
+
*
|
|
1064
|
+
* @param {CSN.Artifact} art
|
|
1065
|
+
* @param {CdlRenderEnvironment} env
|
|
1066
|
+
* @returns {string}
|
|
1067
|
+
*/
|
|
1068
|
+
function renderParameters(art, env) {
|
|
1069
|
+
const childEnv = increaseIndent(env);
|
|
1070
|
+
const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv));
|
|
1071
|
+
if (parameters.length === 0)
|
|
1072
|
+
return '()';
|
|
1073
|
+
return `(\n${parameters.join(',\n')}\n${env.indent})`;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1074
1076
|
/**
|
|
1075
1077
|
* Render an action or function parameter 'par' with name 'parName'. Return the resulting source string (no trailing LF).
|
|
1076
1078
|
*
|
|
@@ -1080,11 +1082,9 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1080
1082
|
* @return {string}
|
|
1081
1083
|
*/
|
|
1082
1084
|
function renderParameter(parName, par, env) {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
result += renderNullability(par);
|
|
1085
|
+
env = envAddPath(env, [ 'params', parName ]);
|
|
1086
|
+
let result = `${renderAnnotationAssignmentsAndDocComment(par, env)}${env.indent}`;
|
|
1087
|
+
result += `${quoteIdIfRequired(parName)} : ${renderTypeReferenceAndProps(par, env)}`;
|
|
1088
1088
|
return result;
|
|
1089
1089
|
}
|
|
1090
1090
|
|
|
@@ -1095,7 +1095,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1095
1095
|
* @param {string} artifactName
|
|
1096
1096
|
* @param {CSN.Artifact} art
|
|
1097
1097
|
* @param {CdlRenderEnvironment} env
|
|
1098
|
-
* @param {String} artType - used for rendering csn.vocabularies, as the annotations there do not have a kind.
|
|
1098
|
+
* @param {String} [artType] - used for rendering csn.vocabularies, as the annotations there do not have a kind.
|
|
1099
1099
|
* @return {string}
|
|
1100
1100
|
*/
|
|
1101
1101
|
function renderTypeOrAnnotation(artifactName, art, env, artType) {
|
|
@@ -1104,96 +1104,73 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1104
1104
|
if (art.includes)
|
|
1105
1105
|
result += renderIncludes(art.includes);
|
|
1106
1106
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
result +=
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
result += `${env.indent}};\n`;
|
|
1115
|
-
}
|
|
1116
|
-
else {
|
|
1117
|
-
// Derived type or annotation with non-anonymous type
|
|
1118
|
-
result += ` : ${renderTypeReference(art, env, false)}`;
|
|
1119
|
-
if (art.default)
|
|
1120
|
-
result += ` default ${renderExpr(art.default, env)}`;
|
|
1121
|
-
result += ';\n';
|
|
1122
|
-
}
|
|
1107
|
+
if (!art.type && art.elements) // For nicer output, no colon if unnamed structure is used.
|
|
1108
|
+
result += ` ${renderTypeReferenceAndProps(art, env)}`;
|
|
1109
|
+
else
|
|
1110
|
+
result += ` : ${renderTypeReferenceAndProps(art, env)}`;
|
|
1111
|
+
// for aspects, but since types don't have `actions` this does not hurt
|
|
1112
|
+
result += `${renderActionsAndFunctions(art, env)};\n`;
|
|
1123
1113
|
return result;
|
|
1124
1114
|
}
|
|
1125
1115
|
|
|
1126
1116
|
/**
|
|
1127
|
-
* Render a reference to a type used by '
|
|
1128
|
-
*
|
|
1117
|
+
* Render a reference to a type used by 'artifact' (named or inline) and (element) properties
|
|
1118
|
+
* such as `not null` and `default <xpr>`.
|
|
1119
|
+
* Allow suppressing rendering of structs such as enums - used in columns for example.
|
|
1129
1120
|
*
|
|
1130
|
-
* @param {
|
|
1121
|
+
* @param {object} artifact
|
|
1131
1122
|
* @param {CdlRenderEnvironment} env
|
|
1132
|
-
* @param {
|
|
1123
|
+
* @param {object} [config={}] - `typeRefOnly` Whether to only render type defs, no arrayed/structured/enum.
|
|
1124
|
+
* - `noAnnoCollect` Do not collect annotations of sub-elements.
|
|
1133
1125
|
* @return {string}
|
|
1134
1126
|
*/
|
|
1135
|
-
function
|
|
1127
|
+
function renderTypeReferenceAndProps(artifact, env, config = {}) {
|
|
1136
1128
|
let result = '';
|
|
1137
|
-
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
subelementAnnotates.push(annotate);
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
return rc;
|
|
1129
|
+
const { typeRefOnly, noAnnoCollect } = config;
|
|
1130
|
+
let isTypeDef = env.path?.length === 2; // e.g [ 'definitions', typeDef ];
|
|
1131
|
+
|
|
1132
|
+
if (typeRefOnly && !artifact.type)
|
|
1133
|
+
throw new ModelError(`Expected artifact to have a type; in: ${env.artifactName}`);
|
|
1134
|
+
|
|
1135
|
+
if (artifact.localized) // works even for type definitions
|
|
1136
|
+
result += 'localized ';
|
|
1137
|
+
|
|
1138
|
+
if (!artifact.type && artifact.items) {
|
|
1139
|
+
result += 'many '; // alternative: 'array of'; but not used
|
|
1140
|
+
artifact = artifact.items;
|
|
1141
|
+
env = envAddPath(env, 'items');
|
|
1142
|
+
// element keywords allowed in MANY case; was an oversight when arrays were introduced.
|
|
1143
|
+
isTypeDef = false;
|
|
1144
|
+
// "many many" does not work in CDL, so we don't check for it.
|
|
1157
1145
|
}
|
|
1158
1146
|
|
|
1159
|
-
|
|
1160
|
-
result += (elm.localized ? 'localized ' : '');
|
|
1161
|
-
|
|
1162
|
-
// Anonymous structured type
|
|
1163
|
-
if (!elm.type) {
|
|
1164
|
-
if (!elm.elements)
|
|
1165
|
-
throw new ModelError(`Missing type of: ${JSON.stringify(elm)}`);
|
|
1166
|
-
|
|
1147
|
+
if (!artifact.type && artifact.elements) {
|
|
1167
1148
|
result += '{\n';
|
|
1168
|
-
const childEnv = increaseIndent(env);
|
|
1169
|
-
for (const name in
|
|
1170
|
-
result += renderElement(name,
|
|
1149
|
+
const childEnv = envAddPath(increaseIndent(env), 'items');
|
|
1150
|
+
for (const name in artifact.elements)
|
|
1151
|
+
result += renderElement(name, artifact.elements[name], childEnv, null);
|
|
1171
1152
|
|
|
1172
1153
|
result += `${env.indent}}`;
|
|
1154
|
+
if (!isTypeDef)
|
|
1155
|
+
result += renderNullability(artifact);
|
|
1156
|
+
// structured default not possible at the moment
|
|
1173
1157
|
return result;
|
|
1174
1158
|
}
|
|
1175
1159
|
|
|
1176
|
-
const comp = 'cds.Composition';
|
|
1177
1160
|
// Association type
|
|
1178
|
-
if (
|
|
1161
|
+
if (artifact.type === 'cds.Association' || artifact.type === 'cds.Composition') {
|
|
1162
|
+
const isComp = artifact.type === 'cds.Composition';
|
|
1179
1163
|
// Type, cardinality and target; CAPire uses CamelCase
|
|
1180
|
-
result +=
|
|
1181
|
-
|
|
1182
|
-
if (isSimpleCardinality(elm.cardinality)) {
|
|
1183
|
-
result += renderSimpleCardinality(elm);
|
|
1184
|
-
}
|
|
1185
|
-
else {
|
|
1186
|
-
result += renderCardinality(elm.cardinality) +
|
|
1187
|
-
((elm.type === comp) ? ' of ' : ' to ');
|
|
1188
|
-
}
|
|
1164
|
+
result += isComp ? 'Composition' : 'Association';
|
|
1165
|
+
result += renderCardinality(artifact);
|
|
1189
1166
|
|
|
1190
1167
|
// `targetAspect` may be set by the core compiler and refers to the original named or unnamed aspect.
|
|
1191
1168
|
// In parseCdl, `target` may still be an object containing elements. This would be replaced
|
|
1192
1169
|
// by targetAspect in client CSN, but we can't rely on that.
|
|
1193
1170
|
// If a name exists (either in target or targetAspect), prefer it over rendering elements.
|
|
1194
|
-
const elements =
|
|
1195
|
-
if (typeof
|
|
1196
|
-
result += renderAbsolutePath({ ref: [
|
|
1171
|
+
const elements = artifact.target && artifact.target.elements || artifact.targetAspect && artifact.targetAspect.elements;
|
|
1172
|
+
if (typeof artifact.target === 'string' || typeof artifact.targetAspect === 'string') {
|
|
1173
|
+
result += renderAbsolutePath({ ref: [ artifact.target || artifact.targetAspect ] }, env);
|
|
1197
1174
|
}
|
|
1198
1175
|
else if (elements) {
|
|
1199
1176
|
// anonymous aspect, either parseCdl or client CSN.
|
|
@@ -1209,40 +1186,48 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1209
1186
|
}
|
|
1210
1187
|
|
|
1211
1188
|
// ON-condition (if any)
|
|
1212
|
-
if (
|
|
1213
|
-
result += ` on ${renderExpr(
|
|
1189
|
+
if (artifact.on)
|
|
1190
|
+
result += ` on ${renderExpr(artifact.on, env, true, true)}`;
|
|
1214
1191
|
|
|
1215
1192
|
|
|
1216
1193
|
// Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
|
|
1217
|
-
if (
|
|
1218
|
-
result += ` { ${Object.keys(
|
|
1194
|
+
if (artifact.keys && !artifact.on)
|
|
1195
|
+
result += ` { ${Object.keys(artifact.keys).map(name => renderForeignKey(artifact.keys[name], env)).join(', ')} }`;
|
|
1196
|
+
|
|
1197
|
+
if (!isTypeDef && !artifact.on) // unmanaged associations can't be followed by "not null"
|
|
1198
|
+
result += renderNullability(artifact);
|
|
1199
|
+
// DEFAULT not possible here.
|
|
1219
1200
|
|
|
1220
1201
|
return result;
|
|
1221
1202
|
}
|
|
1222
1203
|
|
|
1223
|
-
//
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
else {
|
|
1236
|
-
result += renderAbsolutePath(elm.type, env);
|
|
1237
|
-
}
|
|
1238
|
-
return result;
|
|
1204
|
+
// At this point, we will render a named type.
|
|
1205
|
+
|
|
1206
|
+
// If we have a type and elements, we may have sub-structure annotates that would
|
|
1207
|
+
// get lost if we only render the type name.
|
|
1208
|
+
// TODO: Can we annotate elements of targetAspect?
|
|
1209
|
+
// If so, move this block before the composition rendering.
|
|
1210
|
+
if (!noAnnoCollect && (artifact.elements || artifact.items?.elements)) {
|
|
1211
|
+
const annotate = collectAnnotationsOfElements(artifact, env);
|
|
1212
|
+
if (annotate)
|
|
1213
|
+
subelementAnnotates.push(annotate);
|
|
1239
1214
|
}
|
|
1240
1215
|
|
|
1241
|
-
//
|
|
1242
|
-
|
|
1216
|
+
// Reference to another artifact
|
|
1217
|
+
if (typeof artifact.type === 'string') {
|
|
1218
|
+
// If we get here, it must be a named type
|
|
1219
|
+
result += renderNamedTypeWithParameters(artifact);
|
|
1220
|
+
}
|
|
1221
|
+
else if (artifact.type?.ref) {
|
|
1222
|
+
result += renderAbsolutePath(artifact.type, env);
|
|
1223
|
+
}
|
|
1243
1224
|
|
|
1244
|
-
if (
|
|
1245
|
-
result += renderEnum(
|
|
1225
|
+
if (artifact.enum && !typeRefOnly)
|
|
1226
|
+
result += renderEnum(artifact.enum, env);
|
|
1227
|
+
if (!isTypeDef) // NOT NULL not possible for not-arrayed type definitions
|
|
1228
|
+
result += renderNullability(artifact);
|
|
1229
|
+
if (artifact.default)
|
|
1230
|
+
result += ` default ${renderExpr(artifact.default, env)}`;
|
|
1246
1231
|
|
|
1247
1232
|
return result;
|
|
1248
1233
|
}
|
|
@@ -1255,7 +1240,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1255
1240
|
* @return {string}
|
|
1256
1241
|
*/
|
|
1257
1242
|
function renderRedirectedTo(art, env) {
|
|
1258
|
-
let result = `redirected to ${
|
|
1243
|
+
let result = `redirected to ${quotePathIfRequired(art.target)}`;
|
|
1259
1244
|
if (art.on)
|
|
1260
1245
|
result += ` on ${renderExpr(art.on, env, true, true)}`;
|
|
1261
1246
|
else if (art.keys)
|
|
@@ -1280,8 +1265,6 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1280
1265
|
result += artWithType.type.slice(4);
|
|
1281
1266
|
}
|
|
1282
1267
|
else {
|
|
1283
|
-
// Simple absolute name
|
|
1284
|
-
// Type names are never flattened (derived types are unraveled in HANA)
|
|
1285
1268
|
result += renderArtifactName(artWithType.type);
|
|
1286
1269
|
}
|
|
1287
1270
|
|
|
@@ -1346,7 +1329,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1346
1329
|
}
|
|
1347
1330
|
// Shorthand for absolute path (as string)
|
|
1348
1331
|
else if (x['=']) {
|
|
1349
|
-
return
|
|
1332
|
+
return quotePathIfRequired(x['=']);
|
|
1350
1333
|
}
|
|
1351
1334
|
// Shorthand for ellipsis: `... up to <val>`
|
|
1352
1335
|
else if (x['...']) {
|
|
@@ -1355,11 +1338,15 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1355
1338
|
return `... up to ${renderAnnotationValue(x['...'], env)}`;
|
|
1356
1339
|
}
|
|
1357
1340
|
|
|
1358
|
-
// Struct value (can
|
|
1359
|
-
|
|
1360
|
-
//
|
|
1361
|
-
|
|
1362
|
-
|
|
1341
|
+
// Struct value (can currently only occur within an array)
|
|
1342
|
+
// Render as one-liner if there is at most one key. Render as multi-line
|
|
1343
|
+
// struct if there are more and use nicer indentation.
|
|
1344
|
+
const keys = Object.keys(x);
|
|
1345
|
+
const childEnv = keys.length <= 1 ? env : increaseIndent(env);
|
|
1346
|
+
const values = keys.map(key => `${quoteAnnotationPathIfRequired(key)}: ${renderAnnotationValue(x[key], childEnv)}`);
|
|
1347
|
+
if (values.length <= 1)
|
|
1348
|
+
return `{ ${values.join(', ')} }`;
|
|
1349
|
+
return `{\n${childEnv.indent}${values.join(`,\n${childEnv.indent}`)}\n${env.indent}}`;
|
|
1363
1350
|
}
|
|
1364
1351
|
// Null
|
|
1365
1352
|
else if (x === null) {
|
|
@@ -1408,7 +1395,9 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1408
1395
|
}
|
|
1409
1396
|
if (s.where) {
|
|
1410
1397
|
// Filter, possibly with cardinality
|
|
1411
|
-
|
|
1398
|
+
const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
|
|
1399
|
+
const expr = renderExpr(s.where, env, inline, true);
|
|
1400
|
+
result += `[${cardinality}${expr}]`;
|
|
1412
1401
|
}
|
|
1413
1402
|
|
|
1414
1403
|
return result;
|
|
@@ -1430,12 +1419,18 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1430
1419
|
const args = node.args || [];
|
|
1431
1420
|
|
|
1432
1421
|
// Positional arguments
|
|
1433
|
-
if (Array.isArray(args))
|
|
1422
|
+
if (Array.isArray(args)) {
|
|
1423
|
+
const func = node.func?.toUpperCase();
|
|
1424
|
+
if (func)
|
|
1425
|
+
return args.map((arg, i) => renderArgument(arg, env, getKeywordsForSpecialFunctionArgument(func, i))).join(', ');
|
|
1426
|
+
|
|
1434
1427
|
return args.map(arg => renderArgument(arg, env)).join(', ');
|
|
1428
|
+
}
|
|
1435
1429
|
|
|
1436
1430
|
// Named arguments (object/dict)
|
|
1437
|
-
else if (typeof args === 'object')
|
|
1431
|
+
else if (typeof args === 'object') {
|
|
1438
1432
|
return Object.keys(args).map(key => `${quoteIdIfRequired(key)} ${sep} ${renderArgument(args[key], env)}`).join(', ');
|
|
1433
|
+
}
|
|
1439
1434
|
|
|
1440
1435
|
throw new ModelError(`Unknown args: ${JSON.stringify(args)}`);
|
|
1441
1436
|
}
|
|
@@ -1446,23 +1441,40 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1446
1441
|
*
|
|
1447
1442
|
* @param {any} arg
|
|
1448
1443
|
* @param {CdlRenderEnvironment} env
|
|
1444
|
+
* @param {string[]} additionalAllowedKeywords
|
|
1449
1445
|
* @return {string}
|
|
1450
1446
|
*/
|
|
1451
|
-
function renderArgument(arg, env) {
|
|
1447
|
+
function renderArgument(arg, env, additionalAllowedKeywords = []) {
|
|
1452
1448
|
// If the argument is a xpr with e.g. `=`, it may require parentheses.
|
|
1453
1449
|
// For nested xpr, `renderExpr()` will already add parentheses.
|
|
1454
|
-
return renderExpr(arg, env, true, !isSimpleFunctionExpression(arg && arg.xpr), true);
|
|
1450
|
+
return renderExpr(arg, env, true, !isSimpleFunctionExpression(arg && arg.xpr, additionalAllowedKeywords), true);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* Render an artifact's cardinality.
|
|
1455
|
+
*
|
|
1456
|
+
* @param artifact
|
|
1457
|
+
* @returns {string}
|
|
1458
|
+
*/
|
|
1459
|
+
function renderCardinality(artifact) {
|
|
1460
|
+
if (isSimpleCardinality(artifact.cardinality))
|
|
1461
|
+
return renderSimpleCardinality(artifact);
|
|
1462
|
+
return renderBracketCardinality(artifact);
|
|
1455
1463
|
}
|
|
1456
1464
|
|
|
1457
1465
|
/**
|
|
1458
1466
|
* Render a cardinality (only those parts that were actually provided)
|
|
1459
1467
|
*
|
|
1460
|
-
* @param {CSN.
|
|
1468
|
+
* @param {CSN.Artifact} art
|
|
1461
1469
|
* @return {string}
|
|
1462
1470
|
*/
|
|
1463
|
-
function
|
|
1471
|
+
function renderBracketCardinality(art) {
|
|
1472
|
+
const isComp = art.type === 'cds.Composition';
|
|
1473
|
+
const suffix = (isComp ? ' of ' : ' to ');
|
|
1474
|
+
const card = art.cardinality;
|
|
1475
|
+
|
|
1464
1476
|
if (!card)
|
|
1465
|
-
return
|
|
1477
|
+
return suffix;
|
|
1466
1478
|
|
|
1467
1479
|
let result = '[';
|
|
1468
1480
|
if (card.src !== undefined)
|
|
@@ -1474,7 +1486,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1474
1486
|
if (card.max !== undefined)
|
|
1475
1487
|
result += card.max;
|
|
1476
1488
|
|
|
1477
|
-
return `${result}]`;
|
|
1489
|
+
return `${result}]${suffix}`;
|
|
1478
1490
|
}
|
|
1479
1491
|
|
|
1480
1492
|
/**
|
|
@@ -1528,7 +1540,8 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1528
1540
|
* @return {string}
|
|
1529
1541
|
*/
|
|
1530
1542
|
function renderForeignKey(fKey, env) {
|
|
1531
|
-
|
|
1543
|
+
const alias = fKey.as ? (` as ${fKey.as}`) : '';
|
|
1544
|
+
return renderExpr(fKey, env) + alias;
|
|
1532
1545
|
}
|
|
1533
1546
|
|
|
1534
1547
|
/**
|
|
@@ -1543,12 +1556,6 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1543
1556
|
if (params.length === 0)
|
|
1544
1557
|
return '';
|
|
1545
1558
|
|
|
1546
|
-
// TODO(v3): Remove special handling of srid
|
|
1547
|
-
// For backwards compatibility, do not render srid as a named argument for POINT/GEOMETRY builtins.
|
|
1548
|
-
if (artWithType.srid !== undefined &&
|
|
1549
|
-
(artWithType.type === 'cds.hana.ST_POINT' || artWithType.type === 'cds.hana.ST_GEOMETRY'))
|
|
1550
|
-
return `(${artWithType.srid})`;
|
|
1551
|
-
|
|
1552
1559
|
// Special cases for 1 or 2 arguments.
|
|
1553
1560
|
if (params.length === 1 && artWithType.length !== undefined)
|
|
1554
1561
|
return `(${artWithType.length})`;
|
|
@@ -1603,6 +1610,7 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1603
1610
|
// We expand this pattern to also include dots after the first character.
|
|
1604
1611
|
// If the annotation does not follow this pattern `ident(.@ident)*`, it must be quoted:
|
|
1605
1612
|
// `@identifier@identifier` must be quoted but `@identifier.@identifier` should not.
|
|
1613
|
+
// TODO: Use quoteAnnotationPathIfRequired()
|
|
1606
1614
|
const annoRequiresQuoting = !/^[$_a-zA-Z][$_a-zA-Z0-9.]*(?:\.@[$_a-zA-Z][$_a-zA-Z0-9.]*)*$/.test(nameBeforeVariant);
|
|
1607
1615
|
// Unfortunately, the compiler does not allow `.` after the first variant identifier,
|
|
1608
1616
|
// even though that is the result after flattening.
|
|
@@ -1624,77 +1632,47 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1624
1632
|
}
|
|
1625
1633
|
|
|
1626
1634
|
/**
|
|
1627
|
-
* Render the name of an artifact,
|
|
1628
|
-
* and the real name of the artifact. In case of plain names, this
|
|
1629
|
-
* is equivalent to simply flattening and uppercasing the whole name.
|
|
1630
|
-
*
|
|
1631
|
-
* In cdlMode, the prefix is extended to handle cases like an entity shadowing the prefix
|
|
1632
|
-
* of another entity -> Service.E and Service.E.Sub
|
|
1633
|
-
*
|
|
1634
|
-
* To handle such cases for hdbcds in quoted/hdbcds, we:
|
|
1635
|
-
* - Find the part of the name that is no longer prefix (context/service)
|
|
1636
|
-
* - For Service.E -> E, for Service.E.Sub -> E.Sub
|
|
1637
|
-
* - Replace all dots in this "real name" with underscores
|
|
1638
|
-
* - Join with the env prefix
|
|
1635
|
+
* Render the name of an artifact, quote path steps if necessary.
|
|
1639
1636
|
*
|
|
1640
1637
|
* @param {string} artifactName Artifact name to render
|
|
1641
1638
|
* @return {string} Artifact name ready for rendering
|
|
1642
1639
|
*/
|
|
1643
1640
|
function renderArtifactName(artifactName) {
|
|
1644
|
-
|
|
1645
|
-
const prefix = (realname !== artifactName) ? artifactName.slice(0, artifactName.length - realname.length - 1) : '';
|
|
1646
|
-
return prefix ? `${quoteIdIfRequired(prefix)}.${realname.split('.').map(quoteIdIfRequired).join('.')}` : realname.split('.').map(quoteIdIfRequired).join('.');
|
|
1641
|
+
return quotePathIfRequired(artifactName);
|
|
1647
1642
|
}
|
|
1648
1643
|
|
|
1649
1644
|
/**
|
|
1650
|
-
*
|
|
1645
|
+
* Render a function expression.
|
|
1651
1646
|
*
|
|
1652
|
-
* @param {
|
|
1653
|
-
* @
|
|
1647
|
+
* @param {object} obj Object with .func and optionally .args
|
|
1648
|
+
* @param {CdlRenderEnvironment} env
|
|
1649
|
+
* @returns {string}
|
|
1654
1650
|
*/
|
|
1655
|
-
function
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
let seen = '';
|
|
1663
|
-
for (let i = 0; i < parts.length; i++) {
|
|
1664
|
-
if (seen !== '')
|
|
1665
|
-
seen = `${seen}.${parts[i]}`;
|
|
1666
|
-
else
|
|
1667
|
-
seen = parts[i];
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
const art = csn.definitions[seen];
|
|
1671
|
-
if (!art || (art.kind !== 'service' && art.kind !== 'context')) {
|
|
1672
|
-
// We found a case where the prefix ended
|
|
1673
|
-
// Return everything following
|
|
1674
|
-
return parts.slice(i).join('.');
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
// we seem to have a normal case - just return the last part
|
|
1679
|
-
return getLastPartOf(artifactName);
|
|
1651
|
+
function renderFuncExpr( obj, env ) {
|
|
1652
|
+
if (keywords.cdl_functions.includes(obj.func.toUpperCase()))
|
|
1653
|
+
return obj.func;
|
|
1654
|
+
const name = identifierRegex.test(obj.func) ? obj.func : quote(obj.func);
|
|
1655
|
+
return `${name}(${renderArgs( obj, '=>', env )})`;
|
|
1680
1656
|
}
|
|
1681
1657
|
|
|
1682
1658
|
/**
|
|
1683
1659
|
* Render an expression.
|
|
1684
1660
|
*
|
|
1685
1661
|
* @param {any} expr
|
|
1686
|
-
* @param {CdlRenderEnvironment}
|
|
1687
|
-
* @param {boolean} [
|
|
1688
|
-
* @param {boolean} [
|
|
1662
|
+
* @param {CdlRenderEnvironment} exprEnv
|
|
1663
|
+
* @param {boolean} [isInline]
|
|
1664
|
+
* @param {boolean} [isNestedExpr]
|
|
1689
1665
|
* @param {boolean} [alwaysRenderCast]
|
|
1690
1666
|
* @returns {string}
|
|
1691
1667
|
*/
|
|
1692
|
-
function renderExpr(expr,
|
|
1668
|
+
function renderExpr(expr, exprEnv, isInline, isNestedExpr, alwaysRenderCast) {
|
|
1693
1669
|
if (!_renderExpr) {
|
|
1694
1670
|
_renderExpr = getExpressionRenderer({
|
|
1695
|
-
finalize
|
|
1696
|
-
|
|
1697
|
-
|
|
1671
|
+
finalize(x) {
|
|
1672
|
+
return x;
|
|
1673
|
+
},
|
|
1674
|
+
explicitTypeCast(x, env) {
|
|
1675
|
+
const typeRef = renderTypeReferenceAndProps(x.cast, env, { typeRefOnly: true, noAnnoCollect: true });
|
|
1698
1676
|
const arg = { ...x, cast: null }; // "arg" without cast to avoid recursion.
|
|
1699
1677
|
return `cast(${renderArgument(arg, env)} as ${typeRef})`;
|
|
1700
1678
|
},
|
|
@@ -1723,42 +1701,40 @@ function toCdsSourceCsn(csn, options) {
|
|
|
1723
1701
|
aliasOnly(x, _env) {
|
|
1724
1702
|
return x.as;
|
|
1725
1703
|
},
|
|
1726
|
-
enum
|
|
1704
|
+
enum(x) {
|
|
1705
|
+
return `#${x['#']}`;
|
|
1706
|
+
},
|
|
1727
1707
|
ref(x, env) {
|
|
1728
1708
|
const { inline } = this;
|
|
1729
1709
|
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, inline, env)).join('.')}`;
|
|
1730
1710
|
},
|
|
1731
|
-
windowFunction
|
|
1732
|
-
const funcDef =
|
|
1711
|
+
windowFunction(x, env) {
|
|
1712
|
+
const funcDef = renderFuncExpr(x, env);
|
|
1733
1713
|
const windowFunctionOperator = x.xpr.shift(); // OVER ...
|
|
1734
1714
|
return `${funcDef} ${windowFunctionOperator} ( ${renderExpr(x.xpr, env, true)} )`;
|
|
1735
1715
|
},
|
|
1736
|
-
func
|
|
1737
|
-
|
|
1738
|
-
// test for non-regular HANA identifier that needs to be quoted
|
|
1739
|
-
// identifier {letter}({letter_or_digit}|[#$])*
|
|
1740
|
-
// letter [A-Za-z_]
|
|
1741
|
-
// letter_or_digit [A-Za-z_0-9]
|
|
1742
|
-
|
|
1743
|
-
const regex = /^[a-zA-Z][\w#$]*$/g;
|
|
1744
|
-
const funcName = regex.test(x.func) ? x.func : quoteIdIfRequired(x.func);
|
|
1745
|
-
return renderFunc(funcName, x, 'cap', a => renderArgs(a, '=>', env));
|
|
1716
|
+
func(x, env) {
|
|
1717
|
+
return renderFuncExpr(x, env);
|
|
1746
1718
|
},
|
|
1747
1719
|
xpr(x, env) {
|
|
1748
1720
|
if (this.nestedExpr && !x.cast || x.xpr.some(s => s === 'exists'))
|
|
1749
1721
|
return `(${renderExpr(x.xpr, env, this.inline, true)})`;
|
|
1750
|
-
|
|
1751
1722
|
return renderExpr(x.xpr, env, this.inline, true);
|
|
1752
1723
|
},
|
|
1753
1724
|
// Sub-queries in expressions need to be in parentheses, otherwise
|
|
1754
1725
|
// left-associativity of UNIONS may result in different results.
|
|
1755
1726
|
// For example: `select from E where id in (select from E union select from E);`:
|
|
1756
1727
|
// Without parentheses, it would be different query.
|
|
1757
|
-
SET
|
|
1758
|
-
|
|
1728
|
+
SET(x, env) {
|
|
1729
|
+
return `(${renderQuery(x, false, 'view', increaseIndent(env))})`;
|
|
1730
|
+
},
|
|
1731
|
+
SELECT(x, env) {
|
|
1732
|
+
return `(${renderQuery(x, false, 'view', increaseIndent(env))})`;
|
|
1733
|
+
},
|
|
1759
1734
|
});
|
|
1760
1735
|
}
|
|
1761
|
-
|
|
1736
|
+
|
|
1737
|
+
return _renderExpr(expr, exprEnv, isInline, isNestedExpr, alwaysRenderCast);
|
|
1762
1738
|
}
|
|
1763
1739
|
}
|
|
1764
1740
|
|
|
@@ -1772,15 +1748,19 @@ function createEnv() {
|
|
|
1772
1748
|
return {
|
|
1773
1749
|
// Current indentation string
|
|
1774
1750
|
indent: '',
|
|
1775
|
-
|
|
1776
|
-
topLevelAliases: Object.create(null),
|
|
1777
|
-
// Current name prefix (including trailing dot if not empty)
|
|
1778
|
-
namePrefix: '',
|
|
1751
|
+
path: null,
|
|
1779
1752
|
artifactName: '',
|
|
1780
1753
|
elementName: '',
|
|
1781
1754
|
};
|
|
1782
1755
|
}
|
|
1783
1756
|
|
|
1757
|
+
function envAddPath(env, path) {
|
|
1758
|
+
return Object.assign({}, env, { path: [ ...env.path, ...path ] } );
|
|
1759
|
+
}
|
|
1760
|
+
function envNewPath(env, path) {
|
|
1761
|
+
return Object.assign({}, env, { path: [ ...path ] } );
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1784
1764
|
/**
|
|
1785
1765
|
* Returns a copy of 'env' with increased indentation (and reset name prefix)
|
|
1786
1766
|
*
|
|
@@ -1788,22 +1768,25 @@ function createEnv() {
|
|
|
1788
1768
|
* @returns {CdlRenderEnvironment}
|
|
1789
1769
|
*/
|
|
1790
1770
|
function increaseIndent(env) {
|
|
1791
|
-
return Object.assign({}, env, {
|
|
1771
|
+
return Object.assign({}, env, { indent: `${env.indent} ` });
|
|
1792
1772
|
}
|
|
1793
1773
|
|
|
1794
1774
|
/**
|
|
1795
|
-
*
|
|
1775
|
+
* Quote the path steps with `![]` if necessary. For simple ids such as
|
|
1776
|
+
* `elem` use `quoteIdIfRequired` instead.
|
|
1796
1777
|
*
|
|
1797
1778
|
* @param {string} path
|
|
1798
1779
|
* @returns {string}
|
|
1780
|
+
*
|
|
1781
|
+
* @todo For paths such as `E.key`, `key` does not have to be in quotes.
|
|
1799
1782
|
*/
|
|
1800
|
-
function
|
|
1801
|
-
// "foo"."bar"."wiz"."blub"
|
|
1783
|
+
function quotePathIfRequired(path) {
|
|
1802
1784
|
return path.split('.').map(quoteIdIfRequired).join('.');
|
|
1803
1785
|
}
|
|
1804
1786
|
|
|
1805
1787
|
/**
|
|
1806
|
-
*
|
|
1788
|
+
* Quote the id with `![]` if necessary. For paths such as `E.key` use
|
|
1789
|
+
* `quotePathIfRequired` instead.
|
|
1807
1790
|
*
|
|
1808
1791
|
* @param {string} id
|
|
1809
1792
|
* @return {string}
|
|
@@ -1816,6 +1799,23 @@ function quoteIdIfRequired(id) {
|
|
|
1816
1799
|
return id;
|
|
1817
1800
|
}
|
|
1818
1801
|
|
|
1802
|
+
/**
|
|
1803
|
+
* Quote an annotation path, e.g. `@My.@Anno.Description` if necessary.
|
|
1804
|
+
* `anno` can start with `@` but is not required to be.
|
|
1805
|
+
* Example of an annotation path that needs to be quoted:
|
|
1806
|
+
* `@![ spaces in path ].@!["double quotes"]`.
|
|
1807
|
+
*
|
|
1808
|
+
* @param {string} anno
|
|
1809
|
+
* @returns {string}
|
|
1810
|
+
*/
|
|
1811
|
+
function quoteAnnotationPathIfRequired(anno) {
|
|
1812
|
+
return anno.split('.').map((segment) => {
|
|
1813
|
+
if (segment.startsWith('@'))
|
|
1814
|
+
return `@${quoteIdIfRequired(segment.slice(1))}`;
|
|
1815
|
+
return quoteIdIfRequired(segment);
|
|
1816
|
+
}).join('.');
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
1819
|
/**
|
|
1820
1820
|
* Quotes the identifier using CDL-style ![]-quotes.
|
|
1821
1821
|
*
|
|
@@ -1828,20 +1828,14 @@ function quote(id) {
|
|
|
1828
1828
|
|
|
1829
1829
|
/**
|
|
1830
1830
|
* Returns true if 'id' requires quotes for CDL, i.e. if 'id'
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
1833
|
-
* - uppercase letters
|
|
1834
|
-
* - lowercase letters
|
|
1835
|
-
* - digits
|
|
1836
|
-
* - underscore
|
|
1837
|
-
* 3. is a CDL keyword or a CDL function without parentheses (CURRENT_*, SYSUUID, ...)
|
|
1831
|
+
* does not match the first part of the `Identifier` rule of `language.g4`
|
|
1832
|
+
* or if 'id' is a reserved keyword.
|
|
1838
1833
|
*
|
|
1839
1834
|
* @param {string} id
|
|
1840
1835
|
* @return {boolean}
|
|
1841
1836
|
*/
|
|
1842
1837
|
function requiresQuotingForCdl(id) {
|
|
1843
|
-
return
|
|
1844
|
-
/\W/g.test(id.replace(/\./g, '')) ||
|
|
1838
|
+
return !identifierRegex.test(id) ||
|
|
1845
1839
|
keywords.cdl.includes(id.toUpperCase()) ||
|
|
1846
1840
|
keywords.cdl_functions.includes(id.toUpperCase());
|
|
1847
1841
|
}
|
|
@@ -1865,18 +1859,52 @@ const functionExpressionOperatorsRequireParentheses = [
|
|
|
1865
1859
|
* in a `fct(<xpr>)` expression such as `cast(<xpr> as Type)`. We only need to
|
|
1866
1860
|
* look at the first nesting level. Otherwise, `renderExpr()` will already add parentheses.
|
|
1867
1861
|
*
|
|
1868
|
-
* The list was created by looking at
|
|
1862
|
+
* The list of `functionExpressionOperatorsRequireParentheses` was created by looking at
|
|
1863
|
+
* the `expression` Antlr rule.
|
|
1864
|
+
* Because of token-rewrites, there are functions that allow operators/tokens that would
|
|
1865
|
+
* require parentheses in other functions. For example *regex functions allow `IN` but
|
|
1866
|
+
* if `IN` is used in other functions, it requires parentheses. To allow for that case,
|
|
1867
|
+
* you can set `additionalAllowedKeywords` to list of tokens that are allowed.
|
|
1869
1868
|
*
|
|
1870
|
-
*
|
|
1869
|
+
* Note that this is more of a heuristic for "nicer" CDL output. For example the
|
|
1871
1870
|
* following snippet is parsable without parentheses:
|
|
1872
1871
|
* `cast( case when int > 1 then int else 0 end as Integer ),`
|
|
1873
|
-
* However, because it is a flat xpr-array, we see `>` and assume that it is not
|
|
1872
|
+
* However, because it is a flat xpr-array, we see `>` and assume that it is not
|
|
1873
|
+
* a simple expression.
|
|
1874
1874
|
*
|
|
1875
1875
|
* @param {any[]} xpr
|
|
1876
|
+
* @param {string[]} additionalAllowedKeywords
|
|
1876
1877
|
* @return {boolean}
|
|
1877
1878
|
*/
|
|
1878
|
-
function isSimpleFunctionExpression(xpr) {
|
|
1879
|
-
return !xpr || xpr.every(val => typeof val !== 'string' ||
|
|
1879
|
+
function isSimpleFunctionExpression(xpr, additionalAllowedKeywords = []) {
|
|
1880
|
+
return !xpr || xpr.every(val => typeof val !== 'string' ||
|
|
1881
|
+
(additionalAllowedKeywords.includes(val) ||
|
|
1882
|
+
!functionExpressionOperatorsRequireParentheses.includes(val.toLowerCase())));
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
/**
|
|
1886
|
+
* Special functions may have special parser rules, such as SAP HANA RegEx functions.
|
|
1887
|
+
* They allow certain keywords in their arguments.
|
|
1888
|
+
*
|
|
1889
|
+
* This function is used to determine if arguments need to be put in parentheses or not.
|
|
1890
|
+
* See {@link isSimpleFunctionExpression}.
|
|
1891
|
+
*
|
|
1892
|
+
* @param {string} funcName
|
|
1893
|
+
* @param {number} argumentIndex
|
|
1894
|
+
* @returns {string[]}
|
|
1895
|
+
*/
|
|
1896
|
+
function getKeywordsForSpecialFunctionArgument(funcName, argumentIndex) {
|
|
1897
|
+
const f = specialFunctions[funcName] && specialFunctions[funcName][argumentIndex];
|
|
1898
|
+
if (!f)
|
|
1899
|
+
return [];
|
|
1900
|
+
const additionalKeywords = [];
|
|
1901
|
+
if (f.intro)
|
|
1902
|
+
additionalKeywords.push(...f.intro);
|
|
1903
|
+
if (f.expr)
|
|
1904
|
+
additionalKeywords.push(...f.expr);
|
|
1905
|
+
if (f.separator)
|
|
1906
|
+
additionalKeywords.push(...f.separator);
|
|
1907
|
+
return additionalKeywords;
|
|
1880
1908
|
}
|
|
1881
1909
|
|
|
1882
1910
|
/**
|
|
@@ -1884,7 +1912,7 @@ function isSimpleFunctionExpression(xpr) {
|
|
|
1884
1912
|
* @return {string}
|
|
1885
1913
|
*/
|
|
1886
1914
|
function renderIncludes(includes) {
|
|
1887
|
-
return ` : ${includes.map(name =>
|
|
1915
|
+
return ` : ${includes.map(name => quotePathIfRequired(name)).join(', ')}`;
|
|
1888
1916
|
}
|
|
1889
1917
|
|
|
1890
1918
|
/**
|
|
@@ -1965,17 +1993,9 @@ function isSimpleString(str) {
|
|
|
1965
1993
|
* @typedef CdlRenderEnvironment Rendering environment used throughout the render process.
|
|
1966
1994
|
*
|
|
1967
1995
|
* @property {string} indent Current indentation as a string, e.g. ' ' for two spaces.
|
|
1968
|
-
* @property {
|
|
1996
|
+
* @property {string[]} [path] CSN path to the current artifact
|
|
1969
1997
|
* @property {string} [artifactName] Name of the artifact - set in renderArtifact
|
|
1970
1998
|
* @property {string} [elementName] Name of the element being rendered - set in renderElement
|
|
1971
|
-
* @property {{[name: string]: {
|
|
1972
|
-
quotedName: string,
|
|
1973
|
-
quotedAlias: string
|
|
1974
|
-
}}} topLevelAliases Dictionary of aliases for used artifact names
|
|
1975
|
-
*
|
|
1976
|
-
* @property {string} [namePrefix] Current name prefix (including trailing dot if not empty)
|
|
1977
|
-
* @property {boolean} [skipKeys]
|
|
1978
|
-
* @property {CSN.Artifact} [_artifact]
|
|
1979
1999
|
*/
|
|
1980
2000
|
|
|
1981
|
-
module.exports = {
|
|
2001
|
+
module.exports = { csnToCdl };
|