@sap/cds-compiler 5.5.0 → 5.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/lib/checks/cdsMap.js +27 -0
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/define.js +1 -0
- package/lib/gen/CdlParser.js +1089 -1085
- package/lib/model/cloneCsn.js +1 -5
- package/lib/parsers/AstBuildingParser.js +33 -24
- package/lib/parsers/CdlGrammar.g4 +19 -7
- package/lib/utils/objectUtils.js +13 -0
- package/package.json +1 -1
package/lib/model/cloneCsn.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { csnPropertyOrder } = require('../json/to-csn');
|
|
4
4
|
const { ModelError } = require('../base/error');
|
|
5
|
-
const { setHidden } = require('../utils/objectUtils');
|
|
5
|
+
const { setHidden, hasNonEnumerable } = require('../utils/objectUtils');
|
|
6
6
|
const { isAnnotationExpression } = require('../base/builtins');
|
|
7
7
|
|
|
8
8
|
const csnDictionaries = {
|
|
@@ -110,10 +110,6 @@ function cloneCsn( csn, options, sort ) {
|
|
|
110
110
|
return r;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
function hasNonEnumerable( object, property ) {
|
|
114
|
-
return Object.prototype.hasOwnProperty.call( object, property ) &&
|
|
115
|
-
!Object.prototype.propertyIsEnumerable.call( object, property );
|
|
116
|
-
}
|
|
117
113
|
|
|
118
114
|
/**
|
|
119
115
|
* Deeply clone the given CSN dictionary and return it.
|
|
@@ -71,11 +71,14 @@ class AstBuildingParser extends BaseParser {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
expectingArray() {
|
|
74
|
+
const savedState = this.s;
|
|
75
|
+
this.s = this.errorState;
|
|
74
76
|
let array = this.expectingArray_();
|
|
77
|
+
this.s = savedState;
|
|
75
78
|
// compatibility: replace true+false by Boolean - TODO: delete
|
|
76
79
|
if (array.includes( 'true' ))
|
|
77
80
|
array = [ 'Boolean', ...array.filter( n => n !== 'true' && n !== 'false' ) ];
|
|
78
|
-
return array.map( antlrName )
|
|
81
|
+
return array.map( tok => this.antlrName( tok ) )
|
|
79
82
|
.sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
|
|
80
83
|
}
|
|
81
84
|
|
|
@@ -83,7 +86,7 @@ class AstBuildingParser extends BaseParser {
|
|
|
83
86
|
const token = this.la();
|
|
84
87
|
const expecting = this.expectingArray();
|
|
85
88
|
const err = this.error( 'syntax-unexpected-token', token,
|
|
86
|
-
{ offending: antlrName( token ), expecting } );
|
|
89
|
+
{ offending: this.antlrName( token ), expecting } );
|
|
87
90
|
// No 'unwanted' variant, no 'syntax-missing-token'
|
|
88
91
|
err.expectedTokens = expecting;
|
|
89
92
|
}
|
|
@@ -318,10 +321,6 @@ class AstBuildingParser extends BaseParser {
|
|
|
318
321
|
return true;
|
|
319
322
|
}
|
|
320
323
|
|
|
321
|
-
inExpandInline() { // not as <cond>
|
|
322
|
-
this.dynamic_.inSelectItem = 'nested';
|
|
323
|
-
}
|
|
324
|
-
|
|
325
324
|
/**
|
|
326
325
|
* `;` between statements is optional only after a `}` (ex braces of structure
|
|
327
326
|
* values for annotations).
|
|
@@ -355,8 +354,11 @@ class AstBuildingParser extends BaseParser {
|
|
|
355
354
|
const { arrayAnno } = this.dynamic_;
|
|
356
355
|
if (!arrayAnno[0])
|
|
357
356
|
return false;
|
|
358
|
-
|
|
359
|
-
|
|
357
|
+
arrayAnno[0] = this.tokens[this.tokenIdx + 1]?.keyword;
|
|
358
|
+
}
|
|
359
|
+
else if (arg === 'bracket') {
|
|
360
|
+
// closing bracket not allowed if last `...` in array is with `up to
|
|
361
|
+
return typeof this.dynamic_.arrayAnno[0] !== 'string';
|
|
360
362
|
}
|
|
361
363
|
else { // orNotEmpty
|
|
362
364
|
return this.dynamic_.arrayAnno || this.lb().type !== '{';
|
|
@@ -788,16 +790,20 @@ class AstBuildingParser extends BaseParser {
|
|
|
788
790
|
}
|
|
789
791
|
}
|
|
790
792
|
|
|
793
|
+
// TODO: can we remove `;`/EOF from the expected-set for `annotate Foo with ⎀`?
|
|
791
794
|
checkWith( keyword ) {
|
|
792
795
|
if (this.lb() !== keyword)
|
|
793
796
|
return;
|
|
794
797
|
const tok = this.la();
|
|
795
|
-
|
|
796
|
-
|
|
798
|
+
const docTokenIndex = this.docCommentIndex &&
|
|
799
|
+
this.docComments[this.docCommentIndex - 1].location.tokenIndex;
|
|
800
|
+
if (docTokenIndex < tok.location.tokenIndex &&
|
|
801
|
+
docTokenIndex > this.lb().location.tokenIndex)
|
|
797
802
|
return;
|
|
798
|
-
|
|
803
|
+
// filter out what comes after current rule (no generic way necessary):
|
|
804
|
+
const expecting = this.expectingArray().filter( t => t !== '<EOF>' && t !== '\'}\'' );
|
|
799
805
|
const msg = this.warning( 'syntax-unexpected-semicolon', tok,
|
|
800
|
-
{ offending: antlrName( tok ), expecting, keyword: 'with' },
|
|
806
|
+
{ offending: this.antlrName( tok ), expecting, keyword: 'with' },
|
|
801
807
|
// eslint-disable-next-line @stylistic/js/max-len
|
|
802
808
|
'Unexpected $(OFFENDING), expecting $(EXPECTING) - ignored previous $(KEYWORD)' );
|
|
803
809
|
msg.expectedTokens = expecting;
|
|
@@ -841,14 +847,15 @@ class AstBuildingParser extends BaseParser {
|
|
|
841
847
|
}
|
|
842
848
|
|
|
843
849
|
reportExpandInline( column, isInline ) {
|
|
850
|
+
// called before matching `{`
|
|
844
851
|
const { name } = column;
|
|
845
852
|
if (column.value && !column.value.path) {
|
|
846
|
-
const token = this.la();
|
|
847
853
|
// improve error location when using "inline" `.{…}` after ref (arguments and
|
|
848
854
|
// filters not covered, not worth the effort); after an expression where
|
|
849
855
|
// the last token is an identifier, not the `.` is wrong, but the `{`:
|
|
850
|
-
|
|
851
|
-
|
|
856
|
+
const token = (isInline && this.tokens[this.tokenIdx - 2].type !== 'Id')
|
|
857
|
+
? this.lb()
|
|
858
|
+
: this.la();
|
|
852
859
|
this.error( 'syntax-unexpected-nested-proj', token,
|
|
853
860
|
{ code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }' },
|
|
854
861
|
'Unexpected $(CODE); nested projections can only be used after a reference' );
|
|
@@ -1287,6 +1294,17 @@ class AstBuildingParser extends BaseParser {
|
|
|
1287
1294
|
parent, 'elements', kind, seg );
|
|
1288
1295
|
}
|
|
1289
1296
|
}
|
|
1297
|
+
|
|
1298
|
+
// For compatibility with ANTLR-based parser:
|
|
1299
|
+
antlrName( type ) {
|
|
1300
|
+
if (typeof type !== 'string') {
|
|
1301
|
+
type = (!type.parsedAs && this.keywords[type.keyword ?? ''] != null ||
|
|
1302
|
+
type.parsedAs === 'keyword') && type.keyword || type.type;
|
|
1303
|
+
}
|
|
1304
|
+
if (/^[A-Z]+/.test( type ))// eslint-disable-next-line no-nested-ternary
|
|
1305
|
+
return (type === 'Id') ? 'Identifier' : (type === 'EOF') ? '<EOF>' : type;
|
|
1306
|
+
return (/^[a-z]+/.test( type )) ? type.toUpperCase() : `'${ type }'`;
|
|
1307
|
+
}
|
|
1290
1308
|
}
|
|
1291
1309
|
|
|
1292
1310
|
function addOneForDefinition( count, ext ) {
|
|
@@ -1309,15 +1327,6 @@ function relevantDigits( val ) {
|
|
|
1309
1327
|
}
|
|
1310
1328
|
|
|
1311
1329
|
|
|
1312
|
-
// For compatibility with ANTLR-based parser:
|
|
1313
|
-
function antlrName( type ) {
|
|
1314
|
-
if (typeof type !== 'string')
|
|
1315
|
-
type = (!type.parsedAs || type.parsedAs === 'keyword') && type.keyword || type.type;
|
|
1316
|
-
if (/^[A-Z]+/.test( type ))// eslint-disable-next-line no-nested-ternary
|
|
1317
|
-
return (type === 'Id') ? 'Identifier' : (type === 'EOF') ? '<EOF>' : type;
|
|
1318
|
-
return (/^[a-z]+/.test( type )) ? type.toUpperCase() : `'${ type }'`;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
1330
|
// Used for sorting in messages (TODO: make it part of messages.js?)
|
|
1322
1331
|
const token1sort = {
|
|
1323
1332
|
// 0: Identifier, Number, ...
|
|
@@ -208,7 +208,7 @@ aspectDef[ art, outer ]
|
|
|
208
208
|
@finally{ this.attachLocation( $art ); }
|
|
209
209
|
:
|
|
210
210
|
( ASPECT
|
|
211
|
-
| <hide> ABSTRACT { this.warning( 'syntax-deprecated-abstract', this.lb().
|
|
211
|
+
| <hide> ABSTRACT { this.warning( 'syntax-deprecated-abstract', this.combineLocation( this.lb(), this.la() ) ); }
|
|
212
212
|
ENTITY
|
|
213
213
|
)
|
|
214
214
|
name=namePath[ 'Type' ] // TODO: Type?
|
|
@@ -418,7 +418,7 @@ elementDef[ outer, art = undefined ]
|
|
|
418
418
|
( KEY { $art.key = this.valueWithLocation( true ); } )?
|
|
419
419
|
( <hide> MASKED { $art.masked = this.valueWithLocation( true ); }
|
|
420
420
|
{ this.message( 'syntax-unsupported-masked', this.lb(), { keyword: 'masked' } ); } )?
|
|
421
|
-
( ELEMENT { $art.$syntax = 'element'; } )?
|
|
421
|
+
( <hide> ELEMENT { $art.$syntax = 'element'; } )?
|
|
422
422
|
Id['Element'] <prepare=elementRestriction, arg=elem>
|
|
423
423
|
{ this.addDef( $art, $outer, 'elements', 'element', this.identAst() ); }
|
|
424
424
|
{ this.docComment( $art ); } annoAssignMid[ $art ]*
|
|
@@ -515,7 +515,7 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
|
|
|
515
515
|
// Annotate and Extend: main definitions ----------------------------------------
|
|
516
516
|
|
|
517
517
|
annotateArtifact[ art, outer ]
|
|
518
|
-
@finally{ this.
|
|
518
|
+
@finally{ this.attachLocation( $art ); }
|
|
519
519
|
:
|
|
520
520
|
name=namePath[ 'Ext' ]
|
|
521
521
|
( // direct element annotation:
|
|
@@ -538,10 +538,11 @@ annotateArtifact[ art, outer ]
|
|
|
538
538
|
annotateActionsBlock[ $art ]?
|
|
539
539
|
)
|
|
540
540
|
)
|
|
541
|
+
{ this.checkWith( $keyword ); }
|
|
541
542
|
;
|
|
542
543
|
|
|
543
544
|
extendArtifact[ art, outer ]
|
|
544
|
-
@finally{ this.
|
|
545
|
+
@finally{ this.attachLocation( $art ); }
|
|
545
546
|
:
|
|
546
547
|
name=namePath[ 'Ext' ]
|
|
547
548
|
( // direct element annotation:
|
|
@@ -586,6 +587,7 @@ extendArtifact[ art, outer ]
|
|
|
586
587
|
DEFINITIONS artifactsBlock[ $art, this.lb() ]
|
|
587
588
|
)?
|
|
588
589
|
)
|
|
590
|
+
{ this.checkWith( $keyword ); }
|
|
589
591
|
;
|
|
590
592
|
|
|
591
593
|
extendService[ art, outer ]
|
|
@@ -1115,6 +1117,8 @@ selectQuery returns[ default query = {} ]
|
|
|
1115
1117
|
excludingClause[ $query ]?
|
|
1116
1118
|
|
|
|
1117
1119
|
( ALL/DISTINCT { $query.quantifier = this.valueWithLocation(); } )?
|
|
1120
|
+
// TODO TOOL: move <prepare> to all branches if "simple", or with special <…,attach>
|
|
1121
|
+
{;} <prepare=inSelectItem, arg=sqlStyle>
|
|
1118
1122
|
( '*' { $query.columns = [ this.valueWithLocation() ]; }
|
|
1119
1123
|
| selectItemDef[ ($query.columns = []) ]
|
|
1120
1124
|
)
|
|
@@ -1692,7 +1696,7 @@ options{ minTokensMatched = 1 }
|
|
|
1692
1696
|
)*
|
|
1693
1697
|
)
|
|
1694
1698
|
)
|
|
1695
|
-
')'
|
|
1699
|
+
')' { this.finalizeDictOrArray( $pathStep.args ); }
|
|
1696
1700
|
)?
|
|
1697
1701
|
// TODO: not with function!
|
|
1698
1702
|
cardinalityAndFilter[ ...$ ]?
|
|
@@ -1972,11 +1976,19 @@ annoValue returns[ default value = {} ]
|
|
|
1972
1976
|
( sub=annoValue { $value.val.push( $sub ) }
|
|
1973
1977
|
|
|
|
1974
1978
|
<cond=arrayAnno, arg=ellipsis> ellipsis='...'
|
|
1975
|
-
( UP TO upTo=annoValue
|
|
1976
|
-
|
|
1979
|
+
( UP TO upTo=annoValue
|
|
1980
|
+
{ $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
|
|
1981
|
+
| { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
|
|
1982
|
+
)
|
|
1983
|
+
// TODO TOOL: if at last good state the command is ['g'],resume after the
|
|
1984
|
+
// gotos, do not execute its actions - ?
|
|
1985
|
+
// ( UP TO upTo=annoValue | { $upTo = undefined; } )
|
|
1986
|
+
// { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
|
|
1977
1987
|
)
|
|
1978
1988
|
( ',' | <exitLoop> )
|
|
1979
1989
|
)*
|
|
1990
|
+
// TODO TOOL: allow ( <cond=arrayAnno, arg=bracket> ']' )
|
|
1991
|
+
{ this.ec( 'arrayAnno', 'bracket' ); }<always>
|
|
1980
1992
|
']'
|
|
1981
1993
|
|
|
|
1982
1994
|
'(' $value=condition ')' { $value.$tokenTexts = this.ruleTokensText(); }
|
package/lib/utils/objectUtils.js
CHANGED
|
@@ -83,6 +83,18 @@ function setHidden( obj, prop, val ) {
|
|
|
83
83
|
} );
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Check if the given object has the property as non-enumerable
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} object
|
|
90
|
+
* @param {string} propertyName
|
|
91
|
+
* @returns {boolean}
|
|
92
|
+
*/
|
|
93
|
+
function hasNonEnumerable( object, propertyName ) {
|
|
94
|
+
return Object.prototype.hasOwnProperty.call( object, propertyName ) &&
|
|
95
|
+
!Object.prototype.propertyIsEnumerable.call( object, propertyName );
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
module.exports = {
|
|
87
99
|
copyPropIfExist,
|
|
88
100
|
createDict,
|
|
@@ -90,4 +102,5 @@ module.exports = {
|
|
|
90
102
|
forEachValue,
|
|
91
103
|
forEachKey,
|
|
92
104
|
setHidden,
|
|
105
|
+
hasNonEnumerable,
|
|
93
106
|
};
|