@sap/cds-compiler 2.15.4 → 3.0.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 +33 -1590
- package/bin/cdsc.js +36 -33
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -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 +220 -103
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +216 -109
- package/lib/base/message-registry.js +60 -20
- package/lib/base/messages.js +65 -24
- package/lib/base/model.js +44 -2
- package/lib/checks/actionsFunctions.js +7 -5
- 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 +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +87 -9
- package/lib/compiler/define.js +2 -2
- package/lib/compiler/extend.js +59 -11
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +13 -13
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +193 -218
- package/lib/compiler/shared.js +47 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +5 -0
- package/lib/edm/csn2edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +25 -30
- package/lib/edm/edmUtils.js +10 -24
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20632 -22313
- package/lib/json/from-csn.js +56 -49
- package/lib/json/to-csn.js +10 -8
- 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 +303 -229
- package/lib/language/language.g4 +573 -629
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +1 -1
- package/lib/model/csnUtils.js +170 -283
- package/lib/model/revealInternalProperties.js +28 -8
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +12 -21
- 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 +334 -339
- package/lib/render/toHdbcds.js +19 -15
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +53 -51
- package/lib/render/utils/common.js +15 -1
- 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/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- 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 +19 -22
- package/lib/transform/forOdataNew.js +10 -12
- package/lib/transform/localized.js +22 -16
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +3 -3
- 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 +3 -3
- package/lib/utils/timetrace.js +20 -21
- package/package.json +35 -4
- 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/fix_antlr4-8_warning.js +0 -56
package/lib/json/from-csn.js
CHANGED
|
@@ -135,7 +135,7 @@ const schemaClasses = {
|
|
|
135
135
|
condition: {
|
|
136
136
|
arrayOf: exprOrString,
|
|
137
137
|
type: condition,
|
|
138
|
-
msgId: 'syntax-
|
|
138
|
+
msgId: 'syntax-expected-term',
|
|
139
139
|
// TODO: also specify requires here, and adapt onlyWith()
|
|
140
140
|
optional: exprProperties,
|
|
141
141
|
},
|
|
@@ -145,11 +145,11 @@ const schemaClasses = {
|
|
|
145
145
|
},
|
|
146
146
|
natnumOrStar: {
|
|
147
147
|
type: natnumOrStar,
|
|
148
|
-
msgId: 'syntax-
|
|
148
|
+
msgId: 'syntax-expected-cardinality',
|
|
149
149
|
},
|
|
150
150
|
columns: {
|
|
151
151
|
arrayOf: selectItem,
|
|
152
|
-
msgId: 'syntax-
|
|
152
|
+
msgId: 'syntax-expected-column',
|
|
153
153
|
defaultKind: '$column',
|
|
154
154
|
validKinds: [], // pseudo kind '$column'
|
|
155
155
|
// A column with only as+cast.type is a new association
|
|
@@ -290,7 +290,7 @@ const schema = compileSchema( {
|
|
|
290
290
|
// type properties (except: elements, enum, keys, on): ---------------------
|
|
291
291
|
type: {
|
|
292
292
|
type: artifactRef,
|
|
293
|
-
msgId: 'syntax-
|
|
293
|
+
msgId: 'syntax-expected-reference',
|
|
294
294
|
optional: [ 'ref', 'global' ],
|
|
295
295
|
inKind: [ 'element', 'type', 'param', 'mixin', 'event', 'annotation' ],
|
|
296
296
|
},
|
|
@@ -366,7 +366,7 @@ const schema = compileSchema( {
|
|
|
366
366
|
ref: {
|
|
367
367
|
arrayOf: refItem,
|
|
368
368
|
type: renameTo( 'path', arrayOf( refItem ) ),
|
|
369
|
-
msgId: 'syntax-
|
|
369
|
+
msgId: 'syntax-expected-reference',
|
|
370
370
|
minLength: 1,
|
|
371
371
|
requires: 'id',
|
|
372
372
|
optional: [ 'id', 'args', 'cardinality', 'where' ],
|
|
@@ -582,7 +582,7 @@ const schema = compileSchema( {
|
|
|
582
582
|
inKind: [ 'element', '$column' ],
|
|
583
583
|
},
|
|
584
584
|
masked: {
|
|
585
|
-
type:
|
|
585
|
+
type: masked,
|
|
586
586
|
inKind: [ 'element' ],
|
|
587
587
|
},
|
|
588
588
|
notNull: {
|
|
@@ -779,7 +779,7 @@ function arrayOf( fn, filter = undefined ) {
|
|
|
779
779
|
} );
|
|
780
780
|
const minLength = spec.minLength || 0;
|
|
781
781
|
if (minLength > val.length) {
|
|
782
|
-
message( 'syntax-
|
|
782
|
+
message( 'syntax-expected-length', location(true),
|
|
783
783
|
{ prop: spec.prop, n: minLength, '#': minLength === 1 ? 'one' : 'std' });
|
|
784
784
|
}
|
|
785
785
|
if (val.length)
|
|
@@ -849,7 +849,7 @@ function object( obj, spec ) {
|
|
|
849
849
|
if (requires === undefined || requires === true) {
|
|
850
850
|
// console.log(csnProps,JSON.stringify(spec))
|
|
851
851
|
if (!relevantProps) {
|
|
852
|
-
error( 'syntax-
|
|
852
|
+
error( 'syntax-required-subproperty', location(true),
|
|
853
853
|
{
|
|
854
854
|
prop: spec.msgProp,
|
|
855
855
|
'#': (
|
|
@@ -875,7 +875,7 @@ function object( obj, spec ) {
|
|
|
875
875
|
|
|
876
876
|
function vZeroDelete( o, spec ) { // for old-CSN property 'origin'
|
|
877
877
|
if (!csnVersionZero) {
|
|
878
|
-
warning( 'syntax-
|
|
878
|
+
warning( 'syntax-zero-delete', location(true), { prop: spec.msgProp },
|
|
879
879
|
'Delete/inline CSN v0.1.0 property $(PROP)' );
|
|
880
880
|
}
|
|
881
881
|
string( o, spec );
|
|
@@ -960,7 +960,7 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
960
960
|
function dictionaryOf( elementFct ) {
|
|
961
961
|
return function dictionary( dict, spec ) {
|
|
962
962
|
if (!dict || typeof dict !== 'object' || Array.isArray( dict )) {
|
|
963
|
-
error( 'syntax-
|
|
963
|
+
error( 'syntax-expected-object', location(true),
|
|
964
964
|
{ prop: spec.prop }); // spec.prop, not spec.msgProp!
|
|
965
965
|
return ignore( dict );
|
|
966
966
|
}
|
|
@@ -972,7 +972,7 @@ function dictionaryOf( elementFct ) {
|
|
|
972
972
|
++virtualLine;
|
|
973
973
|
for (const name of allNames) {
|
|
974
974
|
if (!name) {
|
|
975
|
-
warning( 'syntax-
|
|
975
|
+
warning( 'syntax-empty-name', location(true),
|
|
976
976
|
{ prop: spec.prop }, // TODO: Error
|
|
977
977
|
'Property names in dictionary $(PROP) must not be empty' );
|
|
978
978
|
}
|
|
@@ -1049,11 +1049,11 @@ function validKind( val, spec, xsn ) {
|
|
|
1049
1049
|
if (val === xsn.kind) // has been set in definition - the same = ok!
|
|
1050
1050
|
return undefined; // already set in definition
|
|
1051
1051
|
if (val === 'view' && xsn.kind === 'entity') {
|
|
1052
|
-
warning( 'syntax-
|
|
1052
|
+
warning( 'syntax-zero-value', location(true), { prop: spec.msgProp },
|
|
1053
1053
|
'Replace CSN v0.1.0 value in $(PROP) by something specified' );
|
|
1054
1054
|
}
|
|
1055
1055
|
else if ((val === 'entity' || val === 'type') && xsn.kind === 'aspect') {
|
|
1056
|
-
info( 'syntax-
|
|
1056
|
+
info( 'syntax-aspect', location(true), { kind: 'aspect', '#': val },
|
|
1057
1057
|
{
|
|
1058
1058
|
std: 'Use the dedicated kind $(KIND) for aspect definitions',
|
|
1059
1059
|
// eslint-disable-next-line max-len
|
|
@@ -1061,7 +1061,7 @@ function validKind( val, spec, xsn ) {
|
|
|
1061
1061
|
} );
|
|
1062
1062
|
}
|
|
1063
1063
|
else {
|
|
1064
|
-
error( 'syntax-
|
|
1064
|
+
error( 'syntax-expected-valid', location(true), { prop: spec.msgProp },
|
|
1065
1065
|
'Expected valid string for property $(PROP)' );
|
|
1066
1066
|
}
|
|
1067
1067
|
return ignore( val );
|
|
@@ -1103,7 +1103,7 @@ function vZeroRef( name, spec, xsn ) {
|
|
|
1103
1103
|
return;
|
|
1104
1104
|
const path = name.split('.');
|
|
1105
1105
|
if (!path.every( id => id)) {
|
|
1106
|
-
warning( 'syntax-
|
|
1106
|
+
warning( 'syntax-expected-name', location(true), { prop: spec.msgProp },
|
|
1107
1107
|
'Expected correct name for property $(PROP)' );
|
|
1108
1108
|
}
|
|
1109
1109
|
xsn.path = path.map( id => ({ id, location: location() }) );
|
|
@@ -1114,7 +1114,7 @@ function vZeroRef( name, spec, xsn ) {
|
|
|
1114
1114
|
function boolOrNull( val, spec ) {
|
|
1115
1115
|
if ([ true, false, null ].includes( val ))
|
|
1116
1116
|
return { val, location: location() };
|
|
1117
|
-
warning( 'syntax-
|
|
1117
|
+
warning( 'syntax-expected-boolean', location(true), { prop: spec.msgProp },
|
|
1118
1118
|
'Expected boolean or null for property $(PROP)' );
|
|
1119
1119
|
ignore( val );
|
|
1120
1120
|
return { val: !!val, location: location() };
|
|
@@ -1124,7 +1124,7 @@ function string( val, spec ) {
|
|
|
1124
1124
|
if (typeof val === 'string' && val)
|
|
1125
1125
|
// XSN TODO: do not require literal
|
|
1126
1126
|
return val;
|
|
1127
|
-
error( 'syntax-
|
|
1127
|
+
error( 'syntax-expected-string', location(true), { prop: spec.msgProp },
|
|
1128
1128
|
'Expected non-empty string for property $(PROP)' );
|
|
1129
1129
|
return ignore( val );
|
|
1130
1130
|
}
|
|
@@ -1133,7 +1133,7 @@ function stringVal( val, spec ) {
|
|
|
1133
1133
|
if (typeof val === 'string' && val)
|
|
1134
1134
|
// XSN TODO: do not require literal
|
|
1135
1135
|
return { val, literal: 'string', location: location() };
|
|
1136
|
-
error( 'syntax-
|
|
1136
|
+
error( 'syntax-expected-string', location(true), { prop: spec.msgProp },
|
|
1137
1137
|
'Expected non-empty string for property $(PROP)' );
|
|
1138
1138
|
return ignore( val );
|
|
1139
1139
|
}
|
|
@@ -1155,7 +1155,7 @@ function natnum( val, spec ) {
|
|
|
1155
1155
|
if (typeof val === 'number' && val >= 0)
|
|
1156
1156
|
// XSN TODO: do not require literal
|
|
1157
1157
|
return { val, literal: 'number', location: location() };
|
|
1158
|
-
error( spec.msgId || 'syntax-
|
|
1158
|
+
error( spec.msgId || 'syntax-expected-natnum', location(true),
|
|
1159
1159
|
{ prop: spec.msgProp } );
|
|
1160
1160
|
return ignore( val );
|
|
1161
1161
|
}
|
|
@@ -1190,10 +1190,8 @@ function annoValue( val, spec ) {
|
|
|
1190
1190
|
/** @type {string|boolean} */
|
|
1191
1191
|
let seenEllipsis = false;
|
|
1192
1192
|
if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
|
|
1193
|
-
if (val.some( isEllipsis ))
|
|
1194
|
-
error( 'syntax-
|
|
1195
|
-
'Unexpected $(CODE) in nested array' );
|
|
1196
|
-
}
|
|
1193
|
+
if (val.some( isEllipsis ))
|
|
1194
|
+
error( 'syntax-unexpected-ellipsis', location(true), { '#': 'nested-array', code: '...' } );
|
|
1197
1195
|
}
|
|
1198
1196
|
else {
|
|
1199
1197
|
for (const item of val) {
|
|
@@ -1202,7 +1200,7 @@ function annoValue( val, spec ) {
|
|
|
1202
1200
|
}
|
|
1203
1201
|
else if (isEllipsis( item )) { // with or without UP TO
|
|
1204
1202
|
// error position at the beginning of the array, but that is fine
|
|
1205
|
-
error( 'syntax-
|
|
1203
|
+
error( 'syntax-duplicate-ellipsis', location(true), { code: '...' },
|
|
1206
1204
|
'Expected no more than one $(CODE)' );
|
|
1207
1205
|
break;
|
|
1208
1206
|
}
|
|
@@ -1216,7 +1214,7 @@ function annoValue( val, spec ) {
|
|
|
1216
1214
|
};
|
|
1217
1215
|
arrayLevelCount--;
|
|
1218
1216
|
if (seenEllipsis === 'upTo') {
|
|
1219
|
-
error( 'syntax-
|
|
1217
|
+
error( 'syntax-expecting-ellipsis', location(true), // at closing bracket
|
|
1220
1218
|
{ code: '... up to', newcode: '...' },
|
|
1221
1219
|
// TODO: should we be more CSN specific in the message?
|
|
1222
1220
|
'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
|
|
@@ -1263,11 +1261,14 @@ function annoValue( val, spec ) {
|
|
|
1263
1261
|
}
|
|
1264
1262
|
|
|
1265
1263
|
function annotation( val, spec, xsn, csn, name ) {
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1264
|
+
const absolute = (xsn ? name.substring(1) : name);
|
|
1265
|
+
// TODO: really care about variant (qualifier parts)?
|
|
1266
|
+
const variantIndex = absolute.indexOf('#') + 1 || absolute.length;
|
|
1267
|
+
const n = refSplit( absolute.substring( 0, variantIndex ), spec.msgProp );
|
|
1268
1268
|
if (!n)
|
|
1269
1269
|
return undefined;
|
|
1270
|
-
|
|
1270
|
+
n.absolute = absolute;
|
|
1271
|
+
if (variantIndex < absolute.length)
|
|
1271
1272
|
n.variant = { id: name.substring( variantIndex ), location: location() };
|
|
1272
1273
|
const r = annoValue( val, spec );
|
|
1273
1274
|
r.name = n;
|
|
@@ -1282,7 +1283,7 @@ function value( val, spec, xsn ) { // for CSN property 'val'
|
|
|
1282
1283
|
xsn.literal = (val === null) ? 'null' : typeof val;
|
|
1283
1284
|
return val;
|
|
1284
1285
|
}
|
|
1285
|
-
error( 'syntax-
|
|
1286
|
+
error( 'syntax-expected-scalar', location(true), { prop: spec.msgProp },
|
|
1286
1287
|
'Only scalar values are supported for property $(PROP)' );
|
|
1287
1288
|
return ignore( val );
|
|
1288
1289
|
}
|
|
@@ -1294,7 +1295,7 @@ function literal( val, spec, xsn, csn ) {
|
|
|
1294
1295
|
return val;
|
|
1295
1296
|
if (typeof val === 'string' && validLiteralsExtra[val] === type)
|
|
1296
1297
|
return val;
|
|
1297
|
-
error( 'syntax-
|
|
1298
|
+
error( 'syntax-expected-valid', location(true), { prop: spec.msgProp },
|
|
1298
1299
|
'Expected valid string for property $(PROP)' );
|
|
1299
1300
|
return ignore( val );
|
|
1300
1301
|
}
|
|
@@ -1309,7 +1310,7 @@ function func( val, spec, xsn ) {
|
|
|
1309
1310
|
function xpr( exprs, spec, xsn, csn ) {
|
|
1310
1311
|
if (csn.func) {
|
|
1311
1312
|
if (!exprs.length) {
|
|
1312
|
-
message( 'syntax-
|
|
1313
|
+
message( 'syntax-expected-length', location(true),
|
|
1313
1314
|
{ prop: 'xpr', otherprop: 'func', '#': 'suffix' });
|
|
1314
1315
|
}
|
|
1315
1316
|
xsn.suffix = exprArgs( exprs, spec );
|
|
@@ -1342,7 +1343,7 @@ function args( exprs, spec ) {
|
|
|
1342
1343
|
return arrayOf( exprOrString )( exprs, spec );
|
|
1343
1344
|
}
|
|
1344
1345
|
else if (!exprs || typeof exprs !== 'object') {
|
|
1345
|
-
error( 'syntax-
|
|
1346
|
+
error( 'syntax-expected-args', location(true),
|
|
1346
1347
|
{ prop: spec.prop }, // spec.prop, not spec.msgProp!
|
|
1347
1348
|
'Expected array or object for property $(PROP)' );
|
|
1348
1349
|
return ignore( exprs );
|
|
@@ -1405,12 +1406,12 @@ function condition( cond, spec ) {
|
|
|
1405
1406
|
function vZeroValue( obj, spec, xsn ) {
|
|
1406
1407
|
if (xsn.value) {
|
|
1407
1408
|
// TODO: also "sign" xsn.value created by inValue to complain about both 'value' and 'ref' etc
|
|
1408
|
-
warning( 'syntax-
|
|
1409
|
+
warning( 'syntax-unexpected-property', location(true), { prop: spec.msgProp },
|
|
1409
1410
|
'Unexpected CSN property $(PROP)' );
|
|
1410
1411
|
return undefined;
|
|
1411
1412
|
}
|
|
1412
1413
|
if (!csnVersionZero) {
|
|
1413
|
-
warning( 'syntax-
|
|
1414
|
+
warning( 'syntax-zero-delete', location(true), { prop: spec.msgProp },
|
|
1414
1415
|
'Delete/inline CSN v0.1.0 property $(PROP)' );
|
|
1415
1416
|
}
|
|
1416
1417
|
return expr( obj, spec );
|
|
@@ -1457,6 +1458,12 @@ function excluding( array, spec, xsn ) {
|
|
|
1457
1458
|
xsn.excludingDict = r;
|
|
1458
1459
|
}
|
|
1459
1460
|
|
|
1461
|
+
function masked( val, spec ) {
|
|
1462
|
+
message('syntax-invalid-masked', location(), { keyword: 'masked' },
|
|
1463
|
+
'Keyword $(KEYWORD) not supported');
|
|
1464
|
+
return boolOrNull( val, spec );
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1460
1467
|
function duplicateExcluding( name, loc ) {
|
|
1461
1468
|
error( 'duplicate-excluding', loc, { name, keyword: 'excluding' },
|
|
1462
1469
|
'Duplicate $(NAME) in the $(KEYWORD) clause' );
|
|
@@ -1477,7 +1484,7 @@ function join( val, spec, xsn ) {
|
|
|
1477
1484
|
|
|
1478
1485
|
function queryArgs( val, spec, xsn, csn ) {
|
|
1479
1486
|
if (Array.isArray( val ) && val.length > 1 && !csn.op) {
|
|
1480
|
-
warning( 'syntax-
|
|
1487
|
+
warning( 'syntax-expected-property', location(true),
|
|
1481
1488
|
{ prop: 'args', otherprop: 'op' },
|
|
1482
1489
|
'CSN property $(PROP) expects property $(OTHERPROP) to be specified' );
|
|
1483
1490
|
xsn.op = { val: 'union', location: location() };
|
|
@@ -1496,7 +1503,7 @@ function i18nLang( val, spec, xsn, csn, langKey ) {
|
|
|
1496
1503
|
function translations( keyVal, spec, xsn, csn, textKey ) {
|
|
1497
1504
|
if (typeof keyVal === 'string') // allow empty string
|
|
1498
1505
|
return { val: keyVal, literal: 'string', location: location() };
|
|
1499
|
-
error( 'syntax-
|
|
1506
|
+
error( 'syntax-expected-translation', location(true),
|
|
1500
1507
|
{ prop: textKey, otherprop: spec.prop },
|
|
1501
1508
|
'Expected string for text key $(PROP) of language $(OTHERPROP)' );
|
|
1502
1509
|
return ignore( keyVal );
|
|
@@ -1510,7 +1517,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1510
1517
|
if (!s || s.noPrefix && prop !== p0 ) {
|
|
1511
1518
|
if (ourpropsRegex.test( prop )) {
|
|
1512
1519
|
// TODO v2: Warning only with --sloppy
|
|
1513
|
-
warning( 'syntax-
|
|
1520
|
+
warning( 'syntax-unknown-property', location(true), { prop },
|
|
1514
1521
|
'Unknown CSN property $(PROP)' );
|
|
1515
1522
|
}
|
|
1516
1523
|
else { // TODO v2: always (i.e. also with message) add to $extra
|
|
@@ -1521,7 +1528,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1521
1528
|
if (s.ignore)
|
|
1522
1529
|
return { type: ignore };
|
|
1523
1530
|
if (s.vZeroIgnore && s.vZeroIgnore === csn[prop]) { // for "op": "call"
|
|
1524
|
-
warning( 'syntax-
|
|
1531
|
+
warning( 'syntax-zero-delete', location(true), { prop },
|
|
1525
1532
|
'Delete/inline CSN v0.1.0 property $(PROP)' );
|
|
1526
1533
|
return { type: ignore };
|
|
1527
1534
|
}
|
|
@@ -1540,7 +1547,7 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1540
1547
|
const variant = kind && s.inKind
|
|
1541
1548
|
? ([ 'extend', 'annotate' ].includes(kind) ? kind : 'def')
|
|
1542
1549
|
: (parentSpec.msgProp ? 'std' : 'top');
|
|
1543
|
-
message( 'syntax-
|
|
1550
|
+
message( 'syntax-unexpected-property', location(true),
|
|
1544
1551
|
{
|
|
1545
1552
|
prop, otherprop: parentSpec.msgProp, kind, '#': variant,
|
|
1546
1553
|
},
|
|
@@ -1598,13 +1605,13 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1598
1605
|
need = allowed.find( p => !xor[schema[p].xorGroup] ) || allowed[0];
|
|
1599
1606
|
}
|
|
1600
1607
|
if (prop) {
|
|
1601
|
-
error( 'syntax-
|
|
1608
|
+
error( 'syntax-dependent-property', location(true),
|
|
1602
1609
|
{ prop, otherprop: need },
|
|
1603
1610
|
'CSN property $(PROP) can only be used in combination with $(OTHERPROP)');
|
|
1604
1611
|
xor['no:req'] = prop;
|
|
1605
1612
|
}
|
|
1606
1613
|
else if (!xor['no:req']) {
|
|
1607
|
-
error( 'syntax-
|
|
1614
|
+
error( 'syntax-required-property', location(true),
|
|
1608
1615
|
{ prop: need, otherprop: spec.msgProp, '#': spec.prop },
|
|
1609
1616
|
{ // TODO $(PARENT), TODO: do not use prop===0 hack
|
|
1610
1617
|
std: 'Object in $(OTHERPROP) must have the property $(PROP)',
|
|
@@ -1626,7 +1633,7 @@ function checkAndSetXorGroup( group, prop, xor ) {
|
|
|
1626
1633
|
if (prop === 'func' && xor[group] === 'xpr' ||
|
|
1627
1634
|
prop === 'xpr' && xor[group] === 'func')
|
|
1628
1635
|
return true; // hack for window function: both func and xpr is allowed
|
|
1629
|
-
error( 'syntax-
|
|
1636
|
+
error( 'syntax-excluded-property', location(true),
|
|
1630
1637
|
{ prop, otherprop: xor[group] },
|
|
1631
1638
|
'CSN property $(PROP) can only be used alternatively to $(OTHERPROP)');
|
|
1632
1639
|
return false;
|
|
@@ -1641,7 +1648,7 @@ function implicitName( ref ) {
|
|
|
1641
1648
|
function replaceZeroProp( otherprop, prop ) {
|
|
1642
1649
|
if (csnVersionZero)
|
|
1643
1650
|
return;
|
|
1644
|
-
warning( 'syntax-
|
|
1651
|
+
warning( 'syntax-zero-prop', location(true), { prop, otherprop },
|
|
1645
1652
|
'Replace CSN v0.1.0 property $(OTHERPROP) by $(PROP)' );
|
|
1646
1653
|
}
|
|
1647
1654
|
|
|
@@ -1650,7 +1657,7 @@ function replaceZeroProp( otherprop, prop ) {
|
|
|
1650
1657
|
function isArray( array, spec ) {
|
|
1651
1658
|
if (Array.isArray( array ))
|
|
1652
1659
|
return array;
|
|
1653
|
-
error( 'syntax-
|
|
1660
|
+
error( 'syntax-expected-array', location(true), { prop: spec.prop },
|
|
1654
1661
|
'Expected array for property $(PROP)' );
|
|
1655
1662
|
return ignore( array );
|
|
1656
1663
|
}
|
|
@@ -1658,7 +1665,7 @@ function isArray( array, spec ) {
|
|
|
1658
1665
|
function isObject( obj, spec ) {
|
|
1659
1666
|
if (obj && typeof obj === 'object' && !Array.isArray( obj ))
|
|
1660
1667
|
return obj;
|
|
1661
|
-
error( spec.msgId || 'syntax-
|
|
1668
|
+
error( spec.msgId || 'syntax-expected-object', location(true),
|
|
1662
1669
|
{ prop: spec.msgProp });
|
|
1663
1670
|
return ignore( obj );
|
|
1664
1671
|
}
|
|
@@ -1666,7 +1673,7 @@ function isObject( obj, spec ) {
|
|
|
1666
1673
|
function refSplit( name, prop ) {
|
|
1667
1674
|
const path = name.split('.');
|
|
1668
1675
|
if (!path.every( id => id)) {
|
|
1669
|
-
warning( 'syntax-
|
|
1676
|
+
warning( 'syntax-expected-name', location(true), { prop },
|
|
1670
1677
|
'Expected correct name for property $(PROP)' );
|
|
1671
1678
|
}
|
|
1672
1679
|
return { path: path.map( id => ({ id, location: location() }) ), location: location() };
|
|
@@ -1674,7 +1681,7 @@ function refSplit( name, prop ) {
|
|
|
1674
1681
|
|
|
1675
1682
|
function replaceZeroValue( spec ) {
|
|
1676
1683
|
if (!csnVersionZero && spec.vZeroFor == null) { // but 0 does not match!
|
|
1677
|
-
warning( 'syntax-
|
|
1684
|
+
warning( 'syntax-zero-value', location(true), { prop: spec.msgProp },
|
|
1678
1685
|
'Replace CSN v0.1.0 value in $(PROP) by something specified' );
|
|
1679
1686
|
}
|
|
1680
1687
|
}
|
|
@@ -1704,7 +1711,7 @@ function pushLocation( obj ) {
|
|
|
1704
1711
|
else if (!loc || typeof loc !== 'string') {
|
|
1705
1712
|
if (loc)
|
|
1706
1713
|
dollarLocations.push( null ); // must match with popLocation()
|
|
1707
|
-
error( 'syntax-
|
|
1714
|
+
error( 'syntax-expected-object', location(true), { prop: '$location' } );
|
|
1708
1715
|
}
|
|
1709
1716
|
// hidden feature: string $location
|
|
1710
1717
|
const m = /:(\d+)(?::(\d+))?$/.exec( loc ); // extra '^'s at end deliberately left out
|
|
@@ -1817,7 +1824,7 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions )
|
|
|
1817
1824
|
line,
|
|
1818
1825
|
col: column,
|
|
1819
1826
|
};
|
|
1820
|
-
messageFunctions.error( 'syntax-
|
|
1827
|
+
messageFunctions.error( 'syntax-illegal-json', loc, { msg }, 'Illegal JSON: $(MSG)' );
|
|
1821
1828
|
return xsn;
|
|
1822
1829
|
}
|
|
1823
1830
|
}
|
package/lib/json/to-csn.js
CHANGED
|
@@ -222,7 +222,7 @@ const operators = {
|
|
|
222
222
|
unboundedFollowing: [ 'unbounded', 'following' ],
|
|
223
223
|
following: postfix( [ 'following' ] ),
|
|
224
224
|
frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
|
|
225
|
-
|
|
225
|
+
ixpr: exprs => [].concat( ...exprs ), // xpr extra, due to extra parentheses
|
|
226
226
|
};
|
|
227
227
|
|
|
228
228
|
const csnDictionaries = [
|
|
@@ -725,7 +725,8 @@ function annotationsAndDocComment( node, annotated ) {
|
|
|
725
725
|
const val = node[prop];
|
|
726
726
|
// val.$priority isn't set for computed annotations like @Core.Computed
|
|
727
727
|
// and @odata.containment.ignore
|
|
728
|
-
|
|
728
|
+
// TODO: use $inferred instead special $priority value
|
|
729
|
+
if (val.$priority !== undefined && (!!val.$priority) === annotated) {
|
|
729
730
|
// transformer (= value) takes care to exclude $inferred annotation assignments
|
|
730
731
|
const sub = transformer( val );
|
|
731
732
|
// As value() just has one value, so we do not provide ( val, csn, node, prop )
|
|
@@ -1588,10 +1589,11 @@ function compactExpr( e ) { // TODO: options
|
|
|
1588
1589
|
*/
|
|
1589
1590
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1590
1591
|
gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
|
|
1591
|
-
|
|
1592
|
-
universalCsn = (options.csnFlavor === 'universal' ||
|
|
1593
|
-
|
|
1594
|
-
|
|
1592
|
+
( options.toCsn && options.toCsn.flavor === 'gensrc');
|
|
1593
|
+
universalCsn = ( options.csnFlavor === 'universal' ||
|
|
1594
|
+
( options.toCsn && options.toCsn.flavor === 'universal') ) &&
|
|
1595
|
+
isBetaEnabled( options, 'enableUniversalCsn' ) &&
|
|
1596
|
+
!options.parseCdl;
|
|
1595
1597
|
strictMode = options.testMode;
|
|
1596
1598
|
const proto = options.dictionaryPrototype;
|
|
1597
1599
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -1599,8 +1601,8 @@ function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
|
1599
1601
|
? proto
|
|
1600
1602
|
: (proto) ? Object.prototype : null;
|
|
1601
1603
|
withLocations = options.withLocations;
|
|
1602
|
-
parensAsStrings = isDeprecatedEnabled( options, '
|
|
1603
|
-
projectionAsQuery = isDeprecatedEnabled( options, '
|
|
1604
|
+
parensAsStrings = isDeprecatedEnabled( options, '_parensAsStrings' );
|
|
1605
|
+
projectionAsQuery = isDeprecatedEnabled( options, '_projectionAsQuery' );
|
|
1604
1606
|
}
|
|
1605
1607
|
|
|
1606
1608
|
module.exports = {
|
|
@@ -13,8 +13,8 @@ const antlr4 = require('antlr4');
|
|
|
13
13
|
const { CompileMessage } = require('../base/messages');
|
|
14
14
|
const errorStrategy = require('./errorStrategy');
|
|
15
15
|
|
|
16
|
-
const Parser = require('../gen/languageParser').
|
|
17
|
-
const Lexer = require('../gen/languageLexer').
|
|
16
|
+
const Parser = require('../gen/languageParser').default;
|
|
17
|
+
const Lexer = require('../gen/languageLexer').default;
|
|
18
18
|
|
|
19
19
|
// Error listener used for ANTLR4-generated parser
|
|
20
20
|
class ErrorListener extends antlr4.error.ErrorListener {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
isWhitespaceOrNewLineOnly,
|
|
5
|
+
isWhitespaceCharacterNoNewline,
|
|
6
|
+
cdlNewLineRegEx,
|
|
7
|
+
} = require('./textUtils');
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* Get the content of a JSDoc-like comment and remove all surrounding asterisks, etc.
|
|
@@ -18,39 +22,40 @@ function parseDocComment(comment) {
|
|
|
18
22
|
if (comment.length <= 5) // at least "/***/"
|
|
19
23
|
return null;
|
|
20
24
|
|
|
21
|
-
let lines =
|
|
25
|
+
let lines = comment.split(cdlNewLineRegEx);
|
|
22
26
|
|
|
23
27
|
if (lines.length === 1) {
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
const content = lines[0]
|
|
27
|
-
|
|
28
|
+
// Special case for one-liners.
|
|
29
|
+
// Remove "/***/" and trim white space and asterisks.
|
|
30
|
+
const content = lines[0]
|
|
31
|
+
.replace(/^\/[*]{2,}/, '')
|
|
32
|
+
.replace(/\*+\/$/, '')
|
|
33
|
+
.trim();
|
|
34
|
+
return isWhitespaceOrNewLineOnly(content) ? null : content;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
// If the comment already has content on the first line, i.e. after `/**`,
|
|
38
|
+
// its leading whitespace is ignored for whitespace trimming.
|
|
39
|
+
const hasContentOnFirstLine = /\/\*+\s*\S/.test(lines[0]);
|
|
40
|
+
|
|
41
|
+
// First line, i.e. header, is always trimmed from left.
|
|
42
|
+
lines[0] = removeHeaderFence(lines[0]).trimStart();
|
|
31
43
|
lines[lines.length - 1] = removeFooterFence(lines[lines.length - 1]);
|
|
32
44
|
|
|
33
|
-
if (
|
|
34
|
-
lines = lines.map((line, index) => ((index === 0) ? line : removeFence(line)));
|
|
35
|
-
}
|
|
36
|
-
else if (lines.length === 2) {
|
|
45
|
+
if (lines.length === 2) {
|
|
37
46
|
// Comment that is essentially just a header + footer.
|
|
38
|
-
// First line, i.e. header, is always trimmed from left.
|
|
39
|
-
lines[0] = lines[0].trimStart();
|
|
40
|
-
|
|
41
47
|
// If the second line starts with an asterisk then remove it.
|
|
42
|
-
// Otherwise trim all whitespace.
|
|
48
|
+
// Otherwise, trim all left whitespace.
|
|
43
49
|
if ((/^\s*[*]/.test(lines[1])))
|
|
44
50
|
lines[1] = removeFence(lines[1]);
|
|
45
51
|
else
|
|
46
52
|
lines[1] = lines[1].trimStart();
|
|
47
53
|
}
|
|
54
|
+
else if (isFencedComment(lines)) {
|
|
55
|
+
lines = lines.map((line, index) => ((index === 0) ? line : removeFence(line)));
|
|
56
|
+
}
|
|
48
57
|
else {
|
|
49
|
-
|
|
50
|
-
// Tabs are regarded as one space.
|
|
51
|
-
const spacesAtBeginning = firstNonEmptyLine.match(/^\s*/)[0].length;
|
|
52
|
-
if (spacesAtBeginning > 0)
|
|
53
|
-
lines = lines.map(line => removeWhitespace(line, spacesAtBeginning));
|
|
58
|
+
stripCommentIndentation(lines, hasContentOnFirstLine);
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
// Remove empty header and footer.
|
|
@@ -58,18 +63,47 @@ function parseDocComment(comment) {
|
|
|
58
63
|
const endIndex = (lines[lines.length - 1] === '') ? lines.length - 1 : lines.length;
|
|
59
64
|
|
|
60
65
|
const content = lines.slice(startIndex, endIndex).join('\n');
|
|
61
|
-
|
|
62
|
-
return isWhiteSpaceOnly(content) ? null : content;
|
|
66
|
+
return isWhitespaceOrNewLineOnly(content) ? null : content;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
70
|
+
* Strips and counts the indentation from the given comment string.
|
|
71
|
+
* This function is similar to the one in multiLineStringParser.js, but does not
|
|
72
|
+
* have special handling for the first and last line of the string.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* | hello
|
|
76
|
+
* | world
|
|
77
|
+
* | foo bar
|
|
78
|
+
* becomes
|
|
79
|
+
* | hello
|
|
80
|
+
* | world
|
|
81
|
+
* | foo bar
|
|
68
82
|
*
|
|
69
|
-
* @param {string}
|
|
83
|
+
* @param {string[]} lines String split into lines.
|
|
84
|
+
* @param {boolean} ignoreFirstLine Whether to ignore the first line for indentation counting.
|
|
70
85
|
*/
|
|
71
|
-
function
|
|
72
|
-
|
|
86
|
+
function stripCommentIndentation(lines, ignoreFirstLine) {
|
|
87
|
+
const n = lines.length;
|
|
88
|
+
|
|
89
|
+
const minIndent = lines.reduce((min, line, index) => {
|
|
90
|
+
// Blank lines are ignored.
|
|
91
|
+
if (isWhitespaceOrNewLineOnly(line) || (index === 0 && ignoreFirstLine))
|
|
92
|
+
return min;
|
|
93
|
+
|
|
94
|
+
let count = 0;
|
|
95
|
+
const length = Math.min(min, line.length);
|
|
96
|
+
while (count < length && isWhitespaceCharacterNoNewline(line[count])) {
|
|
97
|
+
count++;
|
|
98
|
+
}
|
|
99
|
+
return Math.min(min, count);
|
|
100
|
+
}, Number.MAX_SAFE_INTEGER);
|
|
101
|
+
|
|
102
|
+
for (let i = (ignoreFirstLine ? 1 : 0); i < n; ++i) {
|
|
103
|
+
// Note: Line may be empty and have fewer characters than `min`.
|
|
104
|
+
// In that case, slice() returns an empty string.
|
|
105
|
+
lines[i] = lines[i].slice(minIndent);
|
|
106
|
+
}
|
|
73
107
|
}
|
|
74
108
|
|
|
75
109
|
/**
|
|
@@ -85,17 +119,6 @@ function removeFence(line) {
|
|
|
85
119
|
return line.replace(/^\s*[*]\s?/, '');
|
|
86
120
|
}
|
|
87
121
|
|
|
88
|
-
/**
|
|
89
|
-
* Remove the TODO
|
|
90
|
-
*
|
|
91
|
-
* @param {string} line
|
|
92
|
-
* @param {number} spaces Number of whitespace to remove at the beginning of the line
|
|
93
|
-
* @returns {string} line without fence
|
|
94
|
-
*/
|
|
95
|
-
function removeWhitespace(line, spaces) {
|
|
96
|
-
return line.replace(new RegExp(`^\\s{0,${ spaces }}`), ''); // Trailing spaces with '*'? => .replace(/\s+[*]$/, '');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
122
|
/**
|
|
100
123
|
* Removes a header fence, i.e. '/**'.
|
|
101
124
|
* May remove more than two asterisks e.g. '/*******'
|