@sap/cds-compiler 3.3.2 → 3.4.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 +33 -0
- package/bin/cdsc.js +3 -1
- package/doc/CHANGELOG_BETA.md +17 -0
- package/lib/api/main.js +147 -18
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/keywords.js +104 -0
- package/lib/base/message-registry.js +137 -68
- package/lib/base/messages.js +59 -48
- package/lib/base/model.js +1 -0
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +13 -8
- package/lib/checks/defaultValues.js +3 -1
- package/lib/checks/elements.js +1 -1
- package/lib/checks/parameters.js +4 -2
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/validator.js +14 -4
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/checks.js +30 -20
- package/lib/compiler/define.js +89 -25
- package/lib/compiler/extend.js +33 -28
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/populate.js +30 -8
- package/lib/compiler/propagator.js +23 -28
- package/lib/compiler/resolve.js +11 -5
- package/lib/compiler/shared.js +66 -48
- package/lib/compiler/tweak-assocs.js +2 -3
- package/lib/compiler/utils.js +11 -0
- package/lib/edm/annotations/genericTranslation.js +7 -4
- package/lib/edm/csn2edm.js +1 -1
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +3565 -3544
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +140 -158
- package/lib/json/to-csn.js +23 -5
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +7 -10
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +115 -84
- package/lib/language/language.g4 +29 -25
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.js +1 -0
- package/lib/model/csnRefs.js +4 -3
- package/lib/model/csnUtils.js +39 -7
- package/lib/model/sortViews.js +7 -3
- package/lib/modelCompare/compare.js +49 -15
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +5 -1
- package/lib/render/manageConstraints.js +9 -5
- package/lib/render/toCdl.js +120 -62
- package/lib/render/toHdbcds.js +1 -1
- package/lib/render/toSql.js +6 -2
- package/lib/render/utils/common.js +7 -0
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +11 -4
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +7 -1
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +7 -3
- package/lib/transform/forRelationalDB.js +12 -6
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +23 -14
- package/lib/transform/translateAssocsToJoins.js +12 -12
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -0
- package/lib/utils/term.js +5 -5
- package/package.json +2 -2
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +1 -1
package/lib/json/from-csn.js
CHANGED
|
@@ -68,10 +68,8 @@
|
|
|
68
68
|
* may not.
|
|
69
69
|
* @property {string} [xsnOp] Defines the operator to be used for XSN. Used for SET
|
|
70
70
|
* and SELECT. See queryTerm().
|
|
71
|
-
* @property {string
|
|
71
|
+
* @property {string} [vZeroFor] Marks the property as a CSN 0.1.0 property. It is
|
|
72
72
|
* replaced by this CSN 1.0 property (value of vZeroFor).
|
|
73
|
-
* "false" indicates that the property may be a v0.1 one
|
|
74
|
-
* which is handled specially, e.g. with "type:vZeroValue"
|
|
75
73
|
* @property {string} [vZeroIgnore] Marks the property as a CSN 0.1.0 property. The
|
|
76
74
|
* property is ignored and a warning may be issues about
|
|
77
75
|
* it.
|
|
@@ -138,7 +136,7 @@ const schemaClasses = {
|
|
|
138
136
|
condition: {
|
|
139
137
|
arrayOf: exprOrString,
|
|
140
138
|
type: condition,
|
|
141
|
-
msgId: 'syntax-
|
|
139
|
+
msgId: 'syntax-expecting-term',
|
|
142
140
|
// TODO: also specify requires here, and adapt onlyWith()
|
|
143
141
|
optional: exprProperties,
|
|
144
142
|
},
|
|
@@ -148,11 +146,11 @@ const schemaClasses = {
|
|
|
148
146
|
},
|
|
149
147
|
natnumOrStar: {
|
|
150
148
|
type: natnumOrStar,
|
|
151
|
-
msgId: 'syntax-
|
|
149
|
+
msgId: 'syntax-expecting-cardinality',
|
|
152
150
|
},
|
|
153
151
|
columns: {
|
|
154
152
|
arrayOf: selectItem,
|
|
155
|
-
msgId: 'syntax-
|
|
153
|
+
msgId: 'syntax-expecting-column',
|
|
156
154
|
defaultKind: '$column',
|
|
157
155
|
validKinds: [], // pseudo kind '$column'
|
|
158
156
|
// A column with only as+cast.type is a new association
|
|
@@ -296,17 +294,21 @@ const schema = compileSchema( {
|
|
|
296
294
|
// type properties (except: elements, enum, keys, on): ---------------------
|
|
297
295
|
type: {
|
|
298
296
|
type: artifactRef,
|
|
299
|
-
msgId: 'syntax-
|
|
297
|
+
msgId: 'syntax-expecting-reference',
|
|
300
298
|
optional: [ 'ref', 'global' ],
|
|
301
299
|
inKind: [ 'element', 'type', 'param', 'mixin', 'event', 'annotation', 'extend' ],
|
|
302
300
|
},
|
|
303
301
|
targetAspect: {
|
|
304
302
|
type: artifactRef,
|
|
303
|
+
msgId: 'syntax-expecting-reference',
|
|
304
|
+
requires: 'elements',
|
|
305
305
|
optional: [ 'elements' ], // 'elements' for ad-hoc aspect compositions
|
|
306
306
|
inKind: [ 'element', 'type' ],
|
|
307
307
|
},
|
|
308
308
|
target: {
|
|
309
309
|
type: artifactRef,
|
|
310
|
+
msgId: 'syntax-expecting-reference',
|
|
311
|
+
requires: 'elements',
|
|
310
312
|
optional: [ 'elements' ], // 'elements' for ad-hoc COMPOSITION OF (gensrc style CSN)
|
|
311
313
|
inKind: [ 'element', 'type', 'mixin', 'param' ],
|
|
312
314
|
},
|
|
@@ -372,7 +374,7 @@ const schema = compileSchema( {
|
|
|
372
374
|
ref: {
|
|
373
375
|
arrayOf: refItem,
|
|
374
376
|
type: renameTo( 'path', arrayOf( refItem ) ),
|
|
375
|
-
msgId: 'syntax-
|
|
377
|
+
msgId: 'syntax-expecting-reference',
|
|
376
378
|
minLength: 1,
|
|
377
379
|
requires: 'id',
|
|
378
380
|
optional: [ 'id', 'args', 'cardinality', 'where' ],
|
|
@@ -638,14 +640,13 @@ const schema = compileSchema( {
|
|
|
638
640
|
origin: { // old-style CSN
|
|
639
641
|
type: vZeroDelete, ignore: true,
|
|
640
642
|
},
|
|
641
|
-
source: { // CSN v0.1.0 query not supported
|
|
643
|
+
source: { // CSN v0.1.0 query not supported (is error)
|
|
642
644
|
type: ignore,
|
|
643
645
|
},
|
|
644
646
|
value: {
|
|
645
|
-
vZeroFor:
|
|
646
|
-
type:
|
|
647
|
-
|
|
648
|
-
inKind: [ '$column', 'enum' ],
|
|
647
|
+
vZeroFor: 'val', // CSN v0.1.0 property for `val` in enum def
|
|
648
|
+
type: annoValue,
|
|
649
|
+
// inKind: [ 'enum' ],
|
|
649
650
|
},
|
|
650
651
|
// ignored: ----------------------------------------------------------------
|
|
651
652
|
$location: { // special
|
|
@@ -780,8 +781,8 @@ function arrayOf( fn, filter = undefined ) {
|
|
|
780
781
|
} );
|
|
781
782
|
const minLength = spec.minLength || 0;
|
|
782
783
|
if (minLength > val.length) {
|
|
783
|
-
|
|
784
|
-
|
|
784
|
+
error( 'syntax-incomplete-array', location(true),
|
|
785
|
+
{ prop: spec.prop, n: minLength, '#': minLength === 1 ? 'one' : 'std' });
|
|
785
786
|
}
|
|
786
787
|
if (val.length)
|
|
787
788
|
++virtualLine; // [] in one JSON line
|
|
@@ -850,20 +851,8 @@ function object( obj, spec ) {
|
|
|
850
851
|
if (requires === undefined || requires === true) {
|
|
851
852
|
// console.log(csnProps,JSON.stringify(spec))
|
|
852
853
|
if (!relevantProps) {
|
|
853
|
-
error( 'syntax-
|
|
854
|
-
{
|
|
855
|
-
prop: spec.msgProp,
|
|
856
|
-
'#': (
|
|
857
|
-
// eslint-disable-next-line no-nested-ternary
|
|
858
|
-
!csnProps.length ? 'std'
|
|
859
|
-
: csnProps.length === 1 && csnProps[0] === 'as' ? 'as'
|
|
860
|
-
: 'relevant'),
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
std: 'Object in $(PROP) must have at least one property',
|
|
864
|
-
as: 'Object in $(PROP) must have at least one property other than \'as\'',
|
|
865
|
-
relevant: 'Object in $(PROP) must have at least one relevant property',
|
|
866
|
-
} );
|
|
854
|
+
error( 'syntax-incomplete-object', location(true),
|
|
855
|
+
{ '#': (obj.as != null ? 'as' : 'std'), prop: spec.msgProp, otherprop: 'as' } );
|
|
867
856
|
}
|
|
868
857
|
}
|
|
869
858
|
else if (requires) {
|
|
@@ -876,10 +865,10 @@ function object( obj, spec ) {
|
|
|
876
865
|
|
|
877
866
|
function vZeroDelete( o, spec ) { // for old-CSN property 'origin'
|
|
878
867
|
if (!csnVersionZero) {
|
|
879
|
-
warning( 'syntax-
|
|
880
|
-
'
|
|
868
|
+
warning( 'syntax-deprecated-property', location(true),
|
|
869
|
+
{ '#': 'zero', prop: spec.msgProp } );
|
|
881
870
|
}
|
|
882
|
-
|
|
871
|
+
ignore( o );
|
|
883
872
|
}
|
|
884
873
|
|
|
885
874
|
// Definitions, dictionaries and arrays of definitions (std signature) -------
|
|
@@ -889,7 +878,7 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
889
878
|
return {
|
|
890
879
|
kind: (inExtensions ? 'annotate' : spec.defaultKind),
|
|
891
880
|
name: {
|
|
892
|
-
id: '', path: [], absolute: name, location: location(),
|
|
881
|
+
id: '', path: [], absolute: name || '', location: location(),
|
|
893
882
|
},
|
|
894
883
|
location: location(),
|
|
895
884
|
};
|
|
@@ -922,7 +911,6 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
922
911
|
r.name.$inferred = 'as';
|
|
923
912
|
// TODO the following 'if' (if necessary) should be part of the core compiler
|
|
924
913
|
if (prop === 'definitions' || prop === 'vocabularies') { // as spec property
|
|
925
|
-
// xsn.name.path = name.split('.').map( id => ({ id, location: location() }) );
|
|
926
914
|
r.name = {
|
|
927
915
|
absolute: name,
|
|
928
916
|
id: name.substring( name.lastIndexOf('.') + 1 ),
|
|
@@ -961,7 +949,7 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
961
949
|
function dictionaryOf( elementFct ) {
|
|
962
950
|
return function dictionary( dict, spec ) {
|
|
963
951
|
if (!dict || typeof dict !== 'object' || Array.isArray( dict )) {
|
|
964
|
-
error( 'syntax-
|
|
952
|
+
error( 'syntax-expecting-object', location(true),
|
|
965
953
|
{ prop: spec.prop }); // spec.prop, not spec.msgProp!
|
|
966
954
|
return ignore( dict );
|
|
967
955
|
}
|
|
@@ -973,9 +961,8 @@ function dictionaryOf( elementFct ) {
|
|
|
973
961
|
++virtualLine;
|
|
974
962
|
for (const name of allNames) {
|
|
975
963
|
if (!name) {
|
|
976
|
-
warning( 'syntax-
|
|
977
|
-
{
|
|
978
|
-
'Property names in dictionary $(PROP) must not be empty' );
|
|
964
|
+
warning( 'syntax-invalid-name', location(true), // TODO: Error
|
|
965
|
+
{ '#': 'csn', parentprop: spec.prop } );
|
|
979
966
|
}
|
|
980
967
|
const val = elementFct( dict[name], spec, r, dict, name );
|
|
981
968
|
if (val !== undefined)
|
|
@@ -1050,9 +1037,11 @@ function validKind( val, spec, xsn ) {
|
|
|
1050
1037
|
if (val === xsn.kind) // has been set in definition - the same = ok!
|
|
1051
1038
|
return undefined; // already set in definition
|
|
1052
1039
|
if (val === 'view' && xsn.kind === 'entity') {
|
|
1053
|
-
warning( 'syntax-
|
|
1054
|
-
'
|
|
1040
|
+
warning( 'syntax-deprecated-value', location(true),
|
|
1041
|
+
{ '#': 'replace', prop: spec.msgProp, value: 'entity' } );
|
|
1055
1042
|
}
|
|
1043
|
+
// TODO: rather issue info at `abstract` and `$syntax`, i.e. current location is strange
|
|
1044
|
+
// change message id in a later change
|
|
1056
1045
|
else if ((val === 'entity' || val === 'type') && xsn.kind === 'aspect') {
|
|
1057
1046
|
info( 'syntax-aspect', location(true), { kind: 'aspect', '#': val },
|
|
1058
1047
|
{
|
|
@@ -1062,8 +1051,8 @@ function validKind( val, spec, xsn ) {
|
|
|
1062
1051
|
} );
|
|
1063
1052
|
}
|
|
1064
1053
|
else {
|
|
1065
|
-
error( 'syntax-
|
|
1066
|
-
'
|
|
1054
|
+
error( 'syntax-invalid-kind', location(true), { prop: spec.msgProp },
|
|
1055
|
+
'Invalid value for property $(PROP)' );
|
|
1067
1056
|
}
|
|
1068
1057
|
return ignore( val );
|
|
1069
1058
|
}
|
|
@@ -1071,12 +1060,16 @@ function validKind( val, spec, xsn ) {
|
|
|
1071
1060
|
function artifactRef( ref, spec ) {
|
|
1072
1061
|
if (!ref || typeof ref !== 'string')
|
|
1073
1062
|
return object( ref, spec );
|
|
1074
|
-
if (spec.prop !== 'type'
|
|
1063
|
+
if (spec.prop !== 'type')
|
|
1075
1064
|
return stringRef( ref, spec );
|
|
1076
|
-
// now the CSN v0.1.0 type of: 'Artifact..e1.e2'
|
|
1065
|
+
// now the CSN v0.1.0 type of: 'Artifact..e1.e2'; error if not csnVersionZero
|
|
1077
1066
|
const idx = ref.indexOf('..');
|
|
1078
1067
|
if (idx < 0)
|
|
1079
1068
|
return stringRef( ref, spec );
|
|
1069
|
+
if (!csnVersionZero) {
|
|
1070
|
+
warning( 'syntax-deprecated-value', location(true),
|
|
1071
|
+
{ '#': 'zero-replace', prop: spec.msgProp, value: '{ ref: […] }' } );
|
|
1072
|
+
}
|
|
1080
1073
|
const r = refSplit( ref.substring( idx + 2 ), spec.msgProp );
|
|
1081
1074
|
r.path.unshift( { id: ref.substring( 0, idx ), location: location() } );
|
|
1082
1075
|
return r;
|
|
@@ -1103,9 +1096,9 @@ function vZeroRef( name, spec, xsn ) {
|
|
|
1103
1096
|
if (!string( name, spec ))
|
|
1104
1097
|
return;
|
|
1105
1098
|
const path = name.split('.');
|
|
1106
|
-
if (!path.every( id => id)) {
|
|
1107
|
-
warning( 'syntax-
|
|
1108
|
-
'
|
|
1099
|
+
if (!path.every( id => id)) { // TODO: why just warning?
|
|
1100
|
+
warning( 'syntax-invalid-zero-ref', location(true), { prop: spec.msgProp },
|
|
1101
|
+
'Invalid string reference in property $(PROP)' );
|
|
1109
1102
|
}
|
|
1110
1103
|
xsn.path = path.map( id => ({ id, location: location() }) );
|
|
1111
1104
|
}
|
|
@@ -1115,18 +1108,17 @@ function vZeroRef( name, spec, xsn ) {
|
|
|
1115
1108
|
function boolOrNull( val, spec ) {
|
|
1116
1109
|
if ([ true, false, null ].includes( val ))
|
|
1117
1110
|
return { val, location: location() };
|
|
1118
|
-
warning( 'syntax-
|
|
1119
|
-
'
|
|
1111
|
+
warning( 'syntax-expecting-boolean', location(true), { prop: spec.msgProp },
|
|
1112
|
+
'Expecting boolean or null for property $(PROP)' );
|
|
1120
1113
|
ignore( val );
|
|
1121
1114
|
return { val: !!val, location: location() };
|
|
1122
1115
|
}
|
|
1123
1116
|
|
|
1124
1117
|
function string( val, spec ) {
|
|
1125
1118
|
if (typeof val === 'string' && val)
|
|
1126
|
-
// XSN TODO: do not require literal
|
|
1127
1119
|
return val;
|
|
1128
|
-
error( 'syntax-
|
|
1129
|
-
'
|
|
1120
|
+
error( 'syntax-expecting-string', location(true), { prop: spec.msgProp },
|
|
1121
|
+
'Expecting non-empty string for property $(PROP)' );
|
|
1130
1122
|
return ignore( val );
|
|
1131
1123
|
}
|
|
1132
1124
|
|
|
@@ -1134,8 +1126,8 @@ function stringVal( val, spec ) {
|
|
|
1134
1126
|
if (typeof val === 'string' && val)
|
|
1135
1127
|
// XSN TODO: do not require literal
|
|
1136
1128
|
return { val, literal: 'string', location: location() };
|
|
1137
|
-
error( 'syntax-
|
|
1138
|
-
'
|
|
1129
|
+
error( 'syntax-expecting-string', location(true), { prop: spec.msgProp },
|
|
1130
|
+
'Expecting non-empty string for property $(PROP)' );
|
|
1139
1131
|
return ignore( val );
|
|
1140
1132
|
}
|
|
1141
1133
|
|
|
@@ -1156,7 +1148,7 @@ function natnum( val, spec ) {
|
|
|
1156
1148
|
if (typeof val === 'number' && val >= 0)
|
|
1157
1149
|
// XSN TODO: do not require literal
|
|
1158
1150
|
return { val, literal: 'number', location: location() };
|
|
1159
|
-
error( spec.msgId || 'syntax-
|
|
1151
|
+
error( spec.msgId || 'syntax-expecting-natnum', location(true),
|
|
1160
1152
|
{ prop: spec.msgProp } );
|
|
1161
1153
|
return ignore( val );
|
|
1162
1154
|
}
|
|
@@ -1191,18 +1183,19 @@ function annoValue( val, spec ) {
|
|
|
1191
1183
|
/** @type {string|boolean} */
|
|
1192
1184
|
let seenEllipsis = false;
|
|
1193
1185
|
if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
|
|
1194
|
-
if (val.some( isEllipsis ))
|
|
1195
|
-
error( 'syntax-unexpected-ellipsis', location(true),
|
|
1186
|
+
if (val.some( isEllipsis )) { // remark: check is via parsing rules in CDL
|
|
1187
|
+
error( 'syntax-unexpected-ellipsis', location(true),
|
|
1188
|
+
{ '#': 'csn-nested', prop: '...' } );
|
|
1189
|
+
}
|
|
1196
1190
|
}
|
|
1197
1191
|
else {
|
|
1198
1192
|
for (const item of val) {
|
|
1199
|
-
if (seenEllipsis !== true) {
|
|
1193
|
+
if (seenEllipsis !== true) { // no `...` yet, or only `... up to`
|
|
1200
1194
|
seenEllipsis = isEllipsis( item ) || seenEllipsis;
|
|
1201
1195
|
}
|
|
1202
|
-
else if (isEllipsis( item )) { // with or without UP TO
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
'Expected no more than one $(CODE)' );
|
|
1196
|
+
else if (isEllipsis( item )) { // `...`with or without UP TO
|
|
1197
|
+
error( 'syntax-unexpected-ellipsis', location(true),
|
|
1198
|
+
{ '#': 'csn-duplicate', prop: '...', code: '{ "...": true }' } );
|
|
1206
1199
|
break;
|
|
1207
1200
|
}
|
|
1208
1201
|
}
|
|
@@ -1215,10 +1208,8 @@ function annoValue( val, spec ) {
|
|
|
1215
1208
|
};
|
|
1216
1209
|
arrayLevelCount--;
|
|
1217
1210
|
if (seenEllipsis === 'upTo') {
|
|
1218
|
-
error( 'syntax-
|
|
1219
|
-
{ code: '... up to', newcode: '...' }
|
|
1220
|
-
// TODO: should we be more CSN specific in the message?
|
|
1221
|
-
'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
|
|
1211
|
+
error( 'syntax-missing-ellipsis', location(true), // at closing bracket
|
|
1212
|
+
{ code: '{ "...": ‹up to value› }', newcode: '{ "...": true }' } );
|
|
1222
1213
|
}
|
|
1223
1214
|
return retval;
|
|
1224
1215
|
}
|
|
@@ -1234,8 +1225,10 @@ function annoValue( val, spec ) {
|
|
|
1234
1225
|
}
|
|
1235
1226
|
else if (typeof val['='] === 'string') {
|
|
1236
1227
|
if (Object.keys( val ).length === 1) {
|
|
1237
|
-
virtualLine
|
|
1238
|
-
|
|
1228
|
+
++virtualLine;
|
|
1229
|
+
const r = refSplit( val['='], '=' );
|
|
1230
|
+
++virtualLine;
|
|
1231
|
+
return r;
|
|
1239
1232
|
}
|
|
1240
1233
|
}
|
|
1241
1234
|
else if (val['...'] && Object.keys( val ).length === 1) {
|
|
@@ -1284,26 +1277,29 @@ function value( val, spec, xsn ) { // for CSN property 'val'
|
|
|
1284
1277
|
xsn.literal = (val === null) ? 'null' : typeof val;
|
|
1285
1278
|
return val;
|
|
1286
1279
|
}
|
|
1287
|
-
error( 'syntax-
|
|
1280
|
+
error( 'syntax-expecting-scalar', location(true), { prop: spec.msgProp },
|
|
1288
1281
|
'Only scalar values are supported for property $(PROP)' );
|
|
1289
1282
|
return ignore( val );
|
|
1290
1283
|
}
|
|
1291
1284
|
|
|
1292
1285
|
function literal( lit, spec, xsn, csn ) {
|
|
1293
1286
|
// TODO: general: requires other property (here: 'val')
|
|
1287
|
+
if (!string( lit ))
|
|
1288
|
+
return undefined;
|
|
1294
1289
|
const type = (csn.val == null) ? 'null' : typeof csn.val;
|
|
1295
1290
|
if (lit === type) // also for 'object' which is an error for 'val'
|
|
1296
1291
|
return lit;
|
|
1297
|
-
|
|
1298
|
-
|
|
1292
|
+
const p = quotedLiteralPatterns[lit];
|
|
1293
|
+
if (p?.json_type === type) {
|
|
1294
|
+
// TODO: wrong position, we complain about the format here, that is in 'val',
|
|
1295
|
+
// TODO: make it then also more CSN-specific? Next change
|
|
1299
1296
|
if (p && p.test_fn && !p.test_fn(csn.val))
|
|
1300
1297
|
warning( 'syntax-invalid-literal', location(), { '#': p.test_variant } );
|
|
1301
1298
|
return lit;
|
|
1302
1299
|
}
|
|
1303
1300
|
if (lit === 'number' && type === 'string') // special case, not a quoted literal in CDL
|
|
1304
1301
|
return lit;
|
|
1305
|
-
error( 'syntax-
|
|
1306
|
-
'Expected valid string for property $(PROP)' );
|
|
1302
|
+
error( 'syntax-invalid-string', location(true), { prop: spec.msgProp } );
|
|
1307
1303
|
return ignore( lit );
|
|
1308
1304
|
}
|
|
1309
1305
|
|
|
@@ -1317,8 +1313,8 @@ function func( val, spec, xsn ) {
|
|
|
1317
1313
|
function xpr( exprs, spec, xsn, csn ) {
|
|
1318
1314
|
if (csn.func) {
|
|
1319
1315
|
if (!exprs.length) {
|
|
1320
|
-
|
|
1321
|
-
|
|
1316
|
+
error( 'syntax-incomplete-array', location(true),
|
|
1317
|
+
{ prop: 'xpr', siblingprop: 'func', '#': 'suffix' });
|
|
1322
1318
|
}
|
|
1323
1319
|
xsn.suffix = exprArgs( exprs, spec );
|
|
1324
1320
|
}
|
|
@@ -1350,9 +1346,9 @@ function args( exprs, spec ) {
|
|
|
1350
1346
|
return arrayOf( exprOrString )( exprs, spec );
|
|
1351
1347
|
}
|
|
1352
1348
|
else if (!exprs || typeof exprs !== 'object') {
|
|
1353
|
-
error( 'syntax-
|
|
1349
|
+
error( 'syntax-expecting-args', location(true),
|
|
1354
1350
|
{ prop: spec.prop }, // spec.prop, not spec.msgProp!
|
|
1355
|
-
'
|
|
1351
|
+
'Expecting array or object for property $(PROP)' );
|
|
1356
1352
|
return ignore( exprs );
|
|
1357
1353
|
}
|
|
1358
1354
|
const r = Object.create(null);
|
|
@@ -1370,16 +1366,24 @@ function args( exprs, spec ) {
|
|
|
1370
1366
|
}
|
|
1371
1367
|
|
|
1372
1368
|
function expr( e, spec ) {
|
|
1373
|
-
if (Array.isArray( e ) && e.length === 1) {
|
|
1374
|
-
|
|
1369
|
+
if (Array.isArray( e ) && e.length === 1) { // CSN v.0.1.0 way for parentheses
|
|
1370
|
+
const loc = location();
|
|
1371
|
+
if (e[0] && !e[0].op) // do not complain with 'op' (for which we complain)
|
|
1372
|
+
replaceZeroValue( spec, 'zero-parens' );
|
|
1375
1373
|
++virtualLine;
|
|
1376
1374
|
const r = expr( e[0], spec );
|
|
1375
|
+
if (!r)
|
|
1376
|
+
return r;
|
|
1377
|
+
if (r.$parens)
|
|
1378
|
+
r.$parens.push( loc );
|
|
1379
|
+
else
|
|
1380
|
+
r.$parens = [ loc ];
|
|
1377
1381
|
++virtualLine;
|
|
1378
|
-
return
|
|
1382
|
+
return r;
|
|
1379
1383
|
}
|
|
1380
1384
|
else if (e === null || [ 'string', 'number', 'boolean' ].includes( typeof e )) {
|
|
1381
1385
|
// && spec.optional.includes( 'val' )) ?
|
|
1382
|
-
replaceZeroValue( spec );
|
|
1386
|
+
replaceZeroValue( spec, 'zero-replace', '{ val: ‹value› }' );
|
|
1383
1387
|
return annoValue( e, spec );
|
|
1384
1388
|
}
|
|
1385
1389
|
return object( e, spec );
|
|
@@ -1410,20 +1414,6 @@ function condition( cond, spec ) {
|
|
|
1410
1414
|
};
|
|
1411
1415
|
}
|
|
1412
1416
|
|
|
1413
|
-
function vZeroValue( obj, spec, xsn ) {
|
|
1414
|
-
if (xsn.value) {
|
|
1415
|
-
// TODO: also "sign" xsn.value created by inValue to complain about both 'value' and 'ref' etc
|
|
1416
|
-
warning( 'syntax-unexpected-property', location(true), { prop: spec.msgProp },
|
|
1417
|
-
'Unexpected CSN property $(PROP)' );
|
|
1418
|
-
return undefined;
|
|
1419
|
-
}
|
|
1420
|
-
if (!csnVersionZero) {
|
|
1421
|
-
warning( 'syntax-zero-delete', location(true), { prop: spec.msgProp },
|
|
1422
|
-
'Delete/inline CSN v0.1.0 property $(PROP)' );
|
|
1423
|
-
}
|
|
1424
|
-
return expr( obj, spec );
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
1417
|
// Queries (std signature) ---------------------------------------------------
|
|
1428
1418
|
|
|
1429
1419
|
function queryTerm( term, spec, xsn ) { // for CSN properties 'SELECT' and 'SET'
|
|
@@ -1465,17 +1455,16 @@ function excluding( array, spec, xsn ) {
|
|
|
1465
1455
|
xsn.excludingDict = r;
|
|
1466
1456
|
}
|
|
1467
1457
|
|
|
1468
|
-
function masked( val, spec ) {
|
|
1469
|
-
message('syntax-invalid-masked', location(), { keyword: 'masked' },
|
|
1470
|
-
'Keyword $(KEYWORD) not supported');
|
|
1471
|
-
return boolOrNull( val, spec );
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
1458
|
function duplicateExcluding( name, loc ) {
|
|
1475
|
-
error( 'duplicate-excluding', loc, { name, keyword: 'excluding' },
|
|
1459
|
+
error( 'syntax-duplicate-excluding', loc, { name, keyword: 'excluding' }, // TODO: also CDL
|
|
1476
1460
|
'Duplicate $(NAME) in the $(KEYWORD) clause' );
|
|
1477
1461
|
}
|
|
1478
1462
|
|
|
1463
|
+
function masked( val, spec ) {
|
|
1464
|
+
message( 'syntax-unsupported-masked', location(), { '#': 'csn', prop: 'masked' } );
|
|
1465
|
+
return boolOrNull( val, spec );
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1479
1468
|
function setOp( val, spec ) { // UNION, ...
|
|
1480
1469
|
// similar to string(), but without literal
|
|
1481
1470
|
return string( val, spec ) && { val, location: location() };
|
|
@@ -1491,9 +1480,10 @@ function join( val, spec, xsn ) {
|
|
|
1491
1480
|
|
|
1492
1481
|
function queryArgs( val, spec, xsn, csn ) {
|
|
1493
1482
|
if (Array.isArray( val ) && val.length > 1 && !csn.op) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1483
|
+
// Make it error 'syntax-missing-property#sibling' in v4:
|
|
1484
|
+
warning( 'syntax-deprecated-auto-union', location(true),
|
|
1485
|
+
{ siblingprop: 'args', prop: 'op' },
|
|
1486
|
+
'Object with property $(SIBLINGPROP) must also have a property $(PROP)' );
|
|
1497
1487
|
xsn.op = { val: 'union', location: location() };
|
|
1498
1488
|
}
|
|
1499
1489
|
return arrayOf( object )( val, spec ).map( q => q.query );
|
|
@@ -1510,9 +1500,9 @@ function i18nLang( val, spec, xsn, csn, langKey ) {
|
|
|
1510
1500
|
function translations( keyVal, spec, xsn, csn, textKey ) {
|
|
1511
1501
|
if (typeof keyVal === 'string') // allow empty string
|
|
1512
1502
|
return { val: keyVal, literal: 'string', location: location() };
|
|
1513
|
-
error( 'syntax-
|
|
1514
|
-
{ prop: textKey,
|
|
1515
|
-
'
|
|
1503
|
+
error( 'syntax-expecting-translation', location(true),
|
|
1504
|
+
{ prop: textKey, language: spec.prop },
|
|
1505
|
+
'Expecting string for text key $(PROP) of language $(LANGUAGE)' );
|
|
1516
1506
|
return ignore( keyVal );
|
|
1517
1507
|
}
|
|
1518
1508
|
|
|
@@ -1522,21 +1512,18 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1522
1512
|
const p0 = schema[prop] ? prop : prop.charAt(0);
|
|
1523
1513
|
const s = (parentSpec.schema || schema)[p0];
|
|
1524
1514
|
if (!s || s.noPrefix && prop !== p0 ) {
|
|
1525
|
-
if (ourpropsRegex.test( prop ))
|
|
1526
|
-
// TODO v2: Warning only with --sloppy
|
|
1527
|
-
warning( 'syntax-unknown-property', location(true), { prop },
|
|
1528
|
-
'Unknown CSN property $(PROP)' );
|
|
1529
|
-
}
|
|
1530
|
-
else { // TODO v2: always (i.e. also with message) add to $extra
|
|
1515
|
+
if (!ourpropsRegex.test( prop ))
|
|
1531
1516
|
return { prop, type: extra };
|
|
1532
|
-
|
|
1517
|
+
// TODO v4: No warning with --sloppy
|
|
1518
|
+
warning( 'syntax-unknown-property', location(true), { prop },
|
|
1519
|
+
'Unknown CSN property $(PROP)' );
|
|
1520
|
+
return { type: ignore };
|
|
1533
1521
|
}
|
|
1534
1522
|
else if (!expected( p0, s )) {
|
|
1535
1523
|
if (s.ignore)
|
|
1536
1524
|
return { type: ignore };
|
|
1537
1525
|
if (s.vZeroIgnore && s.vZeroIgnore === csn[prop]) { // for "op": "call"
|
|
1538
|
-
warning( 'syntax-
|
|
1539
|
-
'Delete/inline CSN v0.1.0 property $(PROP)' );
|
|
1526
|
+
warning( 'syntax-deprecated-property', location(true), { '#': 'zero', prop } );
|
|
1540
1527
|
return { type: ignore };
|
|
1541
1528
|
}
|
|
1542
1529
|
const zero = s.vZeroFor;
|
|
@@ -1552,20 +1539,16 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1552
1539
|
}
|
|
1553
1540
|
// eslint-disable-next-line no-nested-ternary
|
|
1554
1541
|
const variant = kind && s.inKind
|
|
1555
|
-
? ([ 'extend', 'annotate' ].includes(kind) ? kind : '
|
|
1556
|
-
: (parentSpec.msgProp ? '
|
|
1542
|
+
? ([ 'extend', 'annotate' ].includes(kind) ? kind : 'kind')
|
|
1543
|
+
: (parentSpec.msgProp ? 'prop' : 'top');
|
|
1557
1544
|
message( 'syntax-unexpected-property', location(true),
|
|
1558
1545
|
{
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
def: 'CSN property $(PROP) is not expected by a definition of kind $(KIND)',
|
|
1565
|
-
extend: 'CSN property $(PROP) is not expected by an extend in $(OTHERPROP)',
|
|
1566
|
-
annotate: 'CSN property $(PROP) is not expected by an annotate in $(OTHERPROP)',
|
|
1546
|
+
'#': variant,
|
|
1547
|
+
prop,
|
|
1548
|
+
parentprop:
|
|
1549
|
+
parentSpec.msgProp,
|
|
1550
|
+
kind,
|
|
1567
1551
|
} );
|
|
1568
|
-
// TODO: or still augment it? (but then also handle xorGroup)
|
|
1569
1552
|
}
|
|
1570
1553
|
else if (checkAndSetXorGroup( s.xorGroup, s.xorException, prop, xor )) {
|
|
1571
1554
|
onlyWith( s, s.onlyWith, csn, prop, xor, expected );
|
|
@@ -1612,19 +1595,18 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1612
1595
|
need = allowed.find( p => !xor[schema[p].xorGroup] ) || allowed[0];
|
|
1613
1596
|
}
|
|
1614
1597
|
if (prop) {
|
|
1615
|
-
error( 'syntax-
|
|
1616
|
-
{
|
|
1617
|
-
'CSN property $(PROP) can only be used in combination with $(OTHERPROP)');
|
|
1598
|
+
error( 'syntax-missing-property', location(true), // location at $(PROP)
|
|
1599
|
+
{ '#': 'sibling', prop: need, siblingprop: prop } );
|
|
1618
1600
|
xor['no:req'] = prop;
|
|
1619
1601
|
}
|
|
1602
|
+
// TODO: does no:req work? check test3/NestedProjections/Basics/SyntaxErrorsCsn.err.csn
|
|
1620
1603
|
else if (!xor['no:req']) {
|
|
1621
|
-
error( 'syntax-
|
|
1622
|
-
{
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
extensions: 'Object in $(OTHERPROP) must have the property \'annotate\' or \'extend\'',
|
|
1604
|
+
error( 'syntax-missing-property', location(true), // TODO: re-check columns, expand, inline
|
|
1605
|
+
{
|
|
1606
|
+
'#': spec.prop,
|
|
1607
|
+
prop: need,
|
|
1608
|
+
parentprop: spec.msgProp,
|
|
1609
|
+
otherprop: 'annotate',
|
|
1628
1610
|
} );
|
|
1629
1611
|
}
|
|
1630
1612
|
return spec;
|
|
@@ -1633,15 +1615,14 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1633
1615
|
function checkAndSetXorGroup( group, exception, prop, xor ) {
|
|
1634
1616
|
if (!group)
|
|
1635
1617
|
return true;
|
|
1636
|
-
const
|
|
1637
|
-
if (!
|
|
1618
|
+
const siblingprop = xor[group];
|
|
1619
|
+
if (!siblingprop) {
|
|
1638
1620
|
xor[group] = prop;
|
|
1639
1621
|
return true;
|
|
1640
1622
|
}
|
|
1641
|
-
if (
|
|
1623
|
+
if (siblingprop === exception)
|
|
1642
1624
|
return true;
|
|
1643
|
-
|
|
1644
|
-
'CSN property $(PROP) can only be used alternatively to $(OTHERPROP)');
|
|
1625
|
+
message( 'syntax-unexpected-property', location(true), { '#': 'sibling', prop, siblingprop } );
|
|
1645
1626
|
return false;
|
|
1646
1627
|
}
|
|
1647
1628
|
|
|
@@ -1651,11 +1632,11 @@ function implicitName( ref ) {
|
|
|
1651
1632
|
return (typeof item === 'object') ? item && item.id : item;
|
|
1652
1633
|
}
|
|
1653
1634
|
|
|
1654
|
-
function replaceZeroProp(
|
|
1635
|
+
function replaceZeroProp( prop, otherprop ) {
|
|
1655
1636
|
if (csnVersionZero)
|
|
1656
1637
|
return;
|
|
1657
|
-
warning( 'syntax-
|
|
1658
|
-
'
|
|
1638
|
+
warning( 'syntax-deprecated-property', location(true),
|
|
1639
|
+
{ '#': 'zero-replace', prop, otherprop } );
|
|
1659
1640
|
}
|
|
1660
1641
|
|
|
1661
1642
|
// Other helper functions, locations -----------------------------------------
|
|
@@ -1663,15 +1644,15 @@ function replaceZeroProp( otherprop, prop ) {
|
|
|
1663
1644
|
function isArray( array, spec ) {
|
|
1664
1645
|
if (Array.isArray( array ))
|
|
1665
1646
|
return array;
|
|
1666
|
-
error( 'syntax-
|
|
1667
|
-
'
|
|
1647
|
+
error( 'syntax-expecting-array', location(true), { prop: spec.prop },
|
|
1648
|
+
'Expecting array for property $(PROP)' );
|
|
1668
1649
|
return ignore( array );
|
|
1669
1650
|
}
|
|
1670
1651
|
|
|
1671
1652
|
function isObject( obj, spec ) {
|
|
1672
1653
|
if (obj && typeof obj === 'object' && !Array.isArray( obj ))
|
|
1673
1654
|
return obj;
|
|
1674
|
-
error( spec.msgId || 'syntax-
|
|
1655
|
+
error( spec.msgId || 'syntax-expecting-object', location(true),
|
|
1675
1656
|
{ prop: spec.msgProp });
|
|
1676
1657
|
return ignore( obj );
|
|
1677
1658
|
}
|
|
@@ -1679,16 +1660,16 @@ function isObject( obj, spec ) {
|
|
|
1679
1660
|
function refSplit( name, prop ) {
|
|
1680
1661
|
const path = name.split('.');
|
|
1681
1662
|
if (!path.every( id => id)) {
|
|
1682
|
-
warning( 'syntax-
|
|
1683
|
-
'
|
|
1663
|
+
warning( 'syntax-expecting-name', location(true), { prop }, // TODO: re-check
|
|
1664
|
+
'Expecting correct name for property $(PROP)' );
|
|
1684
1665
|
}
|
|
1685
1666
|
return { path: path.map( id => ({ id, location: location() }) ), location: location() };
|
|
1686
1667
|
}
|
|
1687
1668
|
|
|
1688
|
-
function replaceZeroValue( spec ) {
|
|
1689
|
-
if (!csnVersionZero && spec.vZeroFor
|
|
1690
|
-
warning( 'syntax-
|
|
1691
|
-
'
|
|
1669
|
+
function replaceZeroValue( spec, msgid, otherprop ) {
|
|
1670
|
+
if (!csnVersionZero && !spec.vZeroFor) {
|
|
1671
|
+
warning( 'syntax-deprecated-value', location(true),
|
|
1672
|
+
{ '#': msgid, prop: spec.msgProp, otherprop } );
|
|
1692
1673
|
}
|
|
1693
1674
|
}
|
|
1694
1675
|
|
|
@@ -1717,7 +1698,7 @@ function pushLocation( obj ) {
|
|
|
1717
1698
|
else if (!loc || typeof loc !== 'string') {
|
|
1718
1699
|
if (loc)
|
|
1719
1700
|
dollarLocations.push( null ); // must match with popLocation()
|
|
1720
|
-
error( 'syntax-
|
|
1701
|
+
error( 'syntax-expecting-object', location(true), { prop: '$location' } );
|
|
1721
1702
|
}
|
|
1722
1703
|
// hidden feature: string $location
|
|
1723
1704
|
const m = /:(\d+)(?::(\d+))?$/.exec( loc ); // extra '^'s at end deliberately left out
|
|
@@ -1774,7 +1755,7 @@ function toXsn( csn, filename, options, messageFunctions ) {
|
|
|
1774
1755
|
({ message, error, warning, info } = messageFunctions);
|
|
1775
1756
|
|
|
1776
1757
|
if (csnVersionZero) {
|
|
1777
|
-
warning( 'syntax-csn-
|
|
1758
|
+
warning( 'syntax-deprecated-csn-version', location(true), {},
|
|
1778
1759
|
'Parsing CSN version 0.1.0' );
|
|
1779
1760
|
}
|
|
1780
1761
|
const r = object( csn, topLevelSpec );
|
|
@@ -1830,7 +1811,8 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
|
|
|
1830
1811
|
line,
|
|
1831
1812
|
col: column,
|
|
1832
1813
|
};
|
|
1833
|
-
messageFunctions.error( 'syntax-
|
|
1814
|
+
messageFunctions.error( 'syntax-invalid-json', loc, { msg },
|
|
1815
|
+
'Invalid JSON: $(MSG)' );
|
|
1834
1816
|
return xsn;
|
|
1835
1817
|
}
|
|
1836
1818
|
}
|