@sap/cds-compiler 3.4.2 → 3.4.4
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 +8 -0
- package/bin/cdsc.js +3 -4
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/lib/api/main.js +6 -3
- package/lib/base/message-registry.js +60 -37
- package/lib/base/messages.js +7 -3
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -6
- package/lib/compiler/builtins.js +13 -13
- package/lib/compiler/checks.js +50 -33
- package/lib/compiler/define.js +9 -6
- package/lib/compiler/extend.js +71 -45
- package/lib/compiler/finalize-parse-cdl.js +3 -3
- package/lib/compiler/populate.js +16 -5
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +4 -4
- package/lib/compiler/utils.js +14 -0
- package/lib/edm/annotations/genericTranslation.js +68 -56
- package/lib/edm/csn2edm.js +214 -174
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +1 -1
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/Dictionary.json +176 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4776 -4513
- package/lib/json/from-csn.js +21 -16
- package/lib/json/to-csn.js +37 -41
- package/lib/language/.eslintrc.json +4 -1
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +54 -95
- package/lib/language/language.g4 +92 -66
- package/lib/language/multiLineStringParser.js +2 -2
- package/lib/language/textUtils.js +2 -2
- package/lib/model/csnRefs.js +5 -0
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +99 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/toCdl.js +96 -127
- package/lib/render/toHdbcds.js +38 -35
- package/lib/render/toSql.js +75 -161
- package/lib/render/utils/common.js +133 -83
- package/lib/render/utils/delta.js +227 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/forOdataNew.js +26 -19
- package/lib/transform/localized.js +9 -8
- package/lib/transform/odata/typesExposure.js +26 -4
- package/lib/transform/transformUtilsNew.js +15 -8
- package/package.json +2 -3
- package/lib/modelCompare/filter.js +0 -83
|
@@ -89,6 +89,7 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
89
89
|
unaryOpForParens,
|
|
90
90
|
leftAssocBinaryOp,
|
|
91
91
|
classifyImplicitName,
|
|
92
|
+
warnIfColonFollows,
|
|
92
93
|
fragileAlias,
|
|
93
94
|
identAst,
|
|
94
95
|
functionAst,
|
|
@@ -109,7 +110,6 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
109
110
|
createArray,
|
|
110
111
|
finalizeDictOrArray,
|
|
111
112
|
createPrefixOp,
|
|
112
|
-
setOnce,
|
|
113
113
|
setMaxCardinality,
|
|
114
114
|
pushIdent,
|
|
115
115
|
handleComposition,
|
|
@@ -117,7 +117,6 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
117
117
|
reportExpandInline,
|
|
118
118
|
checkTypeFacet,
|
|
119
119
|
csnParseOnly,
|
|
120
|
-
disallowElementExtension,
|
|
121
120
|
noAssignmentInSameLine,
|
|
122
121
|
noSemicolonHere,
|
|
123
122
|
setLocalToken,
|
|
@@ -217,29 +216,12 @@ function setLocalTokenForId( tokenNameMap ) {
|
|
|
217
216
|
// // throw new antlr4.error.InputMismatchException(this);
|
|
218
217
|
// }
|
|
219
218
|
|
|
220
|
-
/**
|
|
221
|
-
* For element extensions (`extend E:elem` syntax).
|
|
222
|
-
* If `elemName.path` is set, remove the last extension from `$outer` and
|
|
223
|
-
* emit an error that the extension is invalid.
|
|
224
|
-
*
|
|
225
|
-
* @param {object} elemName
|
|
226
|
-
* @param {object} outer
|
|
227
|
-
* @param {string} extensionVariant
|
|
228
|
-
*/
|
|
229
|
-
function disallowElementExtension(elemName, outer, extensionVariant) {
|
|
230
|
-
if (elemName.path) {
|
|
231
|
-
const loc = this.tokenLocation(this.getCurrentToken());
|
|
232
|
-
this.error( 'syntax-invalid-extend', loc, { kind: extensionVariant } );
|
|
233
|
-
// remove last, i.e. new extension
|
|
234
|
-
outer.extensions.pop();
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
219
|
function noAssignmentInSameLine() {
|
|
239
220
|
const t = this.getCurrentToken();
|
|
240
221
|
if (t.text === '@' && t.line <= this._input.LT(-1).line) {
|
|
241
|
-
this.warning( 'syntax-
|
|
242
|
-
|
|
222
|
+
this.warning( 'syntax-missing-newline', t, { anno: '‹anno›' }, // TODO: single quotes, @()
|
|
223
|
+
// eslint-disable-next-line max-len
|
|
224
|
+
'Add a newline before $(ANNO) to indicate that it belongs to the next statement' );
|
|
243
225
|
}
|
|
244
226
|
}
|
|
245
227
|
|
|
@@ -267,7 +249,7 @@ const genericTokenTypes = {
|
|
|
267
249
|
intro: 'GenericIntro',
|
|
268
250
|
};
|
|
269
251
|
|
|
270
|
-
function prepareGenericKeywords( pathItem, expected = null) {
|
|
252
|
+
function prepareGenericKeywords( pathItem, expected = null ) {
|
|
271
253
|
const length = pathItem?.args?.length || 0;
|
|
272
254
|
const argPos = (expected ? length - 1 : length);
|
|
273
255
|
const func = pathItem?.id && specialFunctions[pathItem.id.toUpperCase()];
|
|
@@ -492,7 +474,7 @@ function tokenLocation( token, endToken = null ) {
|
|
|
492
474
|
return loc;
|
|
493
475
|
}
|
|
494
476
|
|
|
495
|
-
function isMultiLineToken(token) {
|
|
477
|
+
function isMultiLineToken( token ) {
|
|
496
478
|
return (
|
|
497
479
|
token.type === this.constructor.DocComment ||
|
|
498
480
|
token.type === this.constructor.String ||
|
|
@@ -584,6 +566,17 @@ function unaryOpForParens( query, val ) {
|
|
|
584
566
|
return { op: { val, location }, location, args: [ query ] };
|
|
585
567
|
}
|
|
586
568
|
|
|
569
|
+
// ANTLR on some OS might corrupt non-ASCII chars for messages
|
|
570
|
+
function warnIfColonFollows( anno ) {
|
|
571
|
+
const t = this.getCurrentToken();
|
|
572
|
+
if (t.text === ':') {
|
|
573
|
+
this.warning( 'syntax-missing-parens', anno.name.location,
|
|
574
|
+
{ code: '@‹anno›', op: ':', newcode: '@(‹anno›…)' },
|
|
575
|
+
// eslint-disable-next-line max-len
|
|
576
|
+
'When $(CODE) is followed by $(OP), use $(NEWCODE) for annotation assignments at this position' );
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
587
580
|
// If the token before the current one is a doc comment (ignoring other tokens
|
|
588
581
|
// on the hidden channel), put its "cleaned-up" text as value of property `doc`
|
|
589
582
|
// of arg `node` (which could be an array). Complain if `doc` is already set.
|
|
@@ -611,9 +604,9 @@ function docComment( node ) {
|
|
|
611
604
|
|
|
612
605
|
// Classify token (identifier category) for implicit names,
|
|
613
606
|
// to be used in the empty alternative to AS <explicitName>.
|
|
614
|
-
function classifyImplicitName( category, ref ) {
|
|
607
|
+
function classifyImplicitName( category, ref, tokpos = 1 ) {
|
|
615
608
|
if (!ref || ref.path && this.getCurrentToken().text !== '.') {
|
|
616
|
-
const implicit = this._input.LT(-1);
|
|
609
|
+
const implicit = this._input.LT( tokpos - 1 || -1 );
|
|
617
610
|
if (implicit.isIdentifier)
|
|
618
611
|
implicit.isIdentifier = category;
|
|
619
612
|
}
|
|
@@ -623,11 +616,11 @@ function fragileAlias( ast, safe = false ) {
|
|
|
623
616
|
if (this.getCurrentToken().text === '.')
|
|
624
617
|
return ast;
|
|
625
618
|
if (safe || ast.$delimited || !/^[a-zA-Z][a-zA-Z_]+$/.test( ast.id )) {
|
|
626
|
-
this.warning( 'syntax-
|
|
619
|
+
this.warning( 'syntax-deprecated-auto-as', ast.location, { keyword: 'as' },
|
|
627
620
|
'Please add the keyword $(KEYWORD) in front of the alias name' );
|
|
628
621
|
}
|
|
629
622
|
else { // configurable error
|
|
630
|
-
this.message( 'syntax-
|
|
623
|
+
this.message( 'syntax-missing-as', ast.location, { keyword: 'as' },
|
|
631
624
|
'Please add the keyword $(KEYWORD) in front of the alias name' );
|
|
632
625
|
}
|
|
633
626
|
return ast;
|
|
@@ -642,10 +635,9 @@ function identAst( token, category, noTokenTypeCheck = false ) {
|
|
|
642
635
|
id = '';
|
|
643
636
|
if (token.text[0] === '!') {
|
|
644
637
|
id = id.slice( 2, -1 ).replace( /]]/g, ']' );
|
|
645
|
-
if (!id)
|
|
646
|
-
this.
|
|
647
|
-
|
|
648
|
-
}
|
|
638
|
+
if (!id)
|
|
639
|
+
this.message( 'syntax-invalid-name', token, {} );
|
|
640
|
+
|
|
649
641
|
// $delimited is used to complain about ![$self] and other magic vars usage;
|
|
650
642
|
// we might complain about that already here via @arg{category}
|
|
651
643
|
return { id, $delimited: true, location: this.tokenLocation( token ) };
|
|
@@ -655,8 +647,7 @@ function identAst( token, category, noTokenTypeCheck = false ) {
|
|
|
655
647
|
// delimited:
|
|
656
648
|
id = id.slice( 1, -1 ).replace( /""/g, '"' );
|
|
657
649
|
if (!id) {
|
|
658
|
-
this.
|
|
659
|
-
'Delimited identifier must contain at least one character' );
|
|
650
|
+
this.message( 'syntax-invalid-name', token, {} );
|
|
660
651
|
}
|
|
661
652
|
else {
|
|
662
653
|
this.message( 'syntax-deprecated-ident', token, { delimited: id },
|
|
@@ -705,7 +696,7 @@ function valuePathAst( ref ) {
|
|
|
705
696
|
const item = path.find( i => i.args && i.$syntax !== ':' );
|
|
706
697
|
if (!item)
|
|
707
698
|
return ref;
|
|
708
|
-
this.error( 'syntax-
|
|
699
|
+
this.error( 'syntax-unsupported-method', item.location, {},
|
|
709
700
|
'Methods in expressions are not supported yet' );
|
|
710
701
|
path.broken = true;
|
|
711
702
|
path.length = 1;
|
|
@@ -819,7 +810,7 @@ function quotedLiteral( token, literal ) {
|
|
|
819
810
|
location,
|
|
820
811
|
};
|
|
821
812
|
|
|
822
|
-
function atChar(i) {
|
|
813
|
+
function atChar( i ) {
|
|
823
814
|
// Is only used with single-line strings.
|
|
824
815
|
return location.col + pos + i;
|
|
825
816
|
}
|
|
@@ -843,7 +834,7 @@ function pushIdent( path, ident, prefix ) {
|
|
|
843
834
|
endLine: ident.location.line,
|
|
844
835
|
endCol: ident.location.col,
|
|
845
836
|
};
|
|
846
|
-
this.error( 'syntax-
|
|
837
|
+
this.error( 'syntax-unexpected-space', wsLocation, {}, // TODO: really Error?
|
|
847
838
|
'Expected identifier after \'@\' but found whitespace' );
|
|
848
839
|
}
|
|
849
840
|
ident.location.line = tokenLoc.line;
|
|
@@ -900,15 +891,15 @@ function addDef( art, parent, env, kind, name ) {
|
|
|
900
891
|
dictAdd( parent[env], art.name.id, art, ( duplicateName, loc ) => {
|
|
901
892
|
// do not use function(), otherwise `this` is wrong:
|
|
902
893
|
if (kind === 0) {
|
|
903
|
-
this.error( 'duplicate-argument', loc, { name: duplicateName },
|
|
894
|
+
this.error( 'syntax-duplicate-argument', loc, { name: duplicateName },
|
|
904
895
|
'Duplicate value for parameter $(NAME)' );
|
|
905
896
|
}
|
|
906
897
|
else if (kind === '') {
|
|
907
|
-
this.error( 'duplicate-excluding', loc,
|
|
908
|
-
|
|
898
|
+
this.error( 'syntax-duplicate-excluding', loc,
|
|
899
|
+
{ name: duplicateName, keyword: 'excluding' } );
|
|
909
900
|
}
|
|
910
901
|
else {
|
|
911
|
-
this.error( 'duplicate-
|
|
902
|
+
this.error( 'syntax-duplicate-property', loc, { name: duplicateName },
|
|
912
903
|
'Duplicate value for structure property $(NAME)' );
|
|
913
904
|
}
|
|
914
905
|
} );
|
|
@@ -1026,33 +1017,14 @@ function leftAssocBinaryOp( left, opToken, eToken, right, extraProp = 'quantifie
|
|
|
1026
1017
|
return { op, args: [ left, right ], location: left.location };
|
|
1027
1018
|
}
|
|
1028
1019
|
|
|
1029
|
-
// Set property `prop` of `target` to value `value`. Issue error if that
|
|
1030
|
-
// property has been set before, while mentioning the keywords previously
|
|
1031
|
-
// provided (as arguments `tokens`).
|
|
1032
|
-
function setOnce( target, prop, value, ...tokens ) {
|
|
1033
|
-
const loc = this.tokenLocation( tokens[0], tokens[tokens.length - 1] );
|
|
1034
|
-
const prev = target[prop];
|
|
1035
|
-
if (prev) {
|
|
1036
|
-
this.error( 'syntax-repeated-option', loc, { option: prev.option },
|
|
1037
|
-
'Option $(OPTION) has already been specified' );
|
|
1038
|
-
}
|
|
1039
|
-
if (typeof value === 'boolean') {
|
|
1040
|
-
if (!value)
|
|
1041
|
-
loc.value = false;
|
|
1042
|
-
value = loc;
|
|
1043
|
-
}
|
|
1044
|
-
value.option = tokens.map( t => t.text.toUpperCase() ).join(' ');
|
|
1045
|
-
target[prop] = value;
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
1020
|
function setMaxCardinality( art, token, max ) {
|
|
1049
1021
|
const location = this.tokenLocation( token );
|
|
1050
1022
|
if (!art.cardinality) {
|
|
1051
1023
|
art.cardinality = { targetMax: Object.assign( { location }, max ), location };
|
|
1052
1024
|
}
|
|
1053
1025
|
else {
|
|
1054
|
-
this.warning( 'syntax-
|
|
1055
|
-
'The target cardinality has already been specified -
|
|
1026
|
+
this.warning( 'syntax-duplicate-cardinality', location, { keyword: token.text },
|
|
1027
|
+
'The target cardinality has already been specified - ignoring $(KEYWORD)' );
|
|
1056
1028
|
}
|
|
1057
1029
|
}
|
|
1058
1030
|
|
|
@@ -1069,33 +1041,20 @@ function handleComposition( cardinality, isComposition ) {
|
|
|
1069
1041
|
}
|
|
1070
1042
|
|
|
1071
1043
|
function associationInSelectItem( art ) {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
art.name = art.value.path[0];
|
|
1080
|
-
}
|
|
1081
|
-
else {
|
|
1082
|
-
// Use alias if provided, i.e. ignore art.value.path.
|
|
1083
|
-
this.error( 'query-unexpected-alias', art.name.location, {},
|
|
1084
|
-
'Unexpected alias for association' );
|
|
1085
|
-
}
|
|
1086
|
-
delete art.value;
|
|
1087
|
-
}
|
|
1088
|
-
else {
|
|
1089
|
-
const loc = isPath ? art.value.path[1].location : art.value.location;
|
|
1090
|
-
// If neither path nor alias are present, `query-req-name` is emitted in `populate.js`.
|
|
1091
|
-
if (isPath || art.name) {
|
|
1092
|
-
this.error( 'query-expected-identifier', loc, { '#': 'assoc' } );
|
|
1093
|
-
if (isPath)
|
|
1094
|
-
art.name = art.value.path[art.value.path.length - 1];
|
|
1095
|
-
|
|
1044
|
+
const { value } = art;
|
|
1045
|
+
const path = value?.path;
|
|
1046
|
+
// we cannot compare "just one token before `:`" because there might be annos
|
|
1047
|
+
if (path && path.length === 1 && !art.name && !art.expand && !art.inline) {
|
|
1048
|
+
const name = value.path[0];
|
|
1049
|
+
if (path.length === 1 && !name.args && !name.cardinality && !name.where) {
|
|
1050
|
+
art.name = name;
|
|
1096
1051
|
delete art.value;
|
|
1052
|
+
return art;
|
|
1097
1053
|
}
|
|
1098
1054
|
}
|
|
1055
|
+
this.error( 'syntax-unexpected-assoc', this.getCurrentToken(), {},
|
|
1056
|
+
'Unexpected association definition in select item' );
|
|
1057
|
+
return {}; // result of the association rules are written into /dev/null
|
|
1099
1058
|
}
|
|
1100
1059
|
|
|
1101
1060
|
function reportExpandInline( column, isInline ) {
|
|
@@ -1108,8 +1067,8 @@ function reportExpandInline( column, isInline ) {
|
|
|
1108
1067
|
if (isInline && !name && this._input.LT(-1).type >= this.constructor.Identifier)
|
|
1109
1068
|
token = this._input.LT(2);
|
|
1110
1069
|
this.error( 'syntax-unexpected-nested-proj', token,
|
|
1111
|
-
{
|
|
1112
|
-
|
|
1070
|
+
{ code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }' },
|
|
1071
|
+
'Unexpected $(CODE); nested projections can only be used after a reference' );
|
|
1113
1072
|
// continuation semantics:
|
|
1114
1073
|
// - add elements anyway (could lead to duplicate errors as usual)
|
|
1115
1074
|
// - no errors for refs inside expand/inline, but for refs in sibling expr
|
|
@@ -1117,25 +1076,25 @@ function reportExpandInline( column, isInline ) {
|
|
|
1117
1076
|
}
|
|
1118
1077
|
if (isInline && name) {
|
|
1119
1078
|
const location = this.tokenLocation( isInline, this._input.LT(-1) );
|
|
1120
|
-
this.error( 'syntax-unexpected-alias', location, {
|
|
1121
|
-
'Unexpected alias name before
|
|
1079
|
+
this.error( 'syntax-unexpected-alias', location, { code: '.{ ‹inline› }' },
|
|
1080
|
+
'Unexpected alias name before $(CODE)' );
|
|
1122
1081
|
// continuation semantics: ignore AS
|
|
1123
1082
|
}
|
|
1124
1083
|
}
|
|
1125
1084
|
|
|
1126
1085
|
function checkTypeFacet( art, argIdent ) {
|
|
1086
|
+
// TODO: use dictAddArray or dictAdd?
|
|
1127
1087
|
const { id } = argIdent;
|
|
1128
1088
|
if (id === 'length' || id === 'scale' || id === 'precision' || id === 'srid') {
|
|
1129
1089
|
if (art[id] !== undefined) {
|
|
1130
|
-
this.error( 'syntax-duplicate-argument', argIdent.location,
|
|
1131
|
-
{ '#': 'duplicate', code: id } );
|
|
1132
1090
|
this.error( 'syntax-duplicate-argument', art[id].location,
|
|
1133
|
-
{ '#': '
|
|
1091
|
+
{ '#': 'type', name: id } );
|
|
1092
|
+
// continuation semantics: use last
|
|
1134
1093
|
}
|
|
1135
1094
|
return true;
|
|
1136
1095
|
}
|
|
1137
|
-
this.error( 'syntax-
|
|
1138
|
-
|
|
1096
|
+
this.error( 'syntax-undefined-param', argIdent.location, { name: id },
|
|
1097
|
+
'There is no type parameter called $(NAME)');
|
|
1139
1098
|
return false;
|
|
1140
1099
|
}
|
|
1141
1100
|
|
package/lib/language/language.g4
CHANGED
|
@@ -239,10 +239,11 @@ annotationAssignment_paren[ art ]
|
|
|
239
239
|
{
|
|
240
240
|
this.meltKeywordToIdentifier();
|
|
241
241
|
if (this.isStraightBefore(')')) {
|
|
242
|
-
this
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
'
|
|
242
|
+
// TODO: or should we simple accept this without warning? (but still no CC)
|
|
243
|
+
this.warning( 'syntax-unexpected-right-paren',
|
|
244
|
+
this.tokenLocation( this.getCurrentToken() ),
|
|
245
|
+
{ offending: "')'", expecting: ['Identifier'], code: '@()' },
|
|
246
|
+
'Unexpected $(OFFENDING), expecting $(EXPECTING); ignoring $(CODE)' );
|
|
246
247
|
this.matchWildcard(); // we know it is the ')' - we do not reach the final match
|
|
247
248
|
return $ctx;
|
|
248
249
|
}
|
|
@@ -273,13 +274,7 @@ annotationAssignment_fix[ art ] locals[ assignment ]
|
|
|
273
274
|
{ $assignment = { name: {} }; }
|
|
274
275
|
annotationPath[ $assignment.name, 'anno' ]
|
|
275
276
|
annotationPathVariant[ $assignment.name ]?
|
|
276
|
-
{
|
|
277
|
-
var t = this.getCurrentToken();
|
|
278
|
-
if (t.text === ':')
|
|
279
|
-
this.warning( 'syntax-anno-short', $assignment.name.location,
|
|
280
|
-
{ code: '@(...)' },
|
|
281
|
-
'Better use $(CODE) for annotation assignments here' );
|
|
282
|
-
}
|
|
277
|
+
{ this.warnIfColonFollows( $assignment ); }
|
|
283
278
|
)
|
|
284
279
|
;
|
|
285
280
|
|
|
@@ -709,8 +704,7 @@ aspectDef[ art, outer ] locals[ name = {} ]
|
|
|
709
704
|
{ this.addDef( $art, $outer, 'artifacts', 'aspect', $name );
|
|
710
705
|
// backends do not like ['$'+'syntax']: ($ent ? 'entity' : 'aspect')
|
|
711
706
|
if ($ent)
|
|
712
|
-
this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent )
|
|
713
|
-
'Abstract entity definitions are deprecated; use aspect definitions instead' );
|
|
707
|
+
this.warning( 'syntax-deprecated-abstract', this.tokenLocation( $abs, $ent ) );
|
|
714
708
|
this.docComment( $art ); }
|
|
715
709
|
annotationAssignment_fix[ $art ]*
|
|
716
710
|
( ':'
|
|
@@ -751,12 +745,33 @@ typeDef[ art, outer ] locals[ name = {} ]
|
|
|
751
745
|
extendType[ art, outer ] locals[ name = {} ]
|
|
752
746
|
@after { this.attachLocation( $art ); }
|
|
753
747
|
:
|
|
754
|
-
// aspects are types, i.e. kind is 'type' for aspects
|
|
755
748
|
TYPE simplePath[ $name, 'Extend' ]
|
|
756
749
|
{ $art.expectedKind = 'type'; $art.name = $name;
|
|
757
750
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
758
751
|
}
|
|
759
|
-
extendWithOptElementsOrType
|
|
752
|
+
// extendWithOptElementsOrType + includeRef:
|
|
753
|
+
(
|
|
754
|
+
extendWithOptElementsNoWith[ art ]
|
|
755
|
+
|
|
|
756
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
757
|
+
annotationAssignment_ll1[ $art ]*
|
|
758
|
+
(
|
|
759
|
+
'{' { $art.elements = this.createDict(); }
|
|
760
|
+
elementDefOrExtend[ $art ]*
|
|
761
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
762
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
763
|
+
optionalSemi
|
|
764
|
+
|
|
|
765
|
+
// extend type|element Art with (length: 10);
|
|
766
|
+
typeNamedArgList[ $art ]
|
|
767
|
+
requiredSemi
|
|
768
|
+
|
|
|
769
|
+
requiredSemi
|
|
770
|
+
|
|
|
771
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
772
|
+
requiredSemi
|
|
773
|
+
)
|
|
774
|
+
)
|
|
760
775
|
;
|
|
761
776
|
|
|
762
777
|
annotationDef[ art, outer ] locals[ name = {} ]
|
|
@@ -779,12 +794,21 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
779
794
|
@after{ /* #ATN 1 */ this.attachLocation( $art ); }
|
|
780
795
|
:
|
|
781
796
|
simplePath[ $name, 'Extend' ]
|
|
782
|
-
( ':' simplePath[ $elemName, 'Element'] )?
|
|
783
|
-
{ this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
|
|
784
797
|
(
|
|
785
|
-
|
|
798
|
+
':' simplePath[ $elemName, 'Element']
|
|
799
|
+
{ this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
|
|
800
|
+
extendWithOptElementsOrType[ art ]
|
|
801
|
+
|
|
|
802
|
+
{ this.addExtension( $art, $outer, 'extend', $name ); }
|
|
803
|
+
extendWithOptElementsNoWith[ art ]
|
|
804
|
+
|
|
|
805
|
+
{ this.addExtension( $art, $outer, 'extend', $name ); }
|
|
806
|
+
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
786
807
|
annotationAssignment_ll1[ $art ]*
|
|
808
|
+
// #ATN: ELEMENTS, ENUM, DEFINITIONS, COLUMNS, ACTIONS are not reserved and
|
|
809
|
+
// could be includeRef
|
|
787
810
|
(
|
|
811
|
+
// all the alternatives from `extendWithOptElementsOrType` --------------
|
|
788
812
|
'{' { $art.elements = this.createDict(); }
|
|
789
813
|
elementDefOrExtend[ $art ]*
|
|
790
814
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
@@ -792,32 +816,33 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
792
816
|
optionalSemi
|
|
793
817
|
|
|
|
794
818
|
requiredSemi
|
|
795
|
-
)
|
|
796
|
-
|
|
|
797
|
-
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
798
|
-
annotationAssignment_ll1[ $art ]*
|
|
799
|
-
// #ATN: DEFINITIONS, COLUMNS, ACTIONS etc are not reserved and could be identifiers (ref).
|
|
800
|
-
// TODO: exclude "expected" according to disallowElementExtension()
|
|
801
|
-
(
|
|
802
|
-
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
803
|
-
requiredSemi
|
|
804
819
|
|
|
|
805
|
-
'{' { $art.elements = this.createDict(); }
|
|
820
|
+
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
806
821
|
elementDefOrExtend[ $art ]*
|
|
807
822
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
808
|
-
|
|
823
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
824
|
+
optionalSemi
|
|
825
|
+
|
|
|
826
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
827
|
+
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
828
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
809
829
|
optionalSemi
|
|
810
830
|
|
|
|
831
|
+
// extend Art with (length: 10);
|
|
832
|
+
// `with` is required, or we could have `extend String(length:10);`.
|
|
833
|
+
typeNamedArgList[ $art ]
|
|
834
|
+
requiredSemi
|
|
835
|
+
|
|
|
836
|
+
// extension alternatives for main definitions --------------------------
|
|
837
|
+
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
811
838
|
requiredSemi
|
|
812
839
|
|
|
|
813
|
-
{ this.disallowElementExtension( $elemName, $outer, 'definitions' ); }
|
|
814
840
|
DEFINITIONS
|
|
815
841
|
'{' { $art.artifacts = this.createDict(); }
|
|
816
842
|
artifactDef[ $art, true ]*
|
|
817
843
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
818
844
|
optionalSemi
|
|
819
845
|
|
|
|
820
|
-
{ this.disallowElementExtension( $elemName, $outer, 'columns' ); }
|
|
821
846
|
COLUMNS
|
|
822
847
|
'{' { $art.columns = []; }
|
|
823
848
|
(
|
|
@@ -829,44 +854,37 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
829
854
|
'}'
|
|
830
855
|
optionalSemi
|
|
831
856
|
|
|
|
832
|
-
{ this.disallowElementExtension( $elemName, $outer, 'actions' ); }
|
|
833
857
|
ACTIONS '{' { $art.actions = this.createDict(); }
|
|
834
858
|
actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
|
|
835
859
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
836
860
|
optionalSemi
|
|
837
|
-
|
|
|
838
|
-
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
839
|
-
elementDefOrExtend[ $art ]*
|
|
840
|
-
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
841
|
-
{ this.checkExtensionDict( $art.elements ); }
|
|
842
|
-
optionalSemi
|
|
843
|
-
|
|
|
844
|
-
ENUM '{' { $art.enum = this.createDict(); }
|
|
845
|
-
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
846
|
-
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
847
|
-
optionalSemi
|
|
848
|
-
|
|
|
849
|
-
// extend Art with (length: 10);
|
|
850
|
-
// `with` is required, or we could have `extend String(length:10);`.
|
|
851
|
-
typeNamedArgList[ $art ]
|
|
852
|
-
requiredSemi
|
|
853
861
|
)
|
|
854
862
|
)
|
|
855
863
|
;
|
|
856
864
|
|
|
857
865
|
extendWithOptElementsOrType[ art ]
|
|
858
866
|
:
|
|
867
|
+
extendWithOptElementsNoWith[ art ]
|
|
868
|
+
|
|
|
859
869
|
WITH { this.noSemicolonHere(); this.docComment( $art ); }
|
|
860
870
|
annotationAssignment_ll1[ $art ]*
|
|
861
871
|
(
|
|
862
|
-
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
863
|
-
requiredSemi
|
|
864
|
-
|
|
|
865
872
|
'{' { $art.elements = this.createDict(); }
|
|
866
873
|
elementDefOrExtend[ $art ]*
|
|
867
874
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
868
875
|
{ this.checkExtensionDict( $art.elements ); }
|
|
869
876
|
optionalSemi
|
|
877
|
+
|
|
|
878
|
+
ELEMENTS '{' { $art.elements = this.createDict(); }
|
|
879
|
+
elementDefOrExtend[ $art ]*
|
|
880
|
+
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
881
|
+
{ this.checkExtensionDict( $art.elements ); }
|
|
882
|
+
optionalSemi
|
|
883
|
+
|
|
|
884
|
+
ENUM '{' { $art.enum = this.createDict(); }
|
|
885
|
+
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
886
|
+
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
887
|
+
optionalSemi
|
|
870
888
|
|
|
|
871
889
|
// extend type|element Art with (length: 10);
|
|
872
890
|
typeNamedArgList[ $art ]
|
|
@@ -874,7 +892,10 @@ extendWithOptElementsOrType[ art ]
|
|
|
874
892
|
|
|
|
875
893
|
requiredSemi
|
|
876
894
|
)
|
|
877
|
-
|
|
895
|
+
;
|
|
896
|
+
|
|
897
|
+
extendWithOptElementsNoWith[ art ]
|
|
898
|
+
:
|
|
878
899
|
{ this.docComment( $art ); }
|
|
879
900
|
annotationAssignment_ll1[ $art ]*
|
|
880
901
|
(
|
|
@@ -1080,12 +1101,12 @@ mixinElementDef[ outer ] locals[ art = {} ]
|
|
|
1080
1101
|
|
|
|
1081
1102
|
typeRefOptArgs[ $art ]
|
|
1082
1103
|
( as='=' expression
|
|
1083
|
-
{ this.error( 'syntax-unsupported-field', $as ); }
|
|
1104
|
+
{ this.error( 'syntax-unsupported-calc-field', $as ); }
|
|
1084
1105
|
)?
|
|
1085
1106
|
)
|
|
1086
1107
|
|
|
|
1087
1108
|
as='=' expression
|
|
1088
|
-
{ this.error( 'syntax-unsupported-field', $as ); }
|
|
1109
|
+
{ this.error( 'syntax-unsupported-calc-field', $as ); }
|
|
1089
1110
|
)
|
|
1090
1111
|
requiredSemi
|
|
1091
1112
|
;
|
|
@@ -1216,7 +1237,7 @@ elementDefInner[ art, outer, allowEq ]
|
|
|
1216
1237
|
eq='=' e=expression // never introduce AS as syntax variant of '='
|
|
1217
1238
|
{
|
|
1218
1239
|
if (!$allowEq || $e.expr && !$e.expr.literal )
|
|
1219
|
-
this.error( 'syntax-unsupported-field', $eq );
|
|
1240
|
+
this.error( 'syntax-unsupported-calc-field', $eq );
|
|
1220
1241
|
else if ($e.expr)
|
|
1221
1242
|
$art.value = $e.expr;
|
|
1222
1243
|
}
|
|
@@ -1251,7 +1272,7 @@ selectItemDef[ outer ] locals[ art ]
|
|
|
1251
1272
|
selectItemDefBody[ $art, $outer ]
|
|
1252
1273
|
;
|
|
1253
1274
|
|
|
1254
|
-
selectItemDefBody[ art, outer ]
|
|
1275
|
+
selectItemDefBody[ art, outer ] locals[ assoc ]
|
|
1255
1276
|
@after{ /* #ATN 2 */ }
|
|
1256
1277
|
:
|
|
1257
1278
|
{ $outer.push( $art ); }
|
|
@@ -1261,7 +1282,7 @@ selectItemDefBody[ art, outer ]
|
|
|
1261
1282
|
// AS (using rule 'ident' instead of 'identNoKeyword') -> ambiguities
|
|
1262
1283
|
( as=AS n1=ident['Item'] { $art.name = $n1.id }
|
|
1263
1284
|
| n2=ident['Item'] { $art.name = this.fragileAlias( $n2.id, true ); }
|
|
1264
|
-
| {
|
|
1285
|
+
| { this.classifyImplicitName( 'Item', $e.expr ); }
|
|
1265
1286
|
)
|
|
1266
1287
|
{ if ($art.value && !$art.value.path) this.excludeExpected( ["'.'", "'{'"] );
|
|
1267
1288
|
else if ($art.name) this.excludeExpected( ["'.'"] );
|
|
@@ -1290,7 +1311,7 @@ selectItemDefBody[ art, outer ]
|
|
|
1290
1311
|
{ this.docComment( $art ); }
|
|
1291
1312
|
annotationAssignment_fix[ $art ]*
|
|
1292
1313
|
( ':'
|
|
1293
|
-
// #ATN: typeRefOptArgs can start with TYPE, REDIRECTED
|
|
1314
|
+
// #ATN: typeRefOptArgs can start with TYPE, REDIRECTED, ASSOCIATION
|
|
1294
1315
|
( re=REDIRECTED to=TO
|
|
1295
1316
|
{ $art.target = {}; }
|
|
1296
1317
|
simplePath[ $art.target, 'artref' ]
|
|
@@ -1303,15 +1324,20 @@ selectItemDefBody[ art, outer ]
|
|
|
1303
1324
|
| typeTypeOf[ $art ]
|
|
1304
1325
|
{ this.docComment( $art ); }
|
|
1305
1326
|
annotationAssignment_ll1[ $art ]*
|
|
1327
|
+
| l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1328
|
+
typeRefOptArgs[ $art ]
|
|
1329
|
+
{ this.docComment( $art ); }
|
|
1330
|
+
annotationAssignment_ll1[ $art ]*
|
|
1306
1331
|
| typeRefOptArgs[ $art ]
|
|
1307
1332
|
{ this.docComment( $art ); }
|
|
1308
1333
|
annotationAssignment_ll1[ $art ]*
|
|
1309
1334
|
|
|
|
1310
|
-
|
|
1335
|
+
{ $assoc = this.associationInSelectItem( $art ); }
|
|
1336
|
+
typeAssociationBase[ $assoc, false ]
|
|
1311
1337
|
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
1312
|
-
( typeToMany[ $
|
|
1313
|
-
|
|
1314
|
-
{
|
|
1338
|
+
( typeToMany[ $assoc ] | typeToOne[ $assoc ] | simplePath[ $assoc.target, 'artref' ] )
|
|
1339
|
+
ON cond=condition
|
|
1340
|
+
{ $assoc.on=$cond.cond; }
|
|
1315
1341
|
)
|
|
1316
1342
|
)?
|
|
1317
1343
|
;
|
|
@@ -1398,7 +1424,7 @@ elementProperties[ elem ]
|
|
|
1398
1424
|
nullability[ $elem ]?
|
|
1399
1425
|
|
|
|
1400
1426
|
eq='='
|
|
1401
|
-
{ this.error( 'syntax-unsupported-field', $eq ); }
|
|
1427
|
+
{ this.error( 'syntax-unsupported-calc-field', $eq ); }
|
|
1402
1428
|
expression
|
|
1403
1429
|
;
|
|
1404
1430
|
|
|
@@ -2230,7 +2256,7 @@ conditionTerm returns [ cond ]
|
|
|
2230
2256
|
{ $cond = { op: this.valueWithTokenLocation( 'exists', $ex ), args: [
|
|
2231
2257
|
{ param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' }
|
|
2232
2258
|
] };
|
|
2233
|
-
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic',
|
|
2259
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
|
|
2234
2260
|
}
|
|
2235
2261
|
|
|
|
2236
2262
|
ep=valuePath[ 'ref' ]
|
|
@@ -2367,7 +2393,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
|
|
|
2367
2393
|
{ $expr = $vp.qp;; $expr.scope = 'param'; }
|
|
2368
2394
|
| pp=Number
|
|
2369
2395
|
{ $expr = { param: this.numberLiteral( $pp ), scope: 'param' };
|
|
2370
|
-
this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional',
|
|
2396
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $pp ], { '#': 'positional', code: ':' + $pp.text } );
|
|
2371
2397
|
}
|
|
2372
2398
|
)
|
|
2373
2399
|
|
|
|
@@ -2375,7 +2401,7 @@ expressionTerm returns [ expr ] locals [ op, args = [] ]
|
|
|
2375
2401
|
// if we have an HideAlternatives here, we would block it to use it in
|
|
2376
2402
|
// parallel to an expression (would produce adaptivePredict() otherwise)
|
|
2377
2403
|
{ $expr = { param: this.valueWithTokenLocation( '?', $qm ), scope: 'param' };
|
|
2378
|
-
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic',
|
|
2404
|
+
this.csnParseOnly( 'syntax-unsupported-param', [ $qm ], { '#': 'dynamic', code: '?' } );
|
|
2379
2405
|
}
|
|
2380
2406
|
|
|
|
2381
2407
|
open='('
|
|
@@ -24,7 +24,7 @@ const {
|
|
|
24
24
|
* @returns {[string, number]} The indentation-stripped string and the number
|
|
25
25
|
* of whitespace characters removed.
|
|
26
26
|
*/
|
|
27
|
-
function stripIndentation(str) {
|
|
27
|
+
function stripIndentation( str ) {
|
|
28
28
|
if (str === '')
|
|
29
29
|
return [ '', 0 ];
|
|
30
30
|
|
|
@@ -510,7 +510,7 @@ class MultiLineStringParser {
|
|
|
510
510
|
*
|
|
511
511
|
* @param {object} token
|
|
512
512
|
*/
|
|
513
|
-
function parseMultiLineStringLiteral(token) {
|
|
513
|
+
function parseMultiLineStringLiteral( token ) {
|
|
514
514
|
const p = new MultiLineStringParser(this, token);
|
|
515
515
|
return p.parse();
|
|
516
516
|
}
|
|
@@ -11,7 +11,7 @@ const cdlNewLineRegEx = /\r\n?|\n|\u2028|\u2029/u;
|
|
|
11
11
|
* @param {string} str
|
|
12
12
|
* @returns {boolean}
|
|
13
13
|
*/
|
|
14
|
-
function isWhitespaceOrNewLineOnly(str) {
|
|
14
|
+
function isWhitespaceOrNewLineOnly( str ) {
|
|
15
15
|
return /^\s*$/.test(str);
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -33,7 +33,7 @@ function isWhitespaceOrNewLineOnly(str) {
|
|
|
33
33
|
* @param char
|
|
34
34
|
* @returns {boolean}
|
|
35
35
|
*/
|
|
36
|
-
function isWhitespaceCharacterNoNewline(char) {
|
|
36
|
+
function isWhitespaceCharacterNoNewline( char ) {
|
|
37
37
|
return whitespaceRegEx.test(char);
|
|
38
38
|
}
|
|
39
39
|
|