@sap/cds-compiler 3.6.2 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +109 -1
- package/README.md +3 -0
- package/bin/cdsc.js +12 -5
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +35 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +63 -23
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +15 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +120 -34
- package/lib/base/messages.js +51 -27
- package/lib/base/model.js +4 -2
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +15 -9
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +5 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +42 -26
- package/lib/compiler/base.js +50 -4
- package/lib/compiler/builtins.js +17 -8
- package/lib/compiler/checks.js +241 -246
- package/lib/compiler/define.js +113 -146
- package/lib/compiler/extend.js +889 -383
- package/lib/compiler/finalize-parse-cdl.js +5 -58
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +7 -8
- package/lib/compiler/populate.js +297 -293
- package/lib/compiler/propagator.js +27 -18
- package/lib/compiler/resolve.js +146 -463
- package/lib/compiler/shared.js +36 -79
- package/lib/compiler/tweak-assocs.js +30 -28
- package/lib/compiler/utils.js +31 -5
- package/lib/edm/annotations/genericTranslation.js +131 -59
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +22 -5
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +42 -26
- package/lib/gen/Dictionary.json +38 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4828 -4472
- package/lib/inspect/inspectPropagation.js +20 -34
- package/lib/json/from-csn.js +140 -44
- package/lib/json/to-csn.js +114 -122
- package/lib/language/errorStrategy.js +2 -0
- package/lib/language/genericAntlrParser.js +156 -36
- package/lib/language/language.g4 +100 -58
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +15 -3
- package/lib/model/csnUtils.js +12 -74
- package/lib/model/revealInternalProperties.js +4 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +216 -104
- package/lib/render/toHdbcds.js +2 -9
- package/lib/render/toRename.js +14 -51
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +600 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +58 -41
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +16 -8
- package/lib/transform/transformUtilsNew.js +42 -14
- package/lib/transform/translateAssocsToJoins.js +60 -37
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
|
@@ -4,7 +4,13 @@ const { createMessageFunctions } = require('../base/messages');
|
|
|
4
4
|
const { locationString } = require('../base/location');
|
|
5
5
|
const { findArtifact, stringRefToPath } = require('./inspectUtils');
|
|
6
6
|
const { term } = require('../utils/term');
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
const inferredNiceOutput = {
|
|
9
|
+
'*': 'wildcard',
|
|
10
|
+
'aspect-composition': 'composition',
|
|
11
|
+
prop: 'propagation',
|
|
12
|
+
$generated: 'generated',
|
|
13
|
+
};
|
|
8
14
|
|
|
9
15
|
/**
|
|
10
16
|
* @param {XSN.Model} xsn
|
|
@@ -77,29 +83,16 @@ function _inspectAnnotations( artifactXsn ) {
|
|
|
77
83
|
for (const anno of annos) {
|
|
78
84
|
const annoXsn = artifactXsn[anno];
|
|
79
85
|
const loc = locationString(annoXsn.name.location);
|
|
86
|
+
|
|
80
87
|
let origin;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
case 'extend':
|
|
90
|
-
case 'annotate':
|
|
91
|
-
origin = annoXsn.$priority;
|
|
92
|
-
break;
|
|
93
|
-
|
|
94
|
-
case undefined:
|
|
95
|
-
if (annoXsn.$inferred === '$generated') {
|
|
96
|
-
origin = 'generated';
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
// fallthrough
|
|
100
|
-
default:
|
|
101
|
-
throw new CompilerAssertion(`inspect anno: Unhandled Case: ${ annoXsn.$priority }`);
|
|
102
|
-
}
|
|
88
|
+
if (annoXsn.$inferred === '$generated')
|
|
89
|
+
origin = 'generated';
|
|
90
|
+
else if (annoXsn.$inferred)
|
|
91
|
+
origin = inferredNiceOutput[annoXsn.$inferred] || annoXsn.$inferred;
|
|
92
|
+
else if (isContainedInParentLocation(annoXsn.name, artifactXsn))
|
|
93
|
+
origin = 'direct';
|
|
94
|
+
else
|
|
95
|
+
origin = 'annotate'; // ...or `extend`
|
|
103
96
|
|
|
104
97
|
maxAnnoLength = Math.max(maxAnnoLength, anno.length);
|
|
105
98
|
|
|
@@ -122,11 +115,6 @@ function _inspectElements( artifactXsn ) {
|
|
|
122
115
|
const result = [];
|
|
123
116
|
const elements = Object.keys(artifactXsn.elements);
|
|
124
117
|
|
|
125
|
-
const inferredNiceOutput = {
|
|
126
|
-
'*': 'wildcard',
|
|
127
|
-
'aspect-composition': 'composition',
|
|
128
|
-
};
|
|
129
|
-
|
|
130
118
|
let maxElemLength = 12;
|
|
131
119
|
let maxOriginLength = 6;
|
|
132
120
|
|
|
@@ -153,7 +141,7 @@ function _inspectElements( artifactXsn ) {
|
|
|
153
141
|
origin = elementXsn.$inferred;
|
|
154
142
|
}
|
|
155
143
|
else if (!isContainedInParentLocation(elementXsn, artifactXsn)) {
|
|
156
|
-
// just a heuristic
|
|
144
|
+
// just a heuristic - a good enough one
|
|
157
145
|
origin = 'extend';
|
|
158
146
|
}
|
|
159
147
|
else {
|
|
@@ -192,11 +180,9 @@ function isContainedInParentLocation( art, parent ) {
|
|
|
192
180
|
const parentLoc = parent.location;
|
|
193
181
|
if (artLoc.file !== parentLoc.file)
|
|
194
182
|
return false;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// TODO: Check columns
|
|
199
|
-
return true;
|
|
183
|
+
const startDiff = artLoc.line - parentLoc.line || artLoc.col - parentLoc.col;
|
|
184
|
+
const endDiff = artLoc.endLine - parentLoc.endLine || artLoc.endCol - parentLoc.endCol;
|
|
185
|
+
return startDiff >= 0 && endDiff <= 0;
|
|
200
186
|
}
|
|
201
187
|
|
|
202
188
|
module.exports = {
|
package/lib/json/from-csn.js
CHANGED
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
* @property {number} [minLength] Minimum number of elements that an array must have.
|
|
63
63
|
* @property {boolean} [inValue] Puts the value into an XSN property "value",
|
|
64
64
|
* e.g. { value: ... }
|
|
65
|
-
* @property {string}
|
|
66
|
-
* xorGroups. If set then only one property of
|
|
65
|
+
* @property {string[]} [xorGroups] Corresponding xor groups. It references a value of
|
|
66
|
+
* xorGroups. If set then only one property of the
|
|
67
67
|
* xorGroup may be set, e.g. if target is set, elements
|
|
68
68
|
* may not.
|
|
69
69
|
* @property {string} [xsnOp] Defines the operator to be used for XSN. Used for SET
|
|
@@ -75,6 +75,8 @@
|
|
|
75
75
|
* it.
|
|
76
76
|
* @property {string} [xorException] A property name that is allowed besides another property
|
|
77
77
|
* of an xorGroup (as an exception to the rule).
|
|
78
|
+
* @property {boolean} [ignoreExtra] Whether extra properties are ignored and not put
|
|
79
|
+
* into $extra.
|
|
78
80
|
*/
|
|
79
81
|
|
|
80
82
|
/**
|
|
@@ -90,6 +92,7 @@
|
|
|
90
92
|
const { dictAdd } = require('../base/dictionaries');
|
|
91
93
|
const { quotedLiteralPatterns } = require('../compiler/builtins');
|
|
92
94
|
const { CompilerAssertion } = require('../base/error');
|
|
95
|
+
const { isBetaEnabled } = require('../base/model');
|
|
93
96
|
|
|
94
97
|
const $location = Symbol.for('cds.$location');
|
|
95
98
|
|
|
@@ -121,7 +124,8 @@ const xorGroups = {
|
|
|
121
124
|
':expr': [ // see also xorException property in schema
|
|
122
125
|
'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET', 'expand',
|
|
123
126
|
'=', 'path', 'value', 'op', // '='/'path' is CSN v0.1.0 here
|
|
124
|
-
],
|
|
127
|
+
],
|
|
128
|
+
':col': [ 'expand', 'inline' ],
|
|
125
129
|
':ext': [ 'annotate', 'extend' ], // TODO: better msg for test/negative/UnexpectedProperties.csn
|
|
126
130
|
':assoc': [ 'on', 'keys', 'foreignKeys', 'onCond' ], // 'foreignKeys'/'onCond' is CSN v0.1.0
|
|
127
131
|
// TODO - improve consequential errors: assume no name given with `join` or `inline`?
|
|
@@ -162,10 +166,15 @@ const schemaClasses = {
|
|
|
162
166
|
xpr: {
|
|
163
167
|
class: 'condition',
|
|
164
168
|
type: xprInValue,
|
|
165
|
-
xorException: 'func',
|
|
169
|
+
xorException: 'func', // see xorGroup :expr; for window functions
|
|
166
170
|
inKind: [ '$column' ],
|
|
167
171
|
inValue: true,
|
|
168
172
|
},
|
|
173
|
+
'=': {
|
|
174
|
+
// by not setting `vZeroFor`, we disallow `=` in `columns`.
|
|
175
|
+
// CSN v0.1 didn't have columns, so this isn't breaking v0.1 compatibility.
|
|
176
|
+
type: ignore,
|
|
177
|
+
},
|
|
169
178
|
},
|
|
170
179
|
},
|
|
171
180
|
};
|
|
@@ -252,12 +261,10 @@ const schema = compileSchema( {
|
|
|
252
261
|
expand: {
|
|
253
262
|
class: 'columns',
|
|
254
263
|
xorException: 'ref', // see xorGroup :expr
|
|
255
|
-
xorGroupTwo: ':col',
|
|
256
264
|
inKind: [ '$column' ], // only valid in $column
|
|
257
265
|
},
|
|
258
266
|
inline: {
|
|
259
267
|
class: 'columns',
|
|
260
|
-
xorGroupTwo: ':col',
|
|
261
268
|
onlyWith: 'ref',
|
|
262
269
|
inKind: [ '$column' ], // only valid in $column
|
|
263
270
|
},
|
|
@@ -298,10 +305,20 @@ const schema = compileSchema( {
|
|
|
298
305
|
},
|
|
299
306
|
// type properties (except: elements, enum, keys, on): ---------------------
|
|
300
307
|
type: {
|
|
301
|
-
type:
|
|
308
|
+
type: typeArtifactRef,
|
|
302
309
|
msgVariant: 'or-object', // for 'syntax-expecting-string',
|
|
303
310
|
optional: [ 'ref', 'global' ],
|
|
304
311
|
inKind: [ 'element', 'type', 'param', 'mixin', 'event', 'annotation', 'extend' ],
|
|
312
|
+
schema: {
|
|
313
|
+
ref: {
|
|
314
|
+
arrayOf: typeRefItem,
|
|
315
|
+
type: renameTo( 'path', typeRef ),
|
|
316
|
+
minLength: 1,
|
|
317
|
+
requires: 'id',
|
|
318
|
+
optional: [ 'id' ],
|
|
319
|
+
ignoreExtra: true, // custom properties inside `ref` ignored.
|
|
320
|
+
},
|
|
321
|
+
},
|
|
305
322
|
},
|
|
306
323
|
targetAspect: {
|
|
307
324
|
type: artifactRef,
|
|
@@ -424,6 +441,7 @@ const schema = compileSchema( {
|
|
|
424
441
|
list: {
|
|
425
442
|
class: 'condition',
|
|
426
443
|
type: list,
|
|
444
|
+
inKind: [ '$column' ],
|
|
427
445
|
},
|
|
428
446
|
val: {
|
|
429
447
|
type: value,
|
|
@@ -592,6 +610,19 @@ const schema = compileSchema( {
|
|
|
592
610
|
prop: '@‹anno›', // which property name do messages use for annotation assignments?
|
|
593
611
|
type: annotation,
|
|
594
612
|
inKind: () => true, // allowed in all definitions (including columns and extensions)
|
|
613
|
+
schema: {
|
|
614
|
+
'-expr': { // '-expr' and '-' must not exist top-level
|
|
615
|
+
prop: '@‹anno›',
|
|
616
|
+
type: object,
|
|
617
|
+
optional: [ '=', '#', 'xpr', 'ref', 'val', 'list', 'literal', 'func', 'args' ],
|
|
618
|
+
schema: {
|
|
619
|
+
'=': {
|
|
620
|
+
type: renameTo( '$tokenTexts', string ),
|
|
621
|
+
xorGroups: null, // reset xorGroup; allow '=' for all :expr
|
|
622
|
+
},
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
},
|
|
595
626
|
},
|
|
596
627
|
abstract: { // v1: with 'abstract', an entity becomes an aspect
|
|
597
628
|
type: abstract,
|
|
@@ -607,7 +638,7 @@ const schema = compileSchema( {
|
|
|
607
638
|
},
|
|
608
639
|
notNull: {
|
|
609
640
|
type: boolOrNull,
|
|
610
|
-
inKind: [ 'element', 'param' ], // TODO: $column - or if so: in 'cast'?
|
|
641
|
+
inKind: [ 'element', 'param', 'type' ], // TODO: $column - or if so: in 'cast'?
|
|
611
642
|
},
|
|
612
643
|
virtual: {
|
|
613
644
|
type: boolOrNull,
|
|
@@ -620,7 +651,7 @@ const schema = compileSchema( {
|
|
|
620
651
|
// 2. Inside "xpr" => inside expressions
|
|
621
652
|
// Because of (1) we have to set this property to false.
|
|
622
653
|
inValue: false,
|
|
623
|
-
optional: typeProperties,
|
|
654
|
+
optional: typeProperties, // TODO: only in CDL-style cast, otherwise just length,…
|
|
624
655
|
inKind: [ '$column' ],
|
|
625
656
|
},
|
|
626
657
|
default: {
|
|
@@ -731,6 +762,8 @@ let virtualLine = 1;
|
|
|
731
762
|
/** @type {CSN.Location[]} */
|
|
732
763
|
let dollarLocations = [];
|
|
733
764
|
let arrayLevelCount = 0;
|
|
765
|
+
/** @type {CSN.Options} */
|
|
766
|
+
let userOptions = null; // must be reset!
|
|
734
767
|
|
|
735
768
|
/**
|
|
736
769
|
* @param {Object.<string, SchemaSpec>} specs
|
|
@@ -763,12 +796,15 @@ function compileSchema( specs, proto = null ) {
|
|
|
763
796
|
else
|
|
764
797
|
throw new CompilerAssertion( `Missing type specification for property "${ p }"` );
|
|
765
798
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
799
|
+
|
|
800
|
+
if (s.xorGroups === undefined) {
|
|
801
|
+
// Only set xorGroup once. Could already be set through shared sub-schema
|
|
802
|
+
// of schemaClasses or be explicitly set.
|
|
803
|
+
s.xorGroups = [];
|
|
804
|
+
for (const group in xorGroups) {
|
|
805
|
+
if (xorGroups[group].includes(p))
|
|
806
|
+
s.xorGroups.push(group);
|
|
807
|
+
}
|
|
772
808
|
}
|
|
773
809
|
}
|
|
774
810
|
if (proto)
|
|
@@ -1110,6 +1146,14 @@ function validKind( val, spec, xsn ) {
|
|
|
1110
1146
|
return ignore( val );
|
|
1111
1147
|
}
|
|
1112
1148
|
|
|
1149
|
+
function typeArtifactRef( ref, spec ) {
|
|
1150
|
+
if (ref && typeof ref === 'object' && !Array.isArray( ref )) {
|
|
1151
|
+
if (ref.ref?.length === 1)
|
|
1152
|
+
return artifactRef( ref, { ...spec, ignoreExtra: true } );
|
|
1153
|
+
}
|
|
1154
|
+
return artifactRef( ref, spec );
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1113
1157
|
// Use with spec.msgVariant: 'or-object'
|
|
1114
1158
|
function artifactRef( ref, spec ) {
|
|
1115
1159
|
if (!ref || typeof ref !== 'string') {
|
|
@@ -1230,6 +1274,32 @@ function symbol( id, spec, xsn ) { // for CSN property '#'
|
|
|
1230
1274
|
xsn.sym = { id, location: location() };
|
|
1231
1275
|
}
|
|
1232
1276
|
|
|
1277
|
+
/**
|
|
1278
|
+
* Wrapper around the default `ref` spec: Don't allow references of length 1 for types.
|
|
1279
|
+
*/
|
|
1280
|
+
function typeRef( val, spec, xsn, csn ) {
|
|
1281
|
+
// e.g. { ref: [ 'T' ] }
|
|
1282
|
+
if (Array.isArray(val) && val.length <= 1)
|
|
1283
|
+
warning( 'syntax-deprecated-type-ref', location(true), { '#': 'std', prop: 'type' });
|
|
1284
|
+
|
|
1285
|
+
return arrayOf(spec.arrayOf)(val, spec, xsn, csn);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Similar to refItem(), but warns that the item should be a string if `id` is the only CSN
|
|
1290
|
+
* property inside the ref-item.
|
|
1291
|
+
*/
|
|
1292
|
+
function typeRefItem( val, spec, xsn, csn ) {
|
|
1293
|
+
// e.g. [ 'T', { id: 'elem', other_prop: true } ]
|
|
1294
|
+
// avoid duplicate messages for single-item reference, see typeRef()
|
|
1295
|
+
if (val && csn.ref?.length > 1 && typeof val === 'object' && val.id) {
|
|
1296
|
+
const ownKeysCount = Object.keys(val).filter(key => ourpropsRegex.test(key)).length;
|
|
1297
|
+
if (ownKeysCount === 1)
|
|
1298
|
+
warning('syntax-deprecated-type-ref', location(true), { '#': 'ref-item', prop: 'ref[]' });
|
|
1299
|
+
}
|
|
1300
|
+
return refItem(val, spec);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1233
1303
|
/**
|
|
1234
1304
|
* returns:
|
|
1235
1305
|
* - false = no "...",
|
|
@@ -1283,21 +1353,28 @@ function annoValue( val, spec ) {
|
|
|
1283
1353
|
}
|
|
1284
1354
|
return retval;
|
|
1285
1355
|
}
|
|
1286
|
-
if (typeof val['
|
|
1287
|
-
|
|
1356
|
+
else if (typeof val['='] === 'string') {
|
|
1357
|
+
const valKeys = Object.keys(val);
|
|
1358
|
+
if (valKeys.length > 1 && isBetaEnabled(userOptions, 'annotationExpressions')) {
|
|
1359
|
+
const s = schema['@'].schema['-expr'];
|
|
1360
|
+
const r = { location: location() };
|
|
1361
|
+
Object.assign(r, object(val, s));
|
|
1362
|
+
return r;
|
|
1363
|
+
}
|
|
1364
|
+
else if (valKeys.length === 1) {
|
|
1288
1365
|
++virtualLine;
|
|
1289
|
-
const
|
|
1290
|
-
symbol( val['#'], schema['#'], xsn );
|
|
1366
|
+
const r = refSplit( val['='], '=' );
|
|
1291
1367
|
++virtualLine;
|
|
1292
|
-
return
|
|
1368
|
+
return r;
|
|
1293
1369
|
}
|
|
1294
1370
|
}
|
|
1295
|
-
|
|
1371
|
+
if (typeof val['#'] === 'string') {
|
|
1296
1372
|
if (Object.keys( val ).length === 1) {
|
|
1297
1373
|
++virtualLine;
|
|
1298
|
-
const
|
|
1374
|
+
const xsn = { location: location() };
|
|
1375
|
+
symbol( val['#'], schema['#'], xsn );
|
|
1299
1376
|
++virtualLine;
|
|
1300
|
-
return
|
|
1377
|
+
return xsn;
|
|
1301
1378
|
}
|
|
1302
1379
|
}
|
|
1303
1380
|
else if (val['...'] && Object.keys( val ).length === 1) {
|
|
@@ -1314,13 +1391,13 @@ function annoValue( val, spec ) {
|
|
|
1314
1391
|
++virtualLine;
|
|
1315
1392
|
return r;
|
|
1316
1393
|
}
|
|
1317
|
-
const
|
|
1394
|
+
const r = { struct: Object.create(null), literal: 'struct', location: location() };
|
|
1318
1395
|
++virtualLine;
|
|
1319
1396
|
for (const name of Object.keys( val )) {
|
|
1320
|
-
struct[name] = annotation( val[name], schema['@'], null, val, name );
|
|
1397
|
+
r.struct[name] = annotation( val[name], schema['@'], null, val, name );
|
|
1321
1398
|
++virtualLine;
|
|
1322
1399
|
}
|
|
1323
|
-
return
|
|
1400
|
+
return r;
|
|
1324
1401
|
}
|
|
1325
1402
|
|
|
1326
1403
|
function annotation( val, spec, xsn, csn, name ) {
|
|
@@ -1333,6 +1410,7 @@ function annotation( val, spec, xsn, csn, name ) {
|
|
|
1333
1410
|
n.absolute = absolute;
|
|
1334
1411
|
if (variantIndex < absolute.length)
|
|
1335
1412
|
n.variant = { id: name.substring( variantIndex ), location: location() };
|
|
1413
|
+
|
|
1336
1414
|
const r = annoValue( val, spec );
|
|
1337
1415
|
r.name = n;
|
|
1338
1416
|
return r;
|
|
@@ -1612,8 +1690,11 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1612
1690
|
const p0 = schema[prop] ? prop : prop.charAt(0);
|
|
1613
1691
|
const s = (parentSpec.schema || schema)[p0];
|
|
1614
1692
|
if (!s || s.noPrefix === (prop !== p0) ) {
|
|
1615
|
-
if (prop && !ourpropsRegex.test( prop ))
|
|
1693
|
+
if (prop && !ourpropsRegex.test( prop )) {
|
|
1694
|
+
if (parentSpec.ignoreExtra)
|
|
1695
|
+
return { prop, type: ignore };
|
|
1616
1696
|
return { prop, type: extra };
|
|
1697
|
+
}
|
|
1617
1698
|
// TODO v4: No warning with --sloppy
|
|
1618
1699
|
warning( 'syntax-unknown-property', location(true), { prop },
|
|
1619
1700
|
'Unknown CSN property $(PROP)' );
|
|
@@ -1628,10 +1709,10 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1628
1709
|
}
|
|
1629
1710
|
const zero = s.vZeroFor;
|
|
1630
1711
|
if (zero) { // (potential) CSN v0.1.0 property
|
|
1631
|
-
const
|
|
1632
|
-
if (expected( zero, schema[zero] ) && !(
|
|
1712
|
+
const groups = s.xorGroups;
|
|
1713
|
+
if (expected( zero, schema[zero] ) && !(groups.length && groups.every(group => xor[group]))) {
|
|
1633
1714
|
replaceZeroProp( prop, zero );
|
|
1634
|
-
|
|
1715
|
+
for (const group of groups)
|
|
1635
1716
|
xor[group] = prop;
|
|
1636
1717
|
onlyWith( s, s.onlyWith, csn, prop, xor, expected );
|
|
1637
1718
|
return s;
|
|
@@ -1650,8 +1731,8 @@ function getSpec( parentSpec, csn, prop, xor, expected, kind ) {
|
|
|
1650
1731
|
kind,
|
|
1651
1732
|
} );
|
|
1652
1733
|
}
|
|
1653
|
-
else if (checkAndSetXorGroup( s.
|
|
1654
|
-
|
|
1734
|
+
else if (checkAndSetXorGroup( s.xorGroups, s.xorException, prop, xor )) {
|
|
1735
|
+
// TODO: If all targets of onlyWith are xor-excluded/ignore, also exclude/ignore this one.
|
|
1655
1736
|
onlyWith( s, s.onlyWith, csn, prop, xor, expected );
|
|
1656
1737
|
return s;
|
|
1657
1738
|
}
|
|
@@ -1693,7 +1774,7 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1693
1774
|
const allowed = need.filter( p => expected( p, spec ));
|
|
1694
1775
|
// There should be at least one elem, otherwise the spec is wrong;
|
|
1695
1776
|
// first try to find element which is not excluded
|
|
1696
|
-
need = allowed.find( p => !
|
|
1777
|
+
need = allowed.find( p => !schema[p].xorGroups?.some(g => xor[g]) ) || allowed[0];
|
|
1697
1778
|
}
|
|
1698
1779
|
if (prop) {
|
|
1699
1780
|
error( 'syntax-missing-property', location(true), // location at $(PROP)
|
|
@@ -1713,18 +1794,31 @@ function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
|
1713
1794
|
return spec;
|
|
1714
1795
|
}
|
|
1715
1796
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
if (
|
|
1797
|
+
/**
|
|
1798
|
+
* @param {string[]} groups
|
|
1799
|
+
* @param {string} exception
|
|
1800
|
+
* @param {string} prop
|
|
1801
|
+
* @param {object} xor
|
|
1802
|
+
* @return {boolean}
|
|
1803
|
+
*/
|
|
1804
|
+
function checkAndSetXorGroup( groups, exception, prop, xor ) {
|
|
1805
|
+
if (!groups || groups.length === 0)
|
|
1725
1806
|
return true;
|
|
1726
|
-
|
|
1727
|
-
return
|
|
1807
|
+
let silent = false;
|
|
1808
|
+
return groups.every((group) => {
|
|
1809
|
+
const siblingprop = xor[group];
|
|
1810
|
+
if (!siblingprop) {
|
|
1811
|
+
xor[group] = prop;
|
|
1812
|
+
return true;
|
|
1813
|
+
}
|
|
1814
|
+
if (siblingprop === exception)
|
|
1815
|
+
return true;
|
|
1816
|
+
if (!silent) {
|
|
1817
|
+
error( 'syntax-unexpected-property', location(true), { '#': 'sibling', prop, siblingprop } );
|
|
1818
|
+
silent = true;
|
|
1819
|
+
}
|
|
1820
|
+
return false;
|
|
1821
|
+
});
|
|
1728
1822
|
}
|
|
1729
1823
|
|
|
1730
1824
|
function implicitName( ref ) {
|
|
@@ -1824,6 +1918,7 @@ function popLocation( obj ) {
|
|
|
1824
1918
|
function resetHeapModuleVars() {
|
|
1825
1919
|
vocabInDefinitions = null;
|
|
1826
1920
|
dollarLocations = [];
|
|
1921
|
+
userOptions = null;
|
|
1827
1922
|
message = () => undefined;
|
|
1828
1923
|
error = () => undefined;
|
|
1829
1924
|
warning = () => undefined;
|
|
@@ -1848,6 +1943,7 @@ function toXsn( csn, filename, options, messageFunctions ) {
|
|
|
1848
1943
|
arrayLevelCount = 0;
|
|
1849
1944
|
inExtensions = null;
|
|
1850
1945
|
vocabInDefinitions = null;
|
|
1946
|
+
userOptions = options;
|
|
1851
1947
|
|
|
1852
1948
|
const xsn = { $frontend: 'json' };
|
|
1853
1949
|
|