@sap/cds-compiler 3.5.2 → 3.6.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 +63 -1
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/model.js +33 -22
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +29 -20
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +39 -42
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +3 -3
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +23 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +29 -20
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +3 -16
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +8 -8
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +2 -1
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +53 -39
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
package/lib/compiler/define.js
CHANGED
|
@@ -119,7 +119,8 @@
|
|
|
119
119
|
|
|
120
120
|
'use strict';
|
|
121
121
|
|
|
122
|
-
const { forEachGeneric, forEachInOrder } = require('../base/model');
|
|
122
|
+
const { forEachGeneric, forEachInOrder, isBetaEnabled } = require('../base/model');
|
|
123
|
+
const shuffleGen = require('../base/shuffle');
|
|
123
124
|
const {
|
|
124
125
|
dictAdd, dictAddArray, dictForEach, pushToDict,
|
|
125
126
|
} = require('../base/dictionaries');
|
|
@@ -161,9 +162,12 @@ function define( model ) {
|
|
|
161
162
|
resolveUncheckedPath,
|
|
162
163
|
checkAnnotate,
|
|
163
164
|
} = model.$functions;
|
|
165
|
+
const { shuffleDict, shuffleArray } = shuffleGen( options.testMode );
|
|
164
166
|
|
|
165
167
|
const extensionsDict = Object.create(null);
|
|
166
168
|
Object.assign( model.$functions, {
|
|
169
|
+
shuffleDict,
|
|
170
|
+
shuffleArray,
|
|
167
171
|
initArtifact,
|
|
168
172
|
initMembers,
|
|
169
173
|
extensionsDict, // a dictionary - TODO: put directly into model?
|
|
@@ -176,6 +180,8 @@ function define( model ) {
|
|
|
176
180
|
return art._subArtifacts || Object.create(null);
|
|
177
181
|
};
|
|
178
182
|
|
|
183
|
+
let boundSelfParamType = true; // special `$self` for binding param must still be initialised
|
|
184
|
+
|
|
179
185
|
return doDefine();
|
|
180
186
|
|
|
181
187
|
/**
|
|
@@ -199,15 +205,16 @@ function define( model ) {
|
|
|
199
205
|
model.$lateExtensions = Object.create(null); // for generated artifacts
|
|
200
206
|
|
|
201
207
|
initBuiltins( model );
|
|
202
|
-
|
|
208
|
+
const sourceNames = shuffleArray( Object.keys( model.sources ) );
|
|
209
|
+
for (const name of sourceNames)
|
|
203
210
|
addSource( model.sources[name] );
|
|
204
|
-
for (const name
|
|
211
|
+
for (const name of sourceNames)
|
|
205
212
|
initNamespaceAndUsing( model.sources[name] );
|
|
206
213
|
dictForEach( model.definitions, initArtifact );
|
|
207
214
|
dictForEach( model.vocabularies, initVocabulary );
|
|
208
215
|
dictForEach( extensionsDict, initExtension );
|
|
209
216
|
|
|
210
|
-
|
|
217
|
+
addI18nBlocks();
|
|
211
218
|
}
|
|
212
219
|
|
|
213
220
|
// Phase 1: ----------------------------------------------------------------
|
|
@@ -232,25 +239,31 @@ function define( model ) {
|
|
|
232
239
|
}
|
|
233
240
|
if (src.$frontend !== 'json') { // CDL input
|
|
234
241
|
// TODO: set _block to builtin
|
|
235
|
-
if (src.artifacts)
|
|
242
|
+
if (src.artifacts) {
|
|
243
|
+
// addArtifact() adds usings to src.artifacts: shuffleDict must be assigned first
|
|
244
|
+
src.artifacts = shuffleDict( src.artifacts );
|
|
236
245
|
addPathPrefixes( src.artifacts, prefix ); // before addUsing
|
|
237
|
-
|
|
246
|
+
}
|
|
247
|
+
else if (src.usings || src.namespace) {
|
|
238
248
|
src.artifacts = Object.create(null);
|
|
249
|
+
}
|
|
239
250
|
if (src.usings)
|
|
240
|
-
src.usings.forEach( u => addUsing( u, src ) );
|
|
251
|
+
shuffleArray( src.usings ).forEach( u => addUsing( u, src ) );
|
|
241
252
|
if (namespace)
|
|
242
253
|
addNamespace( namespace, src );
|
|
243
|
-
if (src.artifacts)
|
|
254
|
+
if (src.artifacts) { // addArtifact needs usings for context extensions
|
|
255
|
+
src.artifacts = shuffleDict( src.artifacts );
|
|
244
256
|
dictForEach( src.artifacts, a => addArtifact( a, src, prefix ) );
|
|
257
|
+
}
|
|
245
258
|
}
|
|
246
259
|
else if (src.definitions) { // CSN input
|
|
247
260
|
prefix = '';
|
|
248
|
-
dictForEach( src.definitions, v => addDefinition( v, src ) );
|
|
261
|
+
dictForEach( shuffleDict( src.definitions ), v => addDefinition( v, src ) );
|
|
249
262
|
}
|
|
250
263
|
if (src.vocabularies) {
|
|
251
264
|
if (!model.vocabularies)
|
|
252
265
|
model.vocabularies = Object.create(null);
|
|
253
|
-
dictForEach( src.vocabularies, v => addVocabulary( v, src, prefix ) );
|
|
266
|
+
dictForEach( shuffleDict( src.vocabularies ), v => addVocabulary( v, src, prefix ) );
|
|
254
267
|
}
|
|
255
268
|
if (src.extensions) { // requires using to be known!
|
|
256
269
|
src.extensions.forEach( e => addExtension( e, src ) );
|
|
@@ -341,6 +354,7 @@ function define( model ) {
|
|
|
341
354
|
dictAddArray( src.artifacts, name, decl );
|
|
342
355
|
}
|
|
343
356
|
|
|
357
|
+
// must be called after addUsing().
|
|
344
358
|
function addNamespace( path, src ) {
|
|
345
359
|
const absolute = pathName( path );
|
|
346
360
|
if (path.broken) // parsing may have failed
|
|
@@ -405,10 +419,56 @@ function define( model ) {
|
|
|
405
419
|
dictAdd( model.vocabularies, name.absolute, vocab );
|
|
406
420
|
}
|
|
407
421
|
|
|
408
|
-
|
|
422
|
+
/**
|
|
423
|
+
* Add (optional) translations into the XSN model.
|
|
424
|
+
*/
|
|
425
|
+
function addI18nBlocks() {
|
|
426
|
+
// TODO: the sequence should be in sync with extend / annotate / future $sources
|
|
427
|
+
const sortedSources = Object.keys(model.sources)
|
|
428
|
+
.filter(name => !!model.sources[name].i18n)
|
|
429
|
+
.sort( (a, b) => compareLayer( model.sources[a], model.sources[b] ) );
|
|
430
|
+
|
|
431
|
+
if (sortedSources.length === 0)
|
|
432
|
+
return;
|
|
433
|
+
|
|
434
|
+
if (!model.i18n)
|
|
435
|
+
model.i18n = Object.create( null );
|
|
436
|
+
|
|
437
|
+
for (const name of sortedSources)
|
|
438
|
+
addI18nFromSource( model.sources[name] );
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Add the source's translations to the model. Warns if the source's translations
|
|
443
|
+
* do not match the ones from previous sources.
|
|
444
|
+
*
|
|
445
|
+
* @param {XSN.SourceAst} src
|
|
446
|
+
*/
|
|
447
|
+
function addI18nFromSource( src ) {
|
|
448
|
+
for (const langKey of Object.keys( src.i18n )) {
|
|
449
|
+
if (!model.i18n[langKey])
|
|
450
|
+
model.i18n[langKey] = Object.create( null );
|
|
451
|
+
|
|
452
|
+
for (const textKey of Object.keys( src.i18n[langKey] )) {
|
|
453
|
+
const sourceVal = src.i18n[langKey][textKey];
|
|
454
|
+
const modelVal = model.i18n[langKey][textKey];
|
|
455
|
+
if (!modelVal) {
|
|
456
|
+
model.i18n[langKey][textKey] = sourceVal;
|
|
457
|
+
}
|
|
458
|
+
else if (modelVal.val !== sourceVal.val) {
|
|
459
|
+
// TODO: behave like annotation assignments? message-id?
|
|
460
|
+
warning( 'i18n-different-value', sourceVal.location,
|
|
461
|
+
{ prop: textKey, otherprop: langKey } );
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Phase 2 ("init"), top-level & main -----------------------------------------
|
|
409
468
|
// Functions called from top-level: initNamespaceAndUsing(), initArtifact(),
|
|
410
469
|
// initVocabulary(), initExtension()
|
|
411
470
|
|
|
471
|
+
// TODO: message ids
|
|
412
472
|
function checkRedefinition( art ) {
|
|
413
473
|
if (!art.$duplicates || art.$errorReported === 'syntax-duplicate-extend' ||
|
|
414
474
|
art.$errorReported === 'syntax-duplicate-annotate')
|
|
@@ -443,7 +503,7 @@ function define( model ) {
|
|
|
443
503
|
// TODO: make it possible to have no location
|
|
444
504
|
const ns = { kind: 'namespace', name: { absolute, location }, location };
|
|
445
505
|
model.definitions[absolute] = ns;
|
|
446
|
-
|
|
506
|
+
initArtifactParentLink( ns, model.definitions );
|
|
447
507
|
}
|
|
448
508
|
const builtin = model.$builtins[id];
|
|
449
509
|
if (builtin && !builtin.internal &&
|
|
@@ -471,7 +531,7 @@ function define( model ) {
|
|
|
471
531
|
|
|
472
532
|
function initArtifact( art, reInit = false ) {
|
|
473
533
|
if (!reInit)
|
|
474
|
-
|
|
534
|
+
initArtifactParentLink( art, model.definitions );
|
|
475
535
|
const block = art._block;
|
|
476
536
|
checkRedefinition( art );
|
|
477
537
|
initAnnotations( art, block );
|
|
@@ -489,27 +549,22 @@ function define( model ) {
|
|
|
489
549
|
if (!setLink( art, '_leadingQuery', initQueryExpression( art.query, art ) ) )
|
|
490
550
|
return; // null or undefined in case of parse error
|
|
491
551
|
setLink( art._leadingQuery, '_$next', art );
|
|
492
|
-
// the following may be removed soon if we have:
|
|
493
|
-
// view elements as proxies to elements of leading query
|
|
494
552
|
if (art.elements) { // specified element via compilation of client-style CSN
|
|
553
|
+
// TODO: consider this part of a revamped on-demand 'extend' functionality
|
|
495
554
|
setLink( art, 'elements$', art.elements );
|
|
496
555
|
delete art.elements;
|
|
497
556
|
}
|
|
498
|
-
if (art.enum) { // specified enum via compilation of client-style CSN
|
|
499
|
-
setLink( art, 'enum$', art.enum );
|
|
500
|
-
delete art.enum;
|
|
501
|
-
}
|
|
502
557
|
}
|
|
503
558
|
|
|
504
559
|
function initVocabulary( art ) {
|
|
505
|
-
|
|
560
|
+
initArtifactParentLink( art, model.vocabularies );
|
|
506
561
|
checkRedefinition( art );
|
|
507
562
|
const block = art._block;
|
|
508
563
|
initAnnotations( art, block );
|
|
509
564
|
initMembers( art, art, block );
|
|
510
565
|
}
|
|
511
566
|
|
|
512
|
-
function
|
|
567
|
+
function initArtifactParentLink( art, definitions ) {
|
|
513
568
|
setLink( art, '_parent', null );
|
|
514
569
|
const { absolute } = art.name;
|
|
515
570
|
const dot = absolute.lastIndexOf('.');
|
|
@@ -522,7 +577,7 @@ function define( model ) {
|
|
|
522
577
|
const { location } = art.name; // TODO: make it possible to have no location
|
|
523
578
|
parent = { kind: 'namespace', name: { absolute: prefix, location }, location };
|
|
524
579
|
definitions[prefix] = parent;
|
|
525
|
-
|
|
580
|
+
initArtifactParentLink( parent, definitions );
|
|
526
581
|
}
|
|
527
582
|
setLink( art, '_parent', parent );
|
|
528
583
|
if (!parent._subArtifacts)
|
|
@@ -540,7 +595,7 @@ function define( model ) {
|
|
|
540
595
|
* - for members in compile(): init annotations via extendMembers/annotateMembers
|
|
541
596
|
* - for members in parse.cdl(): init annotation via initMembers
|
|
542
597
|
*
|
|
543
|
-
* In the future:
|
|
598
|
+
* In the future (after name cleanup):
|
|
544
599
|
*
|
|
545
600
|
* - also initialize members and member extensions/annotations here
|
|
546
601
|
* - we might also do other things, like calculating whether an `extend` is
|
|
@@ -561,7 +616,7 @@ function define( model ) {
|
|
|
561
616
|
// no _block: define, _block: annotate/extend/edmx
|
|
562
617
|
// would fit with extending defs with props like length
|
|
563
618
|
for (const prop in art) {
|
|
564
|
-
if (prop.charAt(0) === '@') {
|
|
619
|
+
if (prop.charAt(0) === '@' || prop === 'doc') {
|
|
565
620
|
const anno = art[prop];
|
|
566
621
|
// TODO: make anno never be an array, see addAnnotation() in genericAntlrParser
|
|
567
622
|
if (Array.isArray( anno ))
|
|
@@ -582,11 +637,7 @@ function define( model ) {
|
|
|
582
637
|
}
|
|
583
638
|
}
|
|
584
639
|
|
|
585
|
-
//
|
|
586
|
-
// See populate:
|
|
587
|
-
// - userQuery() or _query property?
|
|
588
|
-
// - initFromColumns()
|
|
589
|
-
// - ensureColumnName()
|
|
640
|
+
// Init special things: -------------------------------------------------------
|
|
590
641
|
|
|
591
642
|
function initDollarSelf( art ) {
|
|
592
643
|
const selfname = '$self';
|
|
@@ -628,132 +679,76 @@ function define( model ) {
|
|
|
628
679
|
art.$tableAliases.$parameters = parameters;
|
|
629
680
|
}
|
|
630
681
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
if (query.where)
|
|
637
|
-
initExprForQuery( query.where, query );
|
|
638
|
-
if (query.having)
|
|
639
|
-
initExprForQuery( query.having, query );
|
|
640
|
-
initMembers( query, query, query._block );
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
function initSelectItems( parent, columns, user ) {
|
|
644
|
-
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
645
|
-
let wildcard = null;
|
|
646
|
-
let hasItems = false;
|
|
647
|
-
for (const col of columns || parent.expand || parent.inline || []) {
|
|
648
|
-
if (!col) // parse error
|
|
649
|
-
continue;
|
|
650
|
-
hasItems = true;
|
|
651
|
-
if (!columns) {
|
|
652
|
-
if (parent.value)
|
|
653
|
-
setLink( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
654
|
-
else if (parent._pathHead)
|
|
655
|
-
setLink( col, '_pathHead', parent._pathHead );
|
|
656
|
-
}
|
|
657
|
-
if (col.val === '*') {
|
|
658
|
-
if (!wildcard) {
|
|
659
|
-
wildcard = col;
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
663
|
-
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
664
|
-
error( 'syntax-duplicate-clause', [ col.location, null ],
|
|
665
|
-
{ prop: '*', line: wildcard.location.line, col: wildcard.location.col },
|
|
666
|
-
'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
|
|
667
|
-
// TODO: extra text variants for expand/inline? - probably not
|
|
668
|
-
col.val = null; // do not consider it for expandWildcard()
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
// Either expression (value), expand or new association (target && type)
|
|
672
|
-
else if (col.value || col.expand || (col.target && col.type)) {
|
|
673
|
-
setLink( col, '_block', parent._block );
|
|
674
|
-
initAnnotations( col, parent._block );
|
|
675
|
-
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
676
|
-
if (col.doc) {
|
|
677
|
-
warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
|
|
678
|
-
{ '#': 'doc', code: '.{ ‹inline› }' } );
|
|
679
|
-
}
|
|
680
|
-
// col.$annotations no available for CSN input, have to search.
|
|
681
|
-
// Warning about first annotation should be enough to avoid spam.
|
|
682
|
-
const firstAnno = Object.keys(col).find(key => key.startsWith('@'));
|
|
683
|
-
if (firstAnno) {
|
|
684
|
-
warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
|
|
685
|
-
{ code: '.{ ‹inline› }' } );
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
689
|
-
if (columns)
|
|
690
|
-
initExprForQuery( col.value, parent );
|
|
691
|
-
initSelectItems( col, null, user );
|
|
692
|
-
}
|
|
693
|
-
}
|
|
682
|
+
// From here til EOF, reexamine code ---------------------------------------
|
|
683
|
+
// See populate:
|
|
684
|
+
// - userQuery() or _query property?
|
|
685
|
+
// - initFromColumns()
|
|
686
|
+
// - ensureColumnName()
|
|
694
687
|
|
|
695
|
-
|
|
696
|
-
// TODO: Better way to get source file?
|
|
697
|
-
let block = parent;
|
|
698
|
-
while (block._block)
|
|
699
|
-
block = block._block;
|
|
688
|
+
// Init queries: --------------------------------------------------------------
|
|
700
689
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
690
|
+
// art is:
|
|
691
|
+
// - entity for top-level queries (including UNION args)
|
|
692
|
+
// - $tableAlias for sub query in FROM - TODO: what about UNION there?
|
|
693
|
+
// - $query for real sub query (in columns, WHERE, ...), again: what about UNION there?
|
|
694
|
+
function initQueryExpression( query, art ) {
|
|
695
|
+
if (!query) // parse error
|
|
696
|
+
return query;
|
|
697
|
+
if (query.from) { // select
|
|
698
|
+
initQuery();
|
|
699
|
+
initTableExpression( query.from, query, [] );
|
|
700
|
+
if (query.mixin)
|
|
701
|
+
initMixins( query, art );
|
|
702
|
+
if (!query.$tableAliases.$self) { // same as $projection
|
|
703
|
+
const self = {
|
|
704
|
+
name: { alias: '$self', query: query.name.select, absolute: art.name.absolute },
|
|
705
|
+
kind: '$self',
|
|
706
|
+
location: query.location,
|
|
707
|
+
};
|
|
708
|
+
setLink( self, '_origin', query );
|
|
709
|
+
setLink( self, '_parent', query );
|
|
710
|
+
setLink( self, '_main', query._main );
|
|
711
|
+
setLink( self, '_effectiveType', query ); // TODO: remove
|
|
712
|
+
query.$tableAliases.$self = self;
|
|
713
|
+
query.$tableAliases.$projection = self;
|
|
705
714
|
}
|
|
715
|
+
initSubQuery( query ); // check for SELECT clauses after from / mixin
|
|
706
716
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
else if (expr.args) {
|
|
720
|
-
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
721
|
-
args.forEach( e => initExprForQuery( e, query ) );
|
|
717
|
+
else if (query.args) { // UNION, INTERSECT, ..., query in parens
|
|
718
|
+
const leading = initQueryExpression( query.args[0], art );
|
|
719
|
+
for (const q of query.args.slice(1))
|
|
720
|
+
initQueryExpression( q, art );
|
|
721
|
+
setLink( query, '_leadingQuery', leading );
|
|
722
|
+
if (leading && query.orderBy) {
|
|
723
|
+
if (leading.$orderBy)
|
|
724
|
+
leading.$orderBy.push( ...query.orderBy );
|
|
725
|
+
else
|
|
726
|
+
leading.$orderBy = [ ...query.orderBy ];
|
|
727
|
+
}
|
|
728
|
+
// ORDER BY to be evaluated in leading query (LIMIT is literal)
|
|
722
729
|
}
|
|
723
|
-
else
|
|
724
|
-
|
|
725
|
-
approveExistsInChildren(expr);
|
|
730
|
+
else { // with parse error (`select from <EOF>`, `select from E { *, ( select }`)
|
|
731
|
+
return undefined;
|
|
726
732
|
}
|
|
727
|
-
|
|
733
|
+
return query._leadingQuery || query;
|
|
728
734
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
if (!exprOrPathElement) // may be null in case of parse error
|
|
747
|
-
return;
|
|
748
|
-
if (exprOrPathElement.$expected === 'exists')
|
|
749
|
-
exprOrPathElement.$expected = 'approved-exists';
|
|
750
|
-
// Drill down
|
|
751
|
-
if (exprOrPathElement.args)
|
|
752
|
-
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
753
|
-
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
754
|
-
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
755
|
-
else if (exprOrPathElement.path)
|
|
756
|
-
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
735
|
+
function initQuery() {
|
|
736
|
+
const main = art._main || art;
|
|
737
|
+
setLink( query, '_$next',
|
|
738
|
+
// if art is $tableAlias, set to embedding query
|
|
739
|
+
(!art._main || art.kind === 'select' || art.kind === '$join')
|
|
740
|
+
? art : art._parent ); // TODO: check with name resolution change
|
|
741
|
+
setLink( query, '_block', art._block );
|
|
742
|
+
query.kind = 'select';
|
|
743
|
+
query.name = { location: query.location };
|
|
744
|
+
setMemberParent( query, main.$queries.length + 1, main );
|
|
745
|
+
// console.log(art.kind,art.name,query.name,query._$next.name)
|
|
746
|
+
// if (query.name.query === 1 && query.name.absolute === 'S') throw new CompilerAssertion();
|
|
747
|
+
main.$queries.push( query );
|
|
748
|
+
setLink( query, '_parent', art ); // _parent should point to alias/main/query
|
|
749
|
+
query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
|
|
750
|
+
dependsOnSilent( main, query );
|
|
751
|
+
}
|
|
757
752
|
}
|
|
758
753
|
|
|
759
754
|
// table is table expression in FROM, becomes an alias
|
|
@@ -766,8 +761,8 @@ function define( model ) {
|
|
|
766
761
|
return;
|
|
767
762
|
if (!table.name) {
|
|
768
763
|
const last = table.path[table.path.length - 1];
|
|
769
|
-
const dot = last
|
|
770
|
-
const id = (dot
|
|
764
|
+
const dot = last?.id?.lastIndexOf('.');
|
|
765
|
+
const id = (dot >= 0) ? last.id.substring( dot + 1 ) : last.id || '';
|
|
771
766
|
// TODO: if we have too much time, we can calculate the real location with '.'
|
|
772
767
|
table.name = { $inferred: 'as', id, location: last.location };
|
|
773
768
|
}
|
|
@@ -847,117 +842,180 @@ function define( model ) {
|
|
|
847
842
|
}
|
|
848
843
|
}
|
|
849
844
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if (
|
|
856
|
-
|
|
857
|
-
if (query.
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
kind: '$self',
|
|
866
|
-
location: query.location,
|
|
867
|
-
};
|
|
868
|
-
setLink( self, '_origin', query );
|
|
869
|
-
setLink( self, '_parent', query );
|
|
870
|
-
setLink( self, '_main', query._main );
|
|
871
|
-
setLink( self, '_effectiveType', query ); // TODO: remove
|
|
872
|
-
query.$tableAliases.$self = self;
|
|
873
|
-
query.$tableAliases.$projection = self;
|
|
874
|
-
}
|
|
875
|
-
initSubQuery( query ); // check for SELECT clauses after from / mixin
|
|
845
|
+
function initSubQuery( query ) {
|
|
846
|
+
if (query.on)
|
|
847
|
+
initExprForQuery( query.on, query );
|
|
848
|
+
// TODO: MIXIN with name = ...subquery (not yet supported anyway)
|
|
849
|
+
initSelectItems( query, query.columns, query );
|
|
850
|
+
if (query.where)
|
|
851
|
+
initExprForQuery( query.where, query );
|
|
852
|
+
if (query.having)
|
|
853
|
+
initExprForQuery( query.having, query );
|
|
854
|
+
initMembers( query, query, query._block );
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function initExprForQuery( expr, query ) {
|
|
858
|
+
if (Array.isArray(expr)) { // TODO: old-style $parens ?
|
|
859
|
+
expr.forEach( e => initExprForQuery( e, query ) );
|
|
876
860
|
}
|
|
877
|
-
else if (
|
|
878
|
-
|
|
879
|
-
for (const q of query.args.slice(1))
|
|
880
|
-
initQueryExpression( q, art );
|
|
881
|
-
setLink( query, '_leadingQuery', leading );
|
|
882
|
-
if (leading && query.orderBy) {
|
|
883
|
-
if (leading.$orderBy)
|
|
884
|
-
leading.$orderBy.push( ...query.orderBy );
|
|
885
|
-
else
|
|
886
|
-
leading.$orderBy = [ ...query.orderBy ];
|
|
887
|
-
}
|
|
888
|
-
// ORDER BY to be evaluated in leading query (LIMIT is literal)
|
|
861
|
+
else if (!expr) {
|
|
862
|
+
return;
|
|
889
863
|
}
|
|
890
|
-
else
|
|
891
|
-
|
|
864
|
+
else if (expr.query) {
|
|
865
|
+
initQueryExpression( expr.query, query );
|
|
892
866
|
}
|
|
893
|
-
|
|
867
|
+
else if (expr.args) {
|
|
868
|
+
const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
|
|
869
|
+
args.forEach( e => initExprForQuery( e, query ) );
|
|
870
|
+
}
|
|
871
|
+
else if (expr.path && expr.$expected === 'exists') {
|
|
872
|
+
// TODO: does really the parser has to set $expected?
|
|
873
|
+
expr.$expected = 'approved-exists';
|
|
874
|
+
approveExistsInChildren(expr);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
894
877
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
878
|
+
function initMixins( query, art ) {
|
|
879
|
+
// TODO: re-check if mixins have already duplicates
|
|
880
|
+
for (const name in query.mixin) {
|
|
881
|
+
const mixin = query.mixin[name];
|
|
882
|
+
if (!(mixin.$duplicates)) {
|
|
883
|
+
setMemberParent( mixin, name, query );
|
|
884
|
+
mixin.name.alias = mixin.name.id;
|
|
885
|
+
setLink( mixin, '_block', art._block );
|
|
886
|
+
// TODO: do some initMembers() ? If people had annotation
|
|
887
|
+
// assignments on the mixin... (also for future mixin definitions
|
|
888
|
+
// with generated values)
|
|
889
|
+
dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
|
|
890
|
+
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
|
|
891
|
+
} );
|
|
892
|
+
if (mixin.name.id[0] === '$') {
|
|
893
|
+
warning( 'name-invalid-dollar-alias', [ mixin.name.location, mixin ],
|
|
894
|
+
{ '#': 'mixin', name: '$' } );
|
|
895
|
+
}
|
|
896
|
+
}
|
|
911
897
|
}
|
|
898
|
+
}
|
|
912
899
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
//
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
900
|
+
function initSelectItems( parent, columns, user ) {
|
|
901
|
+
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
902
|
+
let wildcard = null;
|
|
903
|
+
let hasItems = false;
|
|
904
|
+
for (const col of columns || parent.expand || parent.inline || []) {
|
|
905
|
+
if (!col) // parse error
|
|
906
|
+
continue;
|
|
907
|
+
hasItems = true;
|
|
908
|
+
if (!columns) {
|
|
909
|
+
if (parent.value)
|
|
910
|
+
setLink( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
911
|
+
else if (parent._pathHead)
|
|
912
|
+
setLink( col, '_pathHead', parent._pathHead );
|
|
913
|
+
}
|
|
914
|
+
if (col.val === '*') {
|
|
915
|
+
if (!wildcard) {
|
|
916
|
+
wildcard = col;
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
920
|
+
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
921
|
+
error( 'syntax-duplicate-wildcard', [ col.location, null ],
|
|
922
|
+
{
|
|
923
|
+
'#': (wildcard.location.col ? 'col' : 'std'),
|
|
924
|
+
prop: '*',
|
|
925
|
+
line: wildcard.location.line,
|
|
926
|
+
col: wildcard.location.col,
|
|
927
|
+
}, {
|
|
928
|
+
std: 'You have provided a $(PROP) already in line $(LINE)',
|
|
929
|
+
col: 'You have provided a $(PROP) already at line $(LINE), column $(COL)',
|
|
930
|
+
} );
|
|
931
|
+
// TODO: extra text variants for expand/inline? - probably not
|
|
932
|
+
col.val = null; // do not consider it for expandWildcard()
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
// Either expression (value), expand or new association (target && type)
|
|
936
|
+
else if (col.value || col.expand || (col.target && col.type)) {
|
|
937
|
+
setLink( col, '_block', parent._block );
|
|
938
|
+
initAnnotations( col, parent._block );
|
|
939
|
+
if (col.inline) { // `@anno elem.{ * }` does not work
|
|
940
|
+
if (col.doc) {
|
|
941
|
+
warning( 'syntax-ignoring-anno', [ col.doc.location, col ],
|
|
942
|
+
{ '#': 'doc', code: '.{ ‹inline› }' } );
|
|
943
|
+
}
|
|
944
|
+
// col.$annotations no available for CSN input, have to search.
|
|
945
|
+
// Warning about first annotation should be enough to avoid spam.
|
|
946
|
+
const firstAnno = Object.keys(col).find(key => key.startsWith('@'));
|
|
947
|
+
if (firstAnno) {
|
|
948
|
+
warning( 'syntax-ignoring-anno', [ col[firstAnno].name.location, col ],
|
|
949
|
+
{ code: '.{ ‹inline› }' } );
|
|
930
950
|
}
|
|
931
951
|
}
|
|
952
|
+
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
953
|
+
if (columns)
|
|
954
|
+
initExprForQuery( col.value, parent );
|
|
955
|
+
initSelectItems( col, null, user );
|
|
932
956
|
}
|
|
933
957
|
}
|
|
934
|
-
}
|
|
935
958
|
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
959
|
+
if (hasItems && !wildcard && parent.excludingDict) {
|
|
960
|
+
// TODO: Better way to get source file?
|
|
961
|
+
let block = parent;
|
|
962
|
+
while (block._block)
|
|
963
|
+
block = block._block;
|
|
964
|
+
|
|
965
|
+
if (block.$frontend === 'cdl') {
|
|
966
|
+
warning('query-ignoring-exclude', [ parent.excludingDict[$location], user ],
|
|
967
|
+
{ prop: '*' },
|
|
968
|
+
'Excluding elements without wildcard $(PROP) has no effect');
|
|
969
|
+
}
|
|
943
970
|
}
|
|
944
|
-
if (elem.targetAspect || options.parseCdl || !isDirectComposition( elem ))
|
|
945
|
-
return false;
|
|
946
|
-
const name = resolveUncheckedPath( target, 'compositionTarget', elem );
|
|
947
|
-
const aspect = name && model.definitions[name];
|
|
948
|
-
return aspect && (aspect.kind === 'aspect' || aspect.kind === 'type'); // type is sloppy
|
|
949
971
|
}
|
|
950
972
|
|
|
973
|
+
/**
|
|
974
|
+
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
975
|
+
* since we will have a top-level subquery after exists-processing in the forRelationalDB.
|
|
976
|
+
*
|
|
977
|
+
* Recursively drill down into:
|
|
978
|
+
* - the .path
|
|
979
|
+
* - the .args
|
|
980
|
+
* - the .where.args
|
|
981
|
+
*
|
|
982
|
+
* Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
|
|
983
|
+
*
|
|
984
|
+
* working: exists toE[exists toE] -> select from E where exists toE
|
|
985
|
+
* not working: toE[exists toE] -> we don't support subqueries in filters
|
|
986
|
+
*
|
|
987
|
+
* @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
|
|
988
|
+
*/
|
|
989
|
+
function approveExistsInChildren( exprOrPathElement ) {
|
|
990
|
+
if (!exprOrPathElement) // may be null in case of parse error
|
|
991
|
+
return;
|
|
992
|
+
if (exprOrPathElement.$expected === 'exists')
|
|
993
|
+
exprOrPathElement.$expected = 'approved-exists';
|
|
994
|
+
// Drill down
|
|
995
|
+
if (exprOrPathElement.args)
|
|
996
|
+
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
997
|
+
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
998
|
+
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
999
|
+
else if (exprOrPathElement.path)
|
|
1000
|
+
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
1001
|
+
}
|
|
1002
|
+
// TODO: we might issue 'expr-unexpected-exists' and 'expr-no-subquery' already in
|
|
1003
|
+
// define.js (using a to-be-written expression traversal function in utils.js)
|
|
1004
|
+
|
|
1005
|
+
// Members (elements, enum, actions, params): ---------------------------------
|
|
1006
|
+
|
|
951
1007
|
/**
|
|
952
1008
|
* Set property `_parent` for all elements in `parent` to `parent` and do so
|
|
953
1009
|
* recursively for all sub elements.
|
|
954
1010
|
*
|
|
955
1011
|
* If not for extensions: construct === parent
|
|
1012
|
+
*
|
|
1013
|
+
* TODO: separate extension!
|
|
956
1014
|
*/
|
|
957
1015
|
function initMembers( construct, parent, block, initExtensions = false ) {
|
|
958
1016
|
// TODO: split extend from init
|
|
959
|
-
const
|
|
960
|
-
|
|
1017
|
+
const main = parent._main || parent;
|
|
1018
|
+
const isQueryExtension = kindProperties[construct.kind].isExtension && main.query;
|
|
961
1019
|
let obj = construct;
|
|
962
1020
|
let { items } = obj;
|
|
963
1021
|
while (items) {
|
|
@@ -986,6 +1044,7 @@ function define( model ) {
|
|
|
986
1044
|
delete obj.on; // continuation semantics: not specified
|
|
987
1045
|
}
|
|
988
1046
|
if (targetAspect.elements) {
|
|
1047
|
+
// TODO: main?
|
|
989
1048
|
const inEntity = parent._main && parent._main.kind === 'entity';
|
|
990
1049
|
// TODO: also allow indirectly (component in component in entity)?
|
|
991
1050
|
setLink( targetAspect, '_outer', obj );
|
|
@@ -1025,16 +1084,8 @@ function define( model ) {
|
|
|
1025
1084
|
obj = targetAspect;
|
|
1026
1085
|
}
|
|
1027
1086
|
}
|
|
1028
|
-
if (obj !== parent && obj.elements && parent.enum) {
|
|
1029
|
-
|
|
1030
|
-
for (const n in obj.elements) {
|
|
1031
|
-
const e = obj.elements[n];
|
|
1032
|
-
if (e.kind === 'element')
|
|
1033
|
-
e.kind = 'enum';
|
|
1034
|
-
}
|
|
1035
|
-
// obj = Object.assign( { enum: obj.elements}, obj );
|
|
1036
|
-
// delete obj.elements; // No extra syntax for EXTEND enum
|
|
1037
|
-
forEachGeneric( { enum: obj.elements }, 'enum', init );
|
|
1087
|
+
if (obj !== parent && obj.elements && parent.enum) { // applying the extension
|
|
1088
|
+
initElementsAsEnum();
|
|
1038
1089
|
}
|
|
1039
1090
|
else {
|
|
1040
1091
|
if (checkDefinitions( construct, parent, 'elements', obj.elements || false ))
|
|
@@ -1056,6 +1107,28 @@ function define( model ) {
|
|
|
1056
1107
|
}
|
|
1057
1108
|
return;
|
|
1058
1109
|
|
|
1110
|
+
function initElementsAsEnum() {
|
|
1111
|
+
// in extensions, extended enums are represented as elements
|
|
1112
|
+
for (const n in obj.elements) {
|
|
1113
|
+
const e = obj.elements[n];
|
|
1114
|
+
if (e.kind === 'element') {
|
|
1115
|
+
// An "element" has `$syntax: 'enum'` if it could also be an enum
|
|
1116
|
+
if (e.$syntax === 'enum') { // TODO: what about "just name"? (current forbidden)
|
|
1117
|
+
e.kind = 'enum';
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
// We do not want to complain separately about all element properties:
|
|
1121
|
+
error( 'ext-unexpected-element', [ e.location, construct ],
|
|
1122
|
+
{ name: e.name.id, code: 'extend … with enum' },
|
|
1123
|
+
// eslint-disable-next-line max-len
|
|
1124
|
+
'Unexpected elements like $(NAME) in an extension for an enum. Additionally, use $(CODE) when extending enums' );
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
forEachGeneric( { enum: obj.elements }, 'enum', init );
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1059
1132
|
function init( elem, name, prop ) {
|
|
1060
1133
|
if (!elem.kind) // wrong CSN input
|
|
1061
1134
|
elem.kind = dictKinds[prop];
|
|
@@ -1083,45 +1156,75 @@ function define( model ) {
|
|
|
1083
1156
|
|
|
1084
1157
|
const bl = elem._block || block;
|
|
1085
1158
|
setLink( elem, '_block', bl );
|
|
1086
|
-
|
|
1159
|
+
const existing = parent[prop]?.[name];
|
|
1160
|
+
const add = construct !== parent && (!existing || elem.$inferred !== 'include');
|
|
1161
|
+
setMemberParent( elem, name, parent, add && prop );
|
|
1087
1162
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1088
1163
|
checkRedefinition( elem );
|
|
1089
1164
|
if (elem.kind === 'annotate' || elem.kind === 'extend')
|
|
1090
1165
|
checkAnnotate( elem, elem );
|
|
1091
1166
|
initAnnotations( elem, bl );
|
|
1092
1167
|
initMembers( elem, elem, bl, initExtensions );
|
|
1168
|
+
if (boundSelfParamType && (elem.kind === 'action' || elem.kind === 'function'))
|
|
1169
|
+
initBoundSelfParam( elem.params );
|
|
1093
1170
|
|
|
1094
1171
|
// for a correct home path, setMemberParent needed to be called
|
|
1095
1172
|
|
|
1096
|
-
if (elem.value
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1173
|
+
if (!elem.value || elem.kind !== 'element' ||
|
|
1174
|
+
elem.$syntax === 'enum' && parent.kind === 'extend') // ambiguous in parse-cdl
|
|
1175
|
+
return;
|
|
1176
|
+
// -> it's a calculated element
|
|
1177
|
+
checkCalculatedElement(elem);
|
|
1178
|
+
elem.$syntax = 'calc';
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
function checkCalculatedElement( elem ) {
|
|
1183
|
+
const loc = [ elem.value.location, elem ];
|
|
1184
|
+
if (elem._main.kind !== 'entity' && elem._main.kind !== 'aspect' &&
|
|
1185
|
+
elem._main.kind !== 'extend') {
|
|
1186
|
+
error( 'syntax-invalid-calc-elem', loc, { '#': elem._main.kind } );
|
|
1187
|
+
}
|
|
1188
|
+
else if (!isBetaEnabled( options, 'calculatedElements' )) {
|
|
1189
|
+
error( 'def-unsupported-calc-elem', loc,
|
|
1190
|
+
'Calculated elements are not supported' );
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
const noTruthyAllowed = [ 'localized', 'key', 'virtual' ];
|
|
1194
|
+
for (const prop of noTruthyAllowed) {
|
|
1195
|
+
if (elem[prop]?.val) {
|
|
1196
|
+
// probably better than a parse error (which is good for DEFAULT vs calc),
|
|
1197
|
+
// also appears with parse-cdl:
|
|
1198
|
+
error('syntax-invalid-calc-elem', loc, { '#': prop });
|
|
1199
|
+
return; // one error is enough
|
|
1114
1200
|
}
|
|
1115
1201
|
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1116
1204
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1205
|
+
function initBoundSelfParam( params ) {
|
|
1206
|
+
if (!params)
|
|
1207
|
+
return;
|
|
1208
|
+
if (boundSelfParamType === true) { // first try
|
|
1209
|
+
const def = model.definitions.$self;
|
|
1210
|
+
if (def) {
|
|
1211
|
+
// TODO v4: bring this always, probably even as error
|
|
1212
|
+
warning( 'name-deprecated-for-artifacts', [ def.location, def ], { name: '$self' },
|
|
1213
|
+
'Do not use $(NAME) as name for an artifact definition' );
|
|
1214
|
+
boundSelfParamType = false;
|
|
1215
|
+
return;
|
|
1121
1216
|
}
|
|
1217
|
+
boundSelfParamType = { name: { absolute: '$self' } };
|
|
1122
1218
|
}
|
|
1219
|
+
const first = params[Object.keys( params )[0] || ''];
|
|
1220
|
+
const type = first?.type || first?.items?.type; // this sequence = no derived type
|
|
1221
|
+
if (type?.path?.length === 1 && type?.path[0]?.id === '$self') // TODO: no where: ?
|
|
1222
|
+
setLink( type, '_artifact', boundSelfParamType );
|
|
1123
1223
|
}
|
|
1124
1224
|
|
|
1225
|
+
// To be reworked -------------------------------------------------------------
|
|
1226
|
+
|
|
1227
|
+
// TODO: make special for extend/annotate
|
|
1125
1228
|
function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
|
|
1126
1229
|
// TODO: do differently, see also annotateMembers() in resolver
|
|
1127
1230
|
// To have been checked by parsers:
|
|
@@ -1161,8 +1264,15 @@ function define( model ) {
|
|
|
1161
1264
|
}
|
|
1162
1265
|
}
|
|
1163
1266
|
else if (feature) { // allowed in principle, but not with extend
|
|
1164
|
-
|
|
1165
|
-
|
|
1267
|
+
if (parent.$inferred === 'include') { // special case for better error message
|
|
1268
|
+
const variant = (construct.enum || construct.elements) ? 'elements' : 'std';
|
|
1269
|
+
error( 'ref-expected-direct-structure', [ location, construct ],
|
|
1270
|
+
{ '#': variant, art: parent });
|
|
1271
|
+
}
|
|
1272
|
+
else {
|
|
1273
|
+
error( 'extend-type', [ location, construct ], {},
|
|
1274
|
+
'Only structures or enum types can be extended with elements/enums' );
|
|
1275
|
+
}
|
|
1166
1276
|
}
|
|
1167
1277
|
else if (prop === 'elements') {
|
|
1168
1278
|
error( 'unexpected-elements', [ location, construct ], {},
|
|
@@ -1178,47 +1288,19 @@ function define( model ) {
|
|
|
1178
1288
|
return construct === parent;
|
|
1179
1289
|
}
|
|
1180
1290
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
.
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
if (sortedSources.length === 0)
|
|
1190
|
-
return;
|
|
1191
|
-
|
|
1192
|
-
if (!model.i18n)
|
|
1193
|
-
model.i18n = Object.create( null );
|
|
1194
|
-
|
|
1195
|
-
for (const name of sortedSources)
|
|
1196
|
-
initI18nFromSource( model.sources[name] );
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
/**
|
|
1200
|
-
* Add the source's translations to the model. Warns if the source's translations
|
|
1201
|
-
* do not match the ones from previous sources.
|
|
1202
|
-
*
|
|
1203
|
-
* @param {XSN.SourceAst} src
|
|
1204
|
-
*/
|
|
1205
|
-
function initI18nFromSource( src ) {
|
|
1206
|
-
for (const langKey of Object.keys( src.i18n )) {
|
|
1207
|
-
if (!model.i18n[langKey])
|
|
1208
|
-
model.i18n[langKey] = Object.create( null );
|
|
1209
|
-
|
|
1210
|
-
for (const textKey of Object.keys( src.i18n[langKey] )) {
|
|
1211
|
-
const sourceVal = src.i18n[langKey][textKey];
|
|
1212
|
-
const modelVal = model.i18n[langKey][textKey];
|
|
1213
|
-
if (!modelVal) {
|
|
1214
|
-
model.i18n[langKey][textKey] = sourceVal;
|
|
1215
|
-
}
|
|
1216
|
-
else if (modelVal.val !== sourceVal.val) {
|
|
1217
|
-
warning( 'i18n-different-value', sourceVal.location,
|
|
1218
|
-
{ prop: textKey, otherprop: langKey } );
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1291
|
+
// Return whether the `target` is actually a `targetAspect`
|
|
1292
|
+
// TODO: really do that here and not in kick-start.js?
|
|
1293
|
+
function targetIsTargetAspect( elem ) {
|
|
1294
|
+
const { target } = elem;
|
|
1295
|
+
if (target.elements) {
|
|
1296
|
+
// TODO: error if CSN has both target.elements and targetAspect.elements -> delete target
|
|
1297
|
+
return true;
|
|
1221
1298
|
}
|
|
1299
|
+
if (elem.targetAspect || options.parseCdl || !isDirectComposition( elem ))
|
|
1300
|
+
return false;
|
|
1301
|
+
const name = resolveUncheckedPath( target, 'compositionTarget', elem );
|
|
1302
|
+
const aspect = name && model.definitions[name];
|
|
1303
|
+
return aspect && (aspect.kind === 'aspect' || aspect.kind === 'type'); // type is sloppy
|
|
1222
1304
|
}
|
|
1223
1305
|
}
|
|
1224
1306
|
|