@sap/cds-compiler 3.6.2 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +109 -1
- package/README.md +3 -0
- package/bin/cdsc.js +12 -5
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +35 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +63 -23
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +15 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +120 -34
- package/lib/base/messages.js +51 -27
- package/lib/base/model.js +4 -2
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +15 -9
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +5 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +42 -26
- package/lib/compiler/base.js +50 -4
- package/lib/compiler/builtins.js +17 -8
- package/lib/compiler/checks.js +241 -246
- package/lib/compiler/define.js +113 -146
- package/lib/compiler/extend.js +889 -383
- package/lib/compiler/finalize-parse-cdl.js +5 -58
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +7 -8
- package/lib/compiler/populate.js +297 -293
- package/lib/compiler/propagator.js +27 -18
- package/lib/compiler/resolve.js +146 -463
- package/lib/compiler/shared.js +36 -79
- package/lib/compiler/tweak-assocs.js +30 -28
- package/lib/compiler/utils.js +31 -5
- package/lib/edm/annotations/genericTranslation.js +131 -59
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +22 -5
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +42 -26
- package/lib/gen/Dictionary.json +38 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4828 -4472
- package/lib/inspect/inspectPropagation.js +20 -34
- package/lib/json/from-csn.js +140 -44
- package/lib/json/to-csn.js +114 -122
- package/lib/language/errorStrategy.js +2 -0
- package/lib/language/genericAntlrParser.js +156 -36
- package/lib/language/language.g4 +100 -58
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +15 -3
- package/lib/model/csnUtils.js +12 -74
- package/lib/model/revealInternalProperties.js +4 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +216 -104
- package/lib/render/toHdbcds.js +2 -9
- package/lib/render/toRename.js +14 -51
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +600 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +58 -41
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +16 -8
- package/lib/transform/transformUtilsNew.js +42 -14
- package/lib/transform/translateAssocsToJoins.js +60 -37
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
|
@@ -19,6 +19,8 @@ const {
|
|
|
19
19
|
} = require('../compiler/builtins');
|
|
20
20
|
const { pathName } = require('../compiler/utils');
|
|
21
21
|
const { isBetaEnabled } = require('../base/model');
|
|
22
|
+
const { weakLocation } = require('../base/messages');
|
|
23
|
+
const { normalizeNewLine } = require('./textUtils');
|
|
22
24
|
|
|
23
25
|
const $location = Symbol.for('cds.$location');
|
|
24
26
|
|
|
@@ -73,9 +75,11 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
73
75
|
info(...args) {
|
|
74
76
|
return _message( this, 'info', ...args );
|
|
75
77
|
},
|
|
78
|
+
isBetaEnabled,
|
|
76
79
|
attachLocation,
|
|
77
80
|
assignAnnotation,
|
|
78
81
|
addAnnotation,
|
|
82
|
+
expressionAsAnnotationValue,
|
|
79
83
|
checkExtensionDict,
|
|
80
84
|
handleDuplicateExtension,
|
|
81
85
|
startLocation,
|
|
@@ -86,6 +90,7 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
86
90
|
previousTokenAtLocation,
|
|
87
91
|
combinedLocation,
|
|
88
92
|
surroundByParens,
|
|
93
|
+
tokensToStringRepresentation,
|
|
89
94
|
secureParens,
|
|
90
95
|
unaryOpForParens,
|
|
91
96
|
leftAssocBinaryOp,
|
|
@@ -96,6 +101,7 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
96
101
|
pushXprToken,
|
|
97
102
|
argsExpression,
|
|
98
103
|
valuePathAst,
|
|
104
|
+
fixNewKeywordPlacement,
|
|
99
105
|
signedExpression,
|
|
100
106
|
numberLiteral,
|
|
101
107
|
quotedLiteral,
|
|
@@ -120,6 +126,7 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
120
126
|
associationInSelectItem,
|
|
121
127
|
reportExpandInline,
|
|
122
128
|
checkTypeFacet,
|
|
129
|
+
checkTypeArgs,
|
|
123
130
|
csnParseOnly,
|
|
124
131
|
noAssignmentInSameLine,
|
|
125
132
|
noSemicolonHere,
|
|
@@ -361,15 +368,12 @@ function assignAnnotation( art, anno, prefix = '' ) {
|
|
|
361
368
|
}
|
|
362
369
|
|
|
363
370
|
function addAnnotation( art, prop, anno ) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
a.$errorReported = 'syntax-duplicate-anno';
|
|
371
|
-
// do not report again later as anno-duplicate-xyz
|
|
372
|
-
} );
|
|
371
|
+
const old = art[prop];
|
|
372
|
+
if (old) {
|
|
373
|
+
this.error( 'syntax-duplicate-anno', old.name.location, { anno: prop },
|
|
374
|
+
'Assignment for $(ANNO) is overwritten by another one below' );
|
|
375
|
+
}
|
|
376
|
+
art[prop] = anno;
|
|
373
377
|
}
|
|
374
378
|
|
|
375
379
|
const extensionDicts = {
|
|
@@ -525,18 +529,24 @@ function fixMultiLineTokenEndLocation( token, location ) {
|
|
|
525
529
|
}
|
|
526
530
|
|
|
527
531
|
/**
|
|
528
|
-
* Return `val` with
|
|
529
|
-
*
|
|
532
|
+
* Return `val` with a location; if `val` and `endToken` are not provided, use the
|
|
533
|
+
* lower-cased token string of `startToken` as `val`. As location, use the
|
|
534
|
+
* location covered by `startToken` and `endToken`, or only `startToken` if no
|
|
535
|
+
* `endToken` is provided. The `startToken` defaults to the previous token.
|
|
530
536
|
*
|
|
531
537
|
* @param {object} startToken
|
|
532
538
|
* @param {object} endToken
|
|
533
539
|
* @param {any} val
|
|
534
540
|
*/
|
|
535
|
-
function valueWithTokenLocation( val, startToken
|
|
536
|
-
|
|
537
|
-
|
|
541
|
+
function valueWithTokenLocation( val = undefined, startToken = this._input.LT(-1),
|
|
542
|
+
endToken = undefined ) {
|
|
543
|
+
// if (!startToken)
|
|
544
|
+
// startToken = this._input.LT(-1);
|
|
538
545
|
const loc = this.tokenLocation( startToken, endToken );
|
|
539
|
-
return {
|
|
546
|
+
return {
|
|
547
|
+
location: loc,
|
|
548
|
+
val: (endToken || val !== undefined) ? val : startToken.text.toLowerCase(),
|
|
549
|
+
};
|
|
540
550
|
}
|
|
541
551
|
|
|
542
552
|
function previousTokenAtLocation( location ) {
|
|
@@ -584,6 +594,23 @@ function surroundByParens( expr, open, close, asQuery = false ) {
|
|
|
584
594
|
return (asQuery) ? { query: expr, location } : expr;
|
|
585
595
|
}
|
|
586
596
|
|
|
597
|
+
|
|
598
|
+
function tokensToStringRepresentation( matchedRule ) {
|
|
599
|
+
const tokens = this._input.getTokens(
|
|
600
|
+
matchedRule.start.tokenIndex,
|
|
601
|
+
matchedRule.stop.tokenIndex + 1, null
|
|
602
|
+
).filter(tok => tok.channel === antlr4.Token.DEFAULT_CHANNEL);
|
|
603
|
+
if (tokens.length === 0)
|
|
604
|
+
return '';
|
|
605
|
+
|
|
606
|
+
let result = tokens[0].text;
|
|
607
|
+
for (let i = 1; i < tokens.length; ++i) {
|
|
608
|
+
const str = normalizeNewLine(tokens[i].text);
|
|
609
|
+
result += (tokens[i].start > tokens[i - 1].stop + 1) ? ` ${ str }` : str;
|
|
610
|
+
}
|
|
611
|
+
return result;
|
|
612
|
+
}
|
|
613
|
+
|
|
587
614
|
function unaryOpForParens( query, val ) {
|
|
588
615
|
const parens = query?.$parens;
|
|
589
616
|
if (!parens)
|
|
@@ -652,7 +679,7 @@ function fragileAlias( ast, safe = false ) {
|
|
|
652
679
|
return ast;
|
|
653
680
|
}
|
|
654
681
|
|
|
655
|
-
// Return AST for identifier token `token`. Also check that
|
|
682
|
+
// Return AST for identifier token `token`. Also check that identifier is not empty.
|
|
656
683
|
function identAst( token, category, noTokenTypeCheck = false ) {
|
|
657
684
|
token.isIdentifier = category;
|
|
658
685
|
let id = token.text;
|
|
@@ -696,8 +723,11 @@ function argsExpression( args, nary, location ) {
|
|
|
696
723
|
location: undefined,
|
|
697
724
|
} );
|
|
698
725
|
}
|
|
726
|
+
// eslint-disable-next-line no-nested-ternary
|
|
727
|
+
const val = nary === '?:' ? nary
|
|
728
|
+
: (nary && nary !== '=' ? 'nary' : 'ixpr');
|
|
699
729
|
const op = {
|
|
700
|
-
val
|
|
730
|
+
val, // there is no n-ary in rule conditionTerm
|
|
701
731
|
location: this.startLocation(),
|
|
702
732
|
};
|
|
703
733
|
return this.attachLocation( { op, args, location: location && { ...location } } );
|
|
@@ -713,35 +743,115 @@ function pushXprToken( args ) {
|
|
|
713
743
|
}
|
|
714
744
|
|
|
715
745
|
function valuePathAst( ref ) {
|
|
716
|
-
// TODO: XSN representation of functions is a bit strange - rework
|
|
717
|
-
// are introduced
|
|
746
|
+
// TODO: XSN representation of functions is a bit strange - rework
|
|
718
747
|
const { path } = ref;
|
|
719
748
|
if (!path || path.broken)
|
|
720
749
|
return ref;
|
|
721
750
|
if (path.length !== 1) {
|
|
722
751
|
const item = path.find( i => i.args && i.$syntax !== ':' );
|
|
723
|
-
if (!item)
|
|
752
|
+
if (!item) // also covers empty paths
|
|
724
753
|
return ref;
|
|
725
|
-
this.error( 'syntax-unsupported-method', item.location, {},
|
|
726
|
-
'Methods in expressions are not supported yet' );
|
|
727
|
-
path.broken = true;
|
|
728
|
-
path.length = 1;
|
|
729
754
|
}
|
|
730
|
-
|
|
731
|
-
|
|
755
|
+
else if (path.length === 1) {
|
|
756
|
+
const { args, id, location } = path[0];
|
|
757
|
+
if (args
|
|
732
758
|
? path[0].$syntax === ':'
|
|
733
759
|
: path[0].$delimited || !functionsWithoutParens.includes( id.toUpperCase() ))
|
|
734
|
-
|
|
760
|
+
return ref;
|
|
761
|
+
|
|
762
|
+
const implicit = this.previousTokenAtLocation( location );
|
|
763
|
+
if (implicit && implicit.isIdentifier)
|
|
764
|
+
implicit.isIdentifier = 'func';
|
|
765
|
+
const op = { location, val: 'call' };
|
|
766
|
+
return (args)
|
|
767
|
+
? {
|
|
768
|
+
op, func: ref, location: ref.location, args,
|
|
769
|
+
}
|
|
770
|
+
: { op, func: ref, location: ref.location };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// method call ---------------------------
|
|
774
|
+
|
|
775
|
+
const args = [];
|
|
776
|
+
const pathRest = [ ...path ];
|
|
777
|
+
let pathHead = pathRest.shift();
|
|
735
778
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
779
|
+
if (pathHead.args) {
|
|
780
|
+
args.push({
|
|
781
|
+
op: { location: pathHead.location, val: 'call' },
|
|
782
|
+
func: { path: [ pathHead ] },
|
|
783
|
+
location: pathHead.location,
|
|
784
|
+
args: pathHead.args || [],
|
|
785
|
+
});
|
|
786
|
+
pathHead = pathRest.shift();
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
const refPath = [];
|
|
790
|
+
while (pathHead && !pathHead.args) {
|
|
791
|
+
refPath.push(pathHead);
|
|
792
|
+
pathHead = pathRest.shift();
|
|
743
793
|
}
|
|
744
|
-
|
|
794
|
+
args.push({ path: refPath, location: refPath[0].location });
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (pathHead?.args)
|
|
798
|
+
pathRest.unshift(pathHead);
|
|
799
|
+
|
|
800
|
+
for (const method of pathRest) {
|
|
801
|
+
args.push({
|
|
802
|
+
// TODO: Update parser to have proper location for `.`?
|
|
803
|
+
location: weakLocation(method.location),
|
|
804
|
+
val: '.',
|
|
805
|
+
literal: 'token',
|
|
806
|
+
});
|
|
807
|
+
const func = {
|
|
808
|
+
op: { location: method.location, val: 'call' },
|
|
809
|
+
func: { path: [ method ] },
|
|
810
|
+
location: method.location,
|
|
811
|
+
};
|
|
812
|
+
if (method.args)
|
|
813
|
+
func.args = method.args;
|
|
814
|
+
args.push(func);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
return {
|
|
818
|
+
op: {
|
|
819
|
+
val: 'ixpr',
|
|
820
|
+
location: this.startLocation(),
|
|
821
|
+
},
|
|
822
|
+
args,
|
|
823
|
+
location: ref.location,
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Adds the first argument of `args` ('new' keyword) to the second argument, if it's a method-ixpr.
|
|
830
|
+
*
|
|
831
|
+
* @todo Cleanup, remove.
|
|
832
|
+
* @param args
|
|
833
|
+
*/
|
|
834
|
+
function fixNewKeywordPlacement( args ) {
|
|
835
|
+
// TODO: Currently, the parser creates an args-array with `new` and an `ixpr` for
|
|
836
|
+
// `new P().abc()`. That is, "new" is separate from the methods.
|
|
837
|
+
// This function tries to work around it, but its more of a hack.
|
|
838
|
+
if (args.length !== 2 || !args[1].args || args[1].op?.val !== 'ixpr')
|
|
839
|
+
return;
|
|
840
|
+
const ixpr = args[1];
|
|
841
|
+
ixpr.args.unshift(args[0]);
|
|
842
|
+
args.length = 0;
|
|
843
|
+
args.push(ixpr);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function expressionAsAnnotationValue( assignment, cond ) {
|
|
847
|
+
if (!cond.cond) // parse error
|
|
848
|
+
return;
|
|
849
|
+
Object.assign(assignment, cond.cond);
|
|
850
|
+
assignment.$tokenTexts = this.tokensToStringRepresentation(cond);
|
|
851
|
+
if (!this.isBetaEnabled(this.options, 'annotationExpressions')) {
|
|
852
|
+
this.error( 'syntax-unsupported-expression', [ cond.cond.location ], {},
|
|
853
|
+
'Expressions in annotation values are not supported' );
|
|
854
|
+
}
|
|
745
855
|
}
|
|
746
856
|
|
|
747
857
|
// If a '-' is directly before an unsigned number, consider it part of the number;
|
|
@@ -992,7 +1102,7 @@ function aspectWithoutElements( art ) {
|
|
|
992
1102
|
}
|
|
993
1103
|
}
|
|
994
1104
|
|
|
995
|
-
// must be in action directly after having parsed '{' or
|
|
1105
|
+
// must be in action directly after having parsed '{', '(`, or a keyword before
|
|
996
1106
|
function createDict() {
|
|
997
1107
|
const dict = Object.create(null);
|
|
998
1108
|
dict[$location] = this.startLocation( this._input.LT(-1) );
|
|
@@ -1174,4 +1284,14 @@ function checkTypeFacet( art, argIdent ) {
|
|
|
1174
1284
|
return false;
|
|
1175
1285
|
}
|
|
1176
1286
|
|
|
1287
|
+
function checkTypeArgs( art ) {
|
|
1288
|
+
const args = art.$typeArgs;
|
|
1289
|
+
// One or two arguments are interpreted as either length or precision/scale.
|
|
1290
|
+
if (args.length > 2) {
|
|
1291
|
+
const loc = args[2].location;
|
|
1292
|
+
this.error( 'syntax-unexpected-argument', loc, {}, 'Too many type arguments' );
|
|
1293
|
+
art.$typeArgs = undefined;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1177
1297
|
module.exports = GenericAntlrParser;
|