@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/checks/elements.js
CHANGED
|
@@ -93,7 +93,7 @@ function checkPrimaryKey(art) {
|
|
|
93
93
|
function checkVirtualElement(member) {
|
|
94
94
|
if (member.virtual) {
|
|
95
95
|
if (this.csnUtils.isAssociation(member.type)) { // or Composition ???
|
|
96
|
-
this.error(null, member.$path, {},
|
|
96
|
+
this.error(null, member.$path, {}, 'Element can\'t be virtual and an association');
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
package/lib/checks/parameters.js
CHANGED
|
@@ -12,8 +12,10 @@ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
|
|
|
12
12
|
*/
|
|
13
13
|
function checkForParams(parent, name, params, path) {
|
|
14
14
|
const artifact = this.csn.definitions[path[1]];
|
|
15
|
-
if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && !(parent.kind !== 'entity'))
|
|
16
|
-
this.error('ref-unexpected-params', [ ...path, 'params' ],
|
|
15
|
+
if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && !(parent.kind !== 'entity')) {
|
|
16
|
+
this.error('ref-unexpected-params', [ ...path, 'params' ], { value: this.options.sqlDialect },
|
|
17
|
+
'Parameterized views can\'t be used with sqlDialect $(VALUE)');
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
module.exports = {
|
|
@@ -101,7 +101,7 @@ function checkQueryForNoDBArtifacts(query) {
|
|
|
101
101
|
this.error(null,
|
|
102
102
|
obj.$path,
|
|
103
103
|
{ id: pathStep, elemref: obj },
|
|
104
|
-
|
|
104
|
+
'Path step $(ID) of $(ELEMREF) has no foreign keys');
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
if (art.on) {
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
function checkSqlAnnotationOnElement(member, memberName, prop, path) {
|
|
15
15
|
if (member['@sql.replace'])
|
|
16
|
-
this.error(null, path, { anno: 'sql.replace' },
|
|
16
|
+
this.error(null, path, { anno: 'sql.replace' }, 'Annotation $(ANNO) is reserved and must not be used');
|
|
17
17
|
if (member['@sql.prepend'])
|
|
18
|
-
this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' },
|
|
18
|
+
this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, 'Annotation $(ANNO) can\'t be used on elements' );
|
|
19
19
|
|
|
20
20
|
if (member['@sql.append']) {
|
|
21
21
|
if (this.artifact.query)
|
|
22
|
-
this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' },
|
|
22
|
+
this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on elements in views' );
|
|
23
23
|
else if (this.csnUtils.isStructured(member))
|
|
24
|
-
this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' },
|
|
24
|
+
this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' }, 'Annotation $(ANNO) can\'t be used on structured elements' );
|
|
25
25
|
else
|
|
26
26
|
checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
|
|
27
27
|
}
|
|
@@ -37,7 +37,7 @@ function checkSqlAnnotationOnElement(member, memberName, prop, path) {
|
|
|
37
37
|
function checkValidAnnoValue(carrier, annotation, path, error, options) {
|
|
38
38
|
if (carrier[annotation] !== undefined && carrier[annotation] !== null) {
|
|
39
39
|
if (typeof carrier[annotation] !== 'string')
|
|
40
|
-
error(null, path, { anno: annotation.slice(1), type: typeof carrier[annotation] },
|
|
40
|
+
error(null, path, { anno: annotation.slice(1), type: typeof carrier[annotation] }, 'Annotation $(ANNO) must be a string, found $(TYPE)' );
|
|
41
41
|
else if (options.transformation === 'sql') // HDI and HDBCDS do their own checks
|
|
42
42
|
guardAgainstInjection(annotation, carrier[annotation], path, error);
|
|
43
43
|
}
|
|
@@ -52,20 +52,22 @@ function checkValidAnnoValue(carrier, annotation, path, error, options) {
|
|
|
52
52
|
function checkSqlAnnotationOnArtifact(artifact, artifactName) {
|
|
53
53
|
if (artifact.kind !== 'entity') {
|
|
54
54
|
if (artifact['@sql.prepend'])
|
|
55
|
-
this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.prepend', kind: artifact.kind },
|
|
55
|
+
this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.prepend', kind: artifact.kind }, 'Annotation $(NAME) can\'t be used on an artifact of kind $(KIND)' );
|
|
56
56
|
if (artifact['@sql.append'])
|
|
57
|
-
this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.append', kind: artifact.kind },
|
|
57
|
+
this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.append', kind: artifact.kind }, 'Annotation $(NAME) can\'t be used on an artifact of kind $(KIND)' );
|
|
58
58
|
}
|
|
59
59
|
else if (artifact['@sql.prepend']) {
|
|
60
60
|
if (artifact.query)
|
|
61
|
-
this.message('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' },
|
|
61
|
+
this.message('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, 'Annotation $(NAME) can\'t be used on views' );
|
|
62
62
|
else
|
|
63
63
|
checkValidAnnoValue(artifact, '@sql.prepend', [ 'definitions', artifactName ], this.error, this.options);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
if (artifact['@sql.replace'])
|
|
68
|
-
this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' },
|
|
67
|
+
if (artifact['@sql.replace']) {
|
|
68
|
+
this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' },
|
|
69
|
+
'Annotation $(ANNO) is reserved and must not be used');
|
|
70
|
+
}
|
|
69
71
|
|
|
70
72
|
checkValidAnnoValue(artifact, '@sql.append', [ 'definitions', artifactName ], this.error, this.options);
|
|
71
73
|
}
|
package/lib/checks/validator.js
CHANGED
|
@@ -190,6 +190,19 @@ function mergeCsnValidators(csnValidators, that) {
|
|
|
190
190
|
return remapped;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Depending on the dialect we need to run different validations.
|
|
195
|
+
*
|
|
196
|
+
* @param {CSN.Options} options
|
|
197
|
+
* @returns {any[]} Array of validator functions (or objects?)
|
|
198
|
+
*/
|
|
199
|
+
function getDBCsnValidators(options) {
|
|
200
|
+
if (options.sqlDialect === 'postgres' || options.sqlDialect === 'h2')
|
|
201
|
+
return [ ...forRelationalDBCsnValidators, checkForHanaTypes, checkForParams ];
|
|
202
|
+
|
|
203
|
+
return forRelationalDBCsnValidators;
|
|
204
|
+
}
|
|
205
|
+
|
|
193
206
|
/**
|
|
194
207
|
* @param {CSN.Model} csn CSN to check
|
|
195
208
|
* @param {object} that Will be provided to the validators via "this"
|
|
@@ -197,10 +210,7 @@ function mergeCsnValidators(csnValidators, that) {
|
|
|
197
210
|
*/
|
|
198
211
|
function forRelationalDB(csn, that) {
|
|
199
212
|
return _validate(csn, that,
|
|
200
|
-
that.options
|
|
201
|
-
? [ ...forRelationalDBCsnValidators,
|
|
202
|
-
checkForHanaTypes,
|
|
203
|
-
checkForParams ] : forRelationalDBCsnValidators,
|
|
213
|
+
getDBCsnValidators(that.options),
|
|
204
214
|
forRelationalDBMemberValidators.concat(commonMemberValidators),
|
|
205
215
|
forRelationalDBArtifactValidators.concat(commonArtifactValidators).concat(
|
|
206
216
|
// why is this hana exclusive
|
|
@@ -295,7 +295,7 @@ function assertConsistency( model, stage ) {
|
|
|
295
295
|
none: { optional: () => true }, // parse error
|
|
296
296
|
},
|
|
297
297
|
columns: {
|
|
298
|
-
kind: [ 'extend' ],
|
|
298
|
+
kind: [ 'extend', '$column' ],
|
|
299
299
|
test: isArray( column ),
|
|
300
300
|
optional: thoseWithKind,
|
|
301
301
|
enum: [ '*' ],
|
|
@@ -363,12 +363,13 @@ function assertConsistency( model, stage ) {
|
|
|
363
363
|
suffix: { test: TODO },
|
|
364
364
|
kind: {
|
|
365
365
|
isRequired: !stageParser && (() => true),
|
|
366
|
+
kind: true,
|
|
366
367
|
// required to be set by Core Compiler even with parse errors
|
|
367
368
|
test: isString,
|
|
368
369
|
enum: [
|
|
369
370
|
'context', 'service', 'entity', 'type', 'aspect', 'const', 'annotation',
|
|
370
371
|
'element', 'enum', 'action', 'function', 'param', 'key', 'event',
|
|
371
|
-
'annotate', 'extend',
|
|
372
|
+
'annotate', 'extend', '$column',
|
|
372
373
|
'select', '$join', 'mixin',
|
|
373
374
|
'source', 'namespace', 'using',
|
|
374
375
|
'$tableAlias', '$navElement',
|
|
@@ -398,7 +399,7 @@ function assertConsistency( model, stage ) {
|
|
|
398
399
|
requires: [ 'location', 'path' ],
|
|
399
400
|
optional: [ 'scope', 'variant', '_artifact', '$inferred', '$parens', 'sort', 'nulls' ],
|
|
400
401
|
},
|
|
401
|
-
none: { optional:
|
|
402
|
+
none: { optional: () => true }, // parse error
|
|
402
403
|
// TODO: why optional / enough in name?
|
|
403
404
|
// TODO: "yes" instead "none": val: true, optional literal/location
|
|
404
405
|
val: {
|
|
@@ -821,7 +822,7 @@ function assertConsistency( model, stage ) {
|
|
|
821
822
|
const sub = Object.assign( {}, s.inherits && schema[s.inherits], s );
|
|
822
823
|
if (spec.requires && sub.requires)
|
|
823
824
|
sub.requires = [ ...sub.requires, ...spec.requires ];
|
|
824
|
-
if (spec.optional && sub.optional)
|
|
825
|
+
if (Array.isArray( spec.optional ) && Array.isArray( sub.optional ))
|
|
825
826
|
sub.optional = [ ...sub.optional, ...spec.optional ];
|
|
826
827
|
// console.log(expressionSpec(node) );
|
|
827
828
|
(sub.test || standard)( node, parent, prop, sub, idx );
|
|
@@ -834,9 +835,9 @@ function assertConsistency( model, stage ) {
|
|
|
834
835
|
return 'val';
|
|
835
836
|
else if (node.query)
|
|
836
837
|
return 'query';
|
|
837
|
-
else if (
|
|
838
|
-
return '
|
|
839
|
-
return '
|
|
838
|
+
else if (node.op)
|
|
839
|
+
return 'op';
|
|
840
|
+
return 'none'; // parse error
|
|
840
841
|
}
|
|
841
842
|
|
|
842
843
|
function args( node, parent, prop, spec ) {
|
package/lib/compiler/checks.js
CHANGED
|
@@ -18,6 +18,8 @@ const builtins = require('../compiler/builtins');
|
|
|
18
18
|
const {
|
|
19
19
|
forEachGeneric, forEachDefinition, forEachMember,
|
|
20
20
|
} = require('../base/model');
|
|
21
|
+
const { CompilerAssertion } = require('../base/error');
|
|
22
|
+
const { pathName } = require('./utils');
|
|
21
23
|
|
|
22
24
|
function check( model ) { // = XSN
|
|
23
25
|
const {
|
|
@@ -66,7 +68,7 @@ function check( model ) { // = XSN
|
|
|
66
68
|
forEachGeneric( elem, 'elements', checkElement );
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
function checkName( construct ) {
|
|
71
|
+
function checkName( construct ) { // TODO: move to define.js
|
|
70
72
|
if (model.options.$skipNameCheck)
|
|
71
73
|
return;
|
|
72
74
|
// TODO: Move a corrected version of this check to definer (but do not rely
|
|
@@ -78,11 +80,11 @@ function check( model ) { // = XSN
|
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
// TODO: move into definer.js
|
|
82
83
|
function checkLocalizedElement(elem) {
|
|
83
84
|
// if it is directly a localized element
|
|
84
85
|
if (elem.localized && elem.localized.val) {
|
|
85
86
|
const type = elem._effectiveType;
|
|
87
|
+
// See discussion issue #6520: should we allow all scalar types?
|
|
86
88
|
if (!type || !type.builtin || type.category !== 'string') {
|
|
87
89
|
warning(null, [ elem.type?.location, elem ], { keyword: 'localized' },
|
|
88
90
|
'Keyword $(KEYWORD) should only be used in combination with string types');
|
|
@@ -339,6 +341,7 @@ function check( model ) { // = XSN
|
|
|
339
341
|
}
|
|
340
342
|
|
|
341
343
|
// Check that min and max cardinalities of 'elem' in 'art' have legal values
|
|
344
|
+
// TODO: move to define.js or parsers
|
|
342
345
|
function checkCardinality(elem) {
|
|
343
346
|
if (!elem.cardinality)
|
|
344
347
|
return;
|
|
@@ -382,13 +385,16 @@ function check( model ) { // = XSN
|
|
|
382
385
|
|
|
383
386
|
// If provided, min cardinality must not exceed max cardinality (note that
|
|
384
387
|
// '*' is considered to be >= any number)
|
|
385
|
-
const pair = [ [ 'sourceMin', 'sourceMax', '
|
|
388
|
+
const pair = [ [ 'sourceMin', 'sourceMax', 'source' ], [ 'targetMin', 'targetMax', 'target' ] ];
|
|
386
389
|
pair.forEach((p) => {
|
|
387
390
|
if (elem.cardinality[p[0]] && elem.cardinality[p[1]] &&
|
|
388
391
|
elem.cardinality[p[1]].literal === 'number' &&
|
|
389
392
|
elem.cardinality[p[0]].val > elem.cardinality[p[1]].val) {
|
|
390
|
-
error(null, [ elem.cardinality.location, elem ], {},
|
|
391
|
-
|
|
393
|
+
error(null, [ elem.cardinality.location, elem ], { '#': p[2] }, {
|
|
394
|
+
std: 'Minimum cardinality must not be greater than maximum cardinality', // variant unused
|
|
395
|
+
source: 'Source minimum cardinality must not be greater than source maximum cardinality',
|
|
396
|
+
target: 'Target minimum cardinality must not be greater than target maximum cardinality',
|
|
397
|
+
});
|
|
392
398
|
}
|
|
393
399
|
});
|
|
394
400
|
}
|
|
@@ -494,7 +500,7 @@ function check( model ) { // = XSN
|
|
|
494
500
|
// associations can be followed (in the ON condition)
|
|
495
501
|
//
|
|
496
502
|
// TODO: this function must be completely reworked, probably even before
|
|
497
|
-
// integration into name
|
|
503
|
+
// integration into name resolution - did the first step.
|
|
498
504
|
// It is also incomplete, as associations in structures are not checked.
|
|
499
505
|
// Additionally, `$self.assoc` references are also not found.
|
|
500
506
|
function singleCheckUnmanagedAssocCondArgumentNoFollowUnmanagedAssoc(elem, arg, op) {
|
|
@@ -639,8 +645,10 @@ function check( model ) { // = XSN
|
|
|
639
645
|
'An association can\'t be used as a value in an expression');
|
|
640
646
|
}
|
|
641
647
|
|
|
642
|
-
if (isDollarSelfOrProjectionOperand(arg))
|
|
643
|
-
error(null, arg.location, {
|
|
648
|
+
if (isDollarSelfOrProjectionOperand(arg)) {
|
|
649
|
+
error(null, arg.location, { id: arg.path[0].id },
|
|
650
|
+
'$(ID) can only be used as a value in a comparison to an association');
|
|
651
|
+
}
|
|
644
652
|
|
|
645
653
|
// Recursively traverse the argument expression
|
|
646
654
|
checkTreeLikeExpression(arg, allowAssocTail);
|
|
@@ -739,7 +747,9 @@ function check( model ) { // = XSN
|
|
|
739
747
|
|
|
740
748
|
// Element must exist in annotation
|
|
741
749
|
if (!elementDecl) {
|
|
742
|
-
warning(null, anno.location || anno.name.location,
|
|
750
|
+
warning(null, anno.location || anno.name.location,
|
|
751
|
+
{ name: pathName(anno.name.path), anno: annoDecl.name.absolute },
|
|
752
|
+
'Element $(NAME) not found for annotation $(ANNO)');
|
|
743
753
|
return;
|
|
744
754
|
}
|
|
745
755
|
|
|
@@ -751,8 +761,8 @@ function check( model ) { // = XSN
|
|
|
751
761
|
// Must have literal or path unless it is a boolean
|
|
752
762
|
if (!anno.literal && !anno.path && getFinalTypeNameOf(elementDecl) !== 'cds.Boolean') {
|
|
753
763
|
if (elementDecl.type && elementDecl.type._artifact.name.absolute) {
|
|
754
|
-
warning(null, anno.location || anno.name.location, {},
|
|
755
|
-
|
|
764
|
+
warning(null, anno.location || anno.name.location, { type: elementDecl.type._artifact },
|
|
765
|
+
'Expecting a value of type $(TYPE) for the annotation');
|
|
756
766
|
}
|
|
757
767
|
else {
|
|
758
768
|
warning(null, anno.location || anno.name.location, {},
|
|
@@ -807,31 +817,31 @@ function check( model ) { // = XSN
|
|
|
807
817
|
if (builtins.isStringTypeName(type)) {
|
|
808
818
|
if (value.literal !== 'string' && value.literal !== 'enum' &&
|
|
809
819
|
!elementDecl._effectiveType.enum)
|
|
810
|
-
warning(null, loc, {},
|
|
820
|
+
warning(null, loc, { type }, 'A string value is required for type $(TYPE)');
|
|
811
821
|
}
|
|
812
822
|
else if (builtins.isBinaryTypeName(type)) {
|
|
813
823
|
if (value.literal !== 'string' && value.literal !== 'x')
|
|
814
|
-
warning(null, loc, {},
|
|
824
|
+
warning(null, loc, { type }, 'A hexadecimal string value is required for type $(TYPE)');
|
|
815
825
|
}
|
|
816
826
|
else if (builtins.isNumericTypeName(type)) {
|
|
817
827
|
if (value.literal !== 'number' && value.literal !== 'enum' &&
|
|
818
828
|
!elementDecl._effectiveType.enum)
|
|
819
|
-
warning(null, loc, {},
|
|
829
|
+
warning(null, loc, { type }, 'A numerical value is required for type $(TYPE)');
|
|
820
830
|
}
|
|
821
831
|
else if (builtins.isDateOrTimeTypeName(type)) {
|
|
822
832
|
if (value.literal !== 'date' && value.literal !== 'time' &&
|
|
823
833
|
value.literal !== 'timestamp' && value.literal !== 'string')
|
|
824
|
-
warning(null, loc, {},
|
|
834
|
+
warning(null, loc, { type }, 'A date/time value or a string is required for type $(TYPE)');
|
|
825
835
|
}
|
|
826
836
|
else if (builtins.isBooleanTypeName(type)) {
|
|
827
837
|
if (value.literal && value.literal !== 'boolean')
|
|
828
|
-
warning(null, loc, {},
|
|
838
|
+
warning(null, loc, { type }, 'A boolean value is required for type $(TYPE)');
|
|
829
839
|
}
|
|
830
840
|
else if (builtins.isRelationTypeName(type) || builtins.isGeoTypeName(type)) {
|
|
831
|
-
warning(null, loc, {},
|
|
841
|
+
warning(null, loc, { type }, 'Type $(TYPE) can\'t be assigned a value');
|
|
832
842
|
}
|
|
833
843
|
else {
|
|
834
|
-
throw new
|
|
844
|
+
throw new CompilerAssertion(`Unknown primitive type name: ${ type }`);
|
|
835
845
|
}
|
|
836
846
|
|
|
837
847
|
// Check enums
|
|
@@ -841,12 +851,12 @@ function check( model ) { // = XSN
|
|
|
841
851
|
// Enum symbol provided and expected
|
|
842
852
|
if (!expectedEnum[value.sym.id]) {
|
|
843
853
|
// .. but no such constant
|
|
844
|
-
warning(null, loc, {
|
|
854
|
+
warning(null, loc, { id: `#${ value.sym.id }` }, 'Enum symbol $(ID) not found in enum');
|
|
845
855
|
}
|
|
846
856
|
}
|
|
847
857
|
else {
|
|
848
858
|
// Enum symbol provided but not expected
|
|
849
|
-
warning(null, loc, {},
|
|
859
|
+
warning(null, loc, { id: `#${ value.sym.id }`, type }, 'Can\'t use enum symbol $(ID) for non-enum type $(TYPE)');
|
|
850
860
|
}
|
|
851
861
|
}
|
|
852
862
|
else if (expectedEnum) {
|
package/lib/compiler/define.js
CHANGED
|
@@ -1,15 +1,31 @@
|
|
|
1
|
-
// Compiler phase "define": transform dictionary of AST-like
|
|
1
|
+
// Compiler phase 1 = "define": transform dictionary of AST-like XSNs into XSN
|
|
2
2
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// The 'define' phase (function 'define' below) is the first phase of the compile
|
|
4
|
+
// function. In it, the compiler
|
|
5
|
+
//
|
|
6
|
+
// - collects definitions and extensions from the XSN representation of CDL and
|
|
7
|
+
// CSN sources (“ASTs”) into _one_ XSN model,
|
|
8
|
+
// - sets “structural” links between XSN nodes and completes the “name”,
|
|
9
|
+
// some links and names inside `extensions` are set at a later stage
|
|
10
|
+
// - reports errors for: “late” syntax errors (when it is more convenient to do
|
|
11
|
+
// it here instead of doing it in both CDL and CSN parser), “structural” errors
|
|
12
|
+
// and “duplicate definition errors”
|
|
13
|
+
|
|
14
|
+
// The 'define' phase is the only compile() phase which is also called for
|
|
15
|
+
// parse.cdl. See file ./finalize-parse-cdl.js for details.
|
|
16
|
+
|
|
17
|
+
// --------- TODO: begin in extra markdown document -----------------------------
|
|
18
|
+
|
|
19
|
+
// An XSN for a source looks like
|
|
20
|
+
// { kind: 'source', artifacts: <dictionary of artifact defs>, namespace: {}, ... }
|
|
5
21
|
//
|
|
6
22
|
// The property `artifacts` of a source contains the top-level definitions.
|
|
7
23
|
// Definitions inside a context are not listed here (as opposed to
|
|
8
24
|
// `definitions`, see below), but inside the property `artifacts` of that context.
|
|
9
25
|
|
|
10
26
|
// The 'define' phase (function 'define' below) enriches a dictionary of
|
|
11
|
-
// (file names to) AST-like
|
|
12
|
-
// is called "augmented CSN":
|
|
27
|
+
// (file names to) AST-like XSNs and restructure them a little bit, the result
|
|
28
|
+
// is called XSN ("augmented CSN"):
|
|
13
29
|
// { sources: <dictionary of ASTs>, definitions: <dictionary of artifact defs> }
|
|
14
30
|
//
|
|
15
31
|
// The property `sources` is the input argument (dictionary of source ASTs).
|
|
@@ -20,14 +36,8 @@
|
|
|
20
36
|
// objects as the definitions accessible via `sources` and `artifacts` of the
|
|
21
37
|
// corresponding source/context.
|
|
22
38
|
//
|
|
23
|
-
// Because different sources could define artifacts with the same absolute
|
|
24
|
-
// name, this compiler phase also put a property `messages` to the resulting
|
|
25
|
-
// model, which is a vector of messages for the redefinitions. (Using the same
|
|
26
|
-
// name for different definitions in one source is already recognized during
|
|
27
|
-
// parsing.)
|
|
28
|
-
//
|
|
29
39
|
// You get the compact "official" CSN format by applying the function exported
|
|
30
|
-
// by "../json/to-csn.js" to the
|
|
40
|
+
// by "../json/to-csn.js" to the XSN.
|
|
31
41
|
|
|
32
42
|
// Example 'file.cds':
|
|
33
43
|
// namespace A;
|
|
@@ -54,7 +64,7 @@
|
|
|
54
64
|
// An artifact definition looks as follows (example: context "A.B" above):
|
|
55
65
|
// {
|
|
56
66
|
// kind: 'context',
|
|
57
|
-
// name: { id: 'B', absolute: 'A.B', location: { <for the id "B"> } },
|
|
67
|
+
// name: { path: [ { id: 'B'} ], absolute: 'A.B', location: { <for the id "B"> } },
|
|
58
68
|
// artifacts: <for contexts, a dictionary of artifacts defined within>,
|
|
59
69
|
// location: { <of the complete artifact definition> } },
|
|
60
70
|
// _parent: <the parent artifact, here the source 'file.cds'>
|
|
@@ -74,11 +84,10 @@
|
|
|
74
84
|
// location: { <of the complete element definition> } },
|
|
75
85
|
// _parent: <the parent artifact, here the type "A.B.C">
|
|
76
86
|
// }
|
|
77
|
-
//
|
|
78
|
-
// './resolver.js'. We then get the properties `type.absolute` and `length`.
|
|
87
|
+
// --------- TODO: end in extra markdown document -------------------------------
|
|
79
88
|
|
|
80
89
|
// Sub phase 1 (addXYZ) - only for main artifacts
|
|
81
|
-
// - set _block links
|
|
90
|
+
// - set _block links for main definitions, vocabulary and extensions
|
|
82
91
|
// - store definitions (including context extensions), NO duplicate check
|
|
83
92
|
// - artifact name check
|
|
84
93
|
// - Note: the only allow name resolving is resolveUncheckedPath(),
|
|
@@ -96,6 +105,7 @@
|
|
|
96
105
|
// More sub phases...
|
|
97
106
|
|
|
98
107
|
// The main difficulty is the correct behavior concerning duplicate definitions
|
|
108
|
+
// - For code completion, all duplicate definitions must be further checked.
|
|
99
109
|
// - We need a unique object for the _subArtifacts dictionary.
|
|
100
110
|
// - We must have a property at the artifact whether there are duplicates in order
|
|
101
111
|
// to avoid consequential or repeated errors.
|
|
@@ -121,6 +131,7 @@ const {
|
|
|
121
131
|
dependsOnSilent,
|
|
122
132
|
pathName,
|
|
123
133
|
splitIntoPath,
|
|
134
|
+
annotationHasEllipsis,
|
|
124
135
|
} = require('./utils');
|
|
125
136
|
const { compareLayer } = require('./moduleLayers');
|
|
126
137
|
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
@@ -148,15 +159,15 @@ function define( model ) {
|
|
|
148
159
|
const {
|
|
149
160
|
resolveUncheckedPath,
|
|
150
161
|
checkAnnotate,
|
|
151
|
-
defineAnnotations,
|
|
152
162
|
} = model.$functions;
|
|
153
163
|
|
|
154
164
|
const extensionsDict = Object.create(null);
|
|
155
165
|
Object.assign( model.$functions, {
|
|
156
166
|
initArtifact,
|
|
157
167
|
initMembers,
|
|
158
|
-
extensionsDict, // a dictionary - TODO:
|
|
168
|
+
extensionsDict, // a dictionary - TODO: put directly into model?
|
|
159
169
|
checkDefinitions,
|
|
170
|
+
initAnnotations,
|
|
160
171
|
} );
|
|
161
172
|
// During the definer, we can only resolve artifact references, i.e,
|
|
162
173
|
// after a `.`, we only search in the `_subArtifacts` dictionary:
|
|
@@ -193,6 +204,7 @@ function define( model ) {
|
|
|
193
204
|
initNamespaceAndUsing( model.sources[name] );
|
|
194
205
|
dictForEach( model.definitions, initArtifact );
|
|
195
206
|
dictForEach( model.vocabularies, initVocabulary );
|
|
207
|
+
dictForEach( extensionsDict, initExtension );
|
|
196
208
|
|
|
197
209
|
mergeI18nBlocks();
|
|
198
210
|
}
|
|
@@ -394,7 +406,7 @@ function define( model ) {
|
|
|
394
406
|
|
|
395
407
|
// Phase 2 ("init") --------------------------------------------------------
|
|
396
408
|
// Functions called from top-level: initNamespaceAndUsing(), initArtifact(),
|
|
397
|
-
// initVocabulary()
|
|
409
|
+
// initVocabulary(), initExtension()
|
|
398
410
|
|
|
399
411
|
function checkRedefinition( art ) {
|
|
400
412
|
if (!art.$duplicates || art.$errorReported === 'syntax-duplicate-extend' ||
|
|
@@ -461,11 +473,11 @@ function define( model ) {
|
|
|
461
473
|
initParentLink( art, model.definitions );
|
|
462
474
|
const block = art._block;
|
|
463
475
|
checkRedefinition( art );
|
|
464
|
-
|
|
476
|
+
initAnnotations( art, block );
|
|
465
477
|
initMembers( art, art, block );
|
|
466
478
|
initDollarSelf( art ); // $self
|
|
467
479
|
if (art.params)
|
|
468
|
-
|
|
480
|
+
initDollarParameters( art );
|
|
469
481
|
if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
|
|
470
482
|
extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
|
|
471
483
|
|
|
@@ -492,7 +504,7 @@ function define( model ) {
|
|
|
492
504
|
initParentLink( art, model.vocabularies );
|
|
493
505
|
checkRedefinition( art );
|
|
494
506
|
const block = art._block;
|
|
495
|
-
|
|
507
|
+
initAnnotations( art, block );
|
|
496
508
|
initMembers( art, art, block );
|
|
497
509
|
}
|
|
498
510
|
|
|
@@ -518,6 +530,57 @@ function define( model ) {
|
|
|
518
530
|
parent._subArtifacts[absolute.substring( dot + 1 )] = art; // not dictAdd()
|
|
519
531
|
}
|
|
520
532
|
|
|
533
|
+
/** Initialize the extension `ext`.
|
|
534
|
+
*
|
|
535
|
+
* Currently:
|
|
536
|
+
*
|
|
537
|
+
* - initialize annotations (set _block, $priority, `...` check) on "main"
|
|
538
|
+
* extension and its columns do more later
|
|
539
|
+
* - for members in compile(): init annotations via extendMembers/annotateMembers
|
|
540
|
+
* - for members in parse.cdl(): init annotation via initMembers
|
|
541
|
+
*
|
|
542
|
+
* In the future:
|
|
543
|
+
*
|
|
544
|
+
* - also initialize members and member extensions/annotations here
|
|
545
|
+
* - we might also do other things, like calculating whether an `extend` is
|
|
546
|
+
* `annotate`-like, i.e. only contains name-resolution irrelevant extensions.
|
|
547
|
+
*/
|
|
548
|
+
function initExtension( ext ) {
|
|
549
|
+
const block = ext._block;
|
|
550
|
+
initAnnotations( ext, block, ext.kind );
|
|
551
|
+
if (ext.columns) // the columns themselves are "definitions"
|
|
552
|
+
ext.columns.forEach( col => initAnnotations( col, block ) );
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Set _block links for annotations (necessary for layering) and do a late
|
|
556
|
+
// syntax check (`...` only with extensions, not definitions).
|
|
557
|
+
// extKind is either ext.kind (=art is extension) or false (=art is not an extension)
|
|
558
|
+
function initAnnotations( art, block, extKind = false ) {
|
|
559
|
+
// TODO: think of removing $priority, then
|
|
560
|
+
// no _block: define, _block: annotate/extend/edmx
|
|
561
|
+
// would fit with extending defs with props like length
|
|
562
|
+
for (const prop in art) {
|
|
563
|
+
if (prop.charAt(0) === '@') {
|
|
564
|
+
const anno = art[prop];
|
|
565
|
+
// TODO: make anno never be an array, see addAnnotation() in genericAntlrParser
|
|
566
|
+
if (Array.isArray( anno ))
|
|
567
|
+
anno.forEach( init );
|
|
568
|
+
else
|
|
569
|
+
init( anno );
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return;
|
|
573
|
+
|
|
574
|
+
function init( anno ) {
|
|
575
|
+
setLink( anno, '_block', block );
|
|
576
|
+
anno.$priority = extKind;
|
|
577
|
+
if (!extKind && annotationHasEllipsis( anno )) {
|
|
578
|
+
error( 'anno-unexpected-ellipsis',
|
|
579
|
+
[ anno.name.location, art ], { code: '...' } );
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
521
584
|
// From here til EOF, reexamine code ---------------------------------------
|
|
522
585
|
// See populate:
|
|
523
586
|
// - userQuery() or _query property?
|
|
@@ -543,7 +606,8 @@ function define( model ) {
|
|
|
543
606
|
setLink( art, '_$next', model.$magicVariables );
|
|
544
607
|
}
|
|
545
608
|
|
|
546
|
-
function
|
|
609
|
+
function initDollarParameters( art ) {
|
|
610
|
+
// TODO: remove $parameters in v4?
|
|
547
611
|
// TODO: use setMemberParent() ?
|
|
548
612
|
const parameters = {
|
|
549
613
|
name: { id: '$parameters', param: '$parameters', absolute: art.name.absolute },
|
|
@@ -606,7 +670,7 @@ function define( model ) {
|
|
|
606
670
|
// Either expression (value), expand or new association (target && type)
|
|
607
671
|
else if (col.value || col.expand || (col.target && col.type)) {
|
|
608
672
|
setLink( col, '_block', parent._block );
|
|
609
|
-
|
|
673
|
+
initAnnotations( col, parent._block );
|
|
610
674
|
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
611
675
|
if (col.doc)
|
|
612
676
|
warning( 'syntax-ignoring-anno', [ col.doc.location, col ], { '#': 'doc' } );
|
|
@@ -1025,7 +1089,7 @@ function define( model ) {
|
|
|
1025
1089
|
checkRedefinition( elem );
|
|
1026
1090
|
if (elem.kind === 'annotate' || elem.kind === 'extend')
|
|
1027
1091
|
checkAnnotate( elem, elem );
|
|
1028
|
-
|
|
1092
|
+
initAnnotations( elem, bl );
|
|
1029
1093
|
initMembers( elem, elem, bl, initExtensions );
|
|
1030
1094
|
|
|
1031
1095
|
// for a correct home path, setMemberParent needed to be called
|