@sap/cds-compiler 2.15.8 → 3.1.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 +102 -1590
- package/bin/.eslintrc.json +2 -1
- package/bin/cdsc.js +61 -46
- package/doc/API.md +11 -0
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +26 -5
- package/doc/CHANGELOG_DEPRECATED.md +55 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +282 -156
- package/lib/api/options.js +17 -88
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +85 -25
- package/lib/base/messages.js +119 -89
- package/lib/base/model.js +46 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +15 -12
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +101 -15
- package/lib/checks/types.js +7 -8
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +3 -3
- package/lib/compiler/assert-consistency.js +78 -21
- package/lib/compiler/base.js +6 -4
- package/lib/compiler/builtins.js +177 -10
- package/lib/compiler/checks.js +1 -1
- package/lib/compiler/define.js +28 -23
- package/lib/compiler/extend.js +75 -18
- package/lib/compiler/finalize-parse-cdl.js +25 -18
- package/lib/compiler/index.js +27 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +26 -39
- package/lib/compiler/propagator.js +12 -7
- package/lib/compiler/resolve.js +207 -236
- package/lib/compiler/shared.js +100 -93
- package/lib/compiler/tweak-assocs.js +13 -20
- package/lib/compiler/utils.js +20 -6
- package/lib/edm/annotations/preprocessAnnotations.js +12 -13
- package/lib/edm/csn2edm.js +35 -37
- package/lib/edm/edm.js +22 -13
- package/lib/edm/edmAnnoPreprocessor.js +349 -0
- package/lib/edm/edmInboundChecks.js +85 -0
- package/lib/edm/edmPreprocessor.js +338 -689
- package/lib/edm/edmUtils.js +97 -67
- package/lib/gen/Dictionary.json +29 -9
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -31
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +892 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20629 -22474
- package/lib/inspect/.eslintrc.json +4 -0
- package/lib/inspect/index.js +14 -0
- package/lib/inspect/inspectModelStatistics.js +81 -0
- package/lib/inspect/inspectPropagation.js +189 -0
- package/lib/inspect/inspectUtils.js +44 -0
- package/lib/json/from-csn.js +74 -69
- package/lib/json/to-csn.js +17 -14
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +424 -292
- package/lib/language/language.g4 +604 -687
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +28 -42
- package/lib/main.js +104 -81
- package/lib/model/api.js +1 -1
- package/lib/model/csnRefs.js +57 -30
- package/lib/model/csnUtils.js +189 -287
- package/lib/model/revealInternalProperties.js +32 -10
- package/lib/model/sortViews.js +32 -31
- package/lib/modelCompare/compare.js +3 -0
- package/lib/optionProcessor.js +91 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +387 -367
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +81 -59
- package/lib/render/utils/common.js +16 -3
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/associations.js +43 -35
- package/lib/transform/db/cdsPersistence.js +5 -16
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +16 -18
- package/lib/transform/db/transformExists.js +7 -5
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +30 -24
- package/lib/transform/forOdataNew.js +14 -16
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +10 -10
- package/lib/transform/odata/typesExposure.js +17 -8
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +2 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/moduleResolve.js +0 -1
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/checks/unknownMagic.js +0 -41
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/compiler/resolve.js
CHANGED
|
@@ -49,13 +49,13 @@ const { dictAdd } = require('../base/dictionaries');
|
|
|
49
49
|
const { dictLocation } = require('../base/location');
|
|
50
50
|
const { searchName, weakLocation } = require('../base/messages');
|
|
51
51
|
const { combinedLocation } = require('../base/location');
|
|
52
|
-
const { forEachValue } = require('../utils/objectUtils');
|
|
53
52
|
const { typeParameters } = require('./builtins');
|
|
54
53
|
|
|
55
54
|
const { kindProperties } = require('./base');
|
|
56
55
|
const {
|
|
57
56
|
setLink,
|
|
58
57
|
setArtifactLink,
|
|
58
|
+
annotationHasEllipsis,
|
|
59
59
|
pathName,
|
|
60
60
|
linkToOrigin,
|
|
61
61
|
setMemberParent,
|
|
@@ -73,9 +73,6 @@ const layers = require('./moduleLayers');
|
|
|
73
73
|
|
|
74
74
|
const $location = Symbol.for('cds.$location');
|
|
75
75
|
|
|
76
|
-
const annotationPriorities = {
|
|
77
|
-
define: 1, extend: 2, annotate: 2, edmx: 3,
|
|
78
|
-
};
|
|
79
76
|
const $inferred = Symbol.for('cds.$inferred');
|
|
80
77
|
|
|
81
78
|
// Export function of this file. Resolve type references in augmented CSN
|
|
@@ -90,6 +87,7 @@ function resolve( model ) {
|
|
|
90
87
|
} = model.$messageFunctions;
|
|
91
88
|
const {
|
|
92
89
|
resolvePath,
|
|
90
|
+
checkAnnotate,
|
|
93
91
|
defineAnnotations,
|
|
94
92
|
attachAndEmitValidNames,
|
|
95
93
|
lateExtensions,
|
|
@@ -106,11 +104,6 @@ function resolve( model ) {
|
|
|
106
104
|
|
|
107
105
|
/** @type {any} may also be a boolean */
|
|
108
106
|
|
|
109
|
-
// behavior depending on option `deprecated`:
|
|
110
|
-
const enableExpandElements = !isDeprecatedEnabled( options, 'noElementsExpansion' );
|
|
111
|
-
// TODO: we should get rid of noElementsExpansion soon; both
|
|
112
|
-
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
113
|
-
|
|
114
107
|
return doResolve();
|
|
115
108
|
|
|
116
109
|
function doResolve() {
|
|
@@ -211,9 +204,9 @@ function resolve( model ) {
|
|
|
211
204
|
info( 'query-from-many', [ toMany.location, query ], { art: toMany },
|
|
212
205
|
{
|
|
213
206
|
// eslint-disable-next-line max-len
|
|
214
|
-
std: '
|
|
207
|
+
std: 'Key properties are not propagated because a to-many association $(ART) is selected',
|
|
215
208
|
// eslint-disable-next-line max-len
|
|
216
|
-
element: '
|
|
209
|
+
element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
|
|
217
210
|
} );
|
|
218
211
|
}
|
|
219
212
|
// Check that all keys from the source are projected:
|
|
@@ -317,7 +310,7 @@ function resolve( model ) {
|
|
|
317
310
|
if (obj.type) // TODO: && !obj.type.$inferred ?
|
|
318
311
|
resolveTypeExpr( obj, art );
|
|
319
312
|
const type = effectiveType( obj ); // make sure implicitly redirected target exists
|
|
320
|
-
if (!obj.items && type && type.items
|
|
313
|
+
if (!obj.items && type && type.items) {
|
|
321
314
|
// TODO: shouldn't be this part of populate.js ?
|
|
322
315
|
const items = {
|
|
323
316
|
location: weakLocation( (obj.type || obj).location ),
|
|
@@ -330,8 +323,7 @@ function resolve( model ) {
|
|
|
330
323
|
}
|
|
331
324
|
if (obj.items) { // TODO: make this a while in v2 (also items proxy)
|
|
332
325
|
obj = obj.items || obj; // the object which has type properties
|
|
333
|
-
|
|
334
|
-
effectiveType(obj);
|
|
326
|
+
effectiveType(obj);
|
|
335
327
|
}
|
|
336
328
|
if (obj.type) { // TODO: && !obj.type.$inferred ?
|
|
337
329
|
if (obj !== (art.returns || art)) // not already checked
|
|
@@ -484,10 +476,7 @@ function resolve( model ) {
|
|
|
484
476
|
if (ext.$extension) // extension for known artifact -> already applied
|
|
485
477
|
return;
|
|
486
478
|
annotateMembers( ext );
|
|
487
|
-
|
|
488
|
-
if (prop.charAt(0) === '@')
|
|
489
|
-
chooseAssignment( prop, ext );
|
|
490
|
-
}
|
|
479
|
+
chooseAnnotationsInArtifact( ext );
|
|
491
480
|
}
|
|
492
481
|
|
|
493
482
|
/**
|
|
@@ -522,6 +511,8 @@ function resolve( model ) {
|
|
|
522
511
|
setArtifactLink( ext.name, art );
|
|
523
512
|
|
|
524
513
|
if (art) {
|
|
514
|
+
if (art.kind === 'annotate')
|
|
515
|
+
checkAnnotate( ext, art );
|
|
525
516
|
defineAnnotations( ext, art, ext._block, ext.kind );
|
|
526
517
|
// eslint-disable-next-line no-shadow
|
|
527
518
|
forEachMember( ext, ( elem, name, prop ) => {
|
|
@@ -544,7 +535,7 @@ function resolve( model ) {
|
|
|
544
535
|
}
|
|
545
536
|
else if (prop === 'actions') {
|
|
546
537
|
if (!feature) {
|
|
547
|
-
warning( 'anno-unexpected-actions', [ ext.name.location, art ], {},
|
|
538
|
+
warning( 'anno-unexpected-actions', [ ext.name.location, art._parent || art ], {},
|
|
548
539
|
'Actions and functions only exist top-level and for entities' );
|
|
549
540
|
}
|
|
550
541
|
else {
|
|
@@ -575,7 +566,7 @@ function resolve( model ) {
|
|
|
575
566
|
// Currently(?), effectiveType() does not calculate the effective type of
|
|
576
567
|
// its line item:
|
|
577
568
|
effectiveType( obj );
|
|
578
|
-
if (art._annotate.elements)
|
|
569
|
+
if (art._annotate.elements) // explicit $expand on aor needed
|
|
579
570
|
setExpandStatusAnnotate( aor, 'annotate' );
|
|
580
571
|
annotate( obj, 'element', 'elements', 'enum', art );
|
|
581
572
|
annotate( art, 'action', 'actions' );
|
|
@@ -599,6 +590,8 @@ function resolve( model ) {
|
|
|
599
590
|
// eslint-disable-next-line no-shadow
|
|
600
591
|
function annotate( obj, kind, prop, altProp, parent = obj ) {
|
|
601
592
|
const dict = art._annotate[prop];
|
|
593
|
+
if (dict && art._annotate[prop])
|
|
594
|
+
setExpandStatusAnnotate( art, 'annotate' );
|
|
602
595
|
const env = obj[prop] || altProp && obj[altProp] || null;
|
|
603
596
|
for (const n in dict)
|
|
604
597
|
annotateMembers( env && env[n], dict[n], prop, n, parent, kind );
|
|
@@ -621,7 +614,7 @@ function resolve( model ) {
|
|
|
621
614
|
|
|
622
615
|
function expandParameters( action ) {
|
|
623
616
|
// see also expandElements()
|
|
624
|
-
if (!
|
|
617
|
+
if (!effectiveType( action ))
|
|
625
618
|
return;
|
|
626
619
|
const chain = [];
|
|
627
620
|
// Should we be able to consider params and returns separately?
|
|
@@ -714,241 +707,219 @@ function resolve( model ) {
|
|
|
714
707
|
}
|
|
715
708
|
|
|
716
709
|
function chooseAssignment( annoName, art ) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
710
|
+
let anno = art[annoName];
|
|
711
|
+
if (!Array.isArray( anno )) { // just one assignment -> use it
|
|
712
|
+
if (!annotationHasEllipsis( anno ))
|
|
713
|
+
return;
|
|
714
|
+
anno = [ anno ];
|
|
715
|
+
}
|
|
716
|
+
// console.log('ASSIGN:',art.name.absolute,annoName)
|
|
717
|
+
const scheduledAssignments = [];
|
|
718
|
+
// sort assignment according to layer (define is bottom layer):
|
|
719
|
+
const layeredAnnos = layeredAssignments( anno );
|
|
720
|
+
let cont = true;
|
|
721
|
+
while (cont) {
|
|
722
|
+
const { assignments, issue } = assignmentsOfHighestLayers( layeredAnnos );
|
|
723
|
+
let index = assignments.length;
|
|
724
|
+
cont = !!index; // safety
|
|
725
|
+
while (--index >= 0) {
|
|
726
|
+
const a = assignments[index];
|
|
727
|
+
scheduledAssignments.push( a );
|
|
728
|
+
if (!annotationHasEllipsis( a )) {
|
|
729
|
+
cont = false;
|
|
730
|
+
break;
|
|
731
|
+
}
|
|
723
732
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
733
|
+
if (issue) {
|
|
734
|
+
// eslint-disable-next-line no-nested-ternary
|
|
735
|
+
const msg = (issue === true)
|
|
736
|
+
? 'anno-duplicate'
|
|
737
|
+
: (index >= 0) ? 'anno-duplicate-unrelated-layer' : 'anno-unstable-array';
|
|
738
|
+
for (const a of assignments) {
|
|
739
|
+
if (!a.$errorReported)
|
|
740
|
+
message( msg, [ a.name.location, art ], { anno: annoName } );
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
// else if (index > 0) -- if we allow multiple assignments in one file - the last wins
|
|
744
|
+
}
|
|
745
|
+
// Now apply the assignments - all but the first have a '...'
|
|
746
|
+
let result = null;
|
|
747
|
+
scheduledAssignments.reverse();
|
|
748
|
+
for (const a of scheduledAssignments)
|
|
749
|
+
result = applyAssignment( result, a, art, annoName );
|
|
750
|
+
art[annoName] = result.name ? result
|
|
751
|
+
: Object.assign( {}, scheduledAssignments[scheduledAssignments.length - 1], result );
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Group assignments by their layers. An assignment provided with a definition
|
|
755
|
+
// is considered to be provided in a layer named '', the lowest layer.
|
|
756
|
+
// TODO: make this usable for extend (elements), too =
|
|
757
|
+
// do not use $priority, make assignments on define do not have own _block
|
|
758
|
+
function layeredAssignments( assignment ) {
|
|
759
|
+
const layered = Object.create(null);
|
|
760
|
+
for (const a of assignment) {
|
|
761
|
+
const layer = a.$priority && layers.layer( a );
|
|
762
|
+
// just consider layer if Extend/Annotate, not Define
|
|
730
763
|
const name = (layer) ? layer.realname : '';
|
|
731
|
-
const done =
|
|
764
|
+
const done = layered[name];
|
|
732
765
|
if (done)
|
|
733
|
-
done.
|
|
766
|
+
done.assignments.push( a );
|
|
734
767
|
else
|
|
735
|
-
|
|
768
|
+
layered[name] = { name, layer, assignments: [ a ] };
|
|
769
|
+
// TODO: file - if set: unique in layer
|
|
736
770
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
return;
|
|
771
|
+
return layered;
|
|
772
|
+
}
|
|
740
773
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
function mergeLayeredArrays( mergeTarget ) {
|
|
775
|
-
if (mergeTarget.literal === 'array') {
|
|
776
|
-
let layer = layers.layer( mergeTarget._block );
|
|
777
|
-
delete layerAnnos[(layer) ? layer.realname : ''];
|
|
778
|
-
let pos = findEllipsis( mergeTarget );
|
|
779
|
-
let hasRun = false;
|
|
780
|
-
while (pos > -1 && Object.keys( layerAnnos ).length ) {
|
|
781
|
-
hasRun = true;
|
|
782
|
-
const mergeSource = findLayerCandidate();
|
|
783
|
-
if (mergeSource.literal !== 'array') {
|
|
784
|
-
error( 'anno-mismatched-ellipsis',
|
|
785
|
-
[ mergeSource.name.location, art ], { code: '...' } );
|
|
786
|
-
return mergeTarget;
|
|
787
|
-
}
|
|
788
|
-
mergeTarget.val = mergeArrayValues( mergeSource.val, mergeTarget.val );
|
|
789
|
-
layer = layers.layer( mergeSource._block );
|
|
790
|
-
delete layerAnnos[(layer) ? layer.realname : ''];
|
|
791
|
-
pos = findEllipsis( mergeTarget );
|
|
792
|
-
}
|
|
793
|
-
// All layers were processed. Remove excess ellipsis.
|
|
794
|
-
if (removeEllipsis( mergeTarget, pos ) && hasRun) {
|
|
795
|
-
// There shouldn't be any ellipsis or we don't have a base annotation.
|
|
796
|
-
// But only if the loop above has run. Otherwise the in-layer merge
|
|
797
|
-
// already warned about this case.
|
|
798
|
-
message( 'anno-unexpected-ellipsis-layers',
|
|
799
|
-
[ mergeTarget.name.location, art ], { code: '...' } );
|
|
800
|
-
}
|
|
774
|
+
// Return assignments of the highest layers.
|
|
775
|
+
// Also return whether there could be an issue:
|
|
776
|
+
// - false: there is just one assignment
|
|
777
|
+
// - 'unrelated': there is just one assignment per layer
|
|
778
|
+
// - true: there is at least one layer with two or more assignments
|
|
779
|
+
// TODO: make this usable for extend (elements), too
|
|
780
|
+
function assignmentsOfHighestLayers( layeredAnnos ) {
|
|
781
|
+
const layerNames = Object.keys( layeredAnnos );
|
|
782
|
+
// console.log('HIB:',layerNames)
|
|
783
|
+
if (layerNames.length <= 1) {
|
|
784
|
+
const name = layerNames[0];
|
|
785
|
+
const { assignments } = layeredAnnos[name] || { assignments: [] };
|
|
786
|
+
delete layeredAnnos[name];
|
|
787
|
+
return { assignments, issue: assignments.length > 1 };
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// collect all layers which are lower than another layer
|
|
791
|
+
const allExtends = Object.create(null);
|
|
792
|
+
allExtends[''] = {}; // the "Define" layer
|
|
793
|
+
for (const name of layerNames) {
|
|
794
|
+
if (name) // not the "Define" layer
|
|
795
|
+
Object.assign( allExtends, layeredAnnos[name].layer._layerExtends );
|
|
796
|
+
}
|
|
797
|
+
// console.log('HIE:',Object.keys(allExtends))
|
|
798
|
+
const assignments = [];
|
|
799
|
+
const highest = [];
|
|
800
|
+
for (const name of layerNames) {
|
|
801
|
+
if (!(name in allExtends)) {
|
|
802
|
+
const layer = layeredAnnos[name];
|
|
803
|
+
delete layeredAnnos[name];
|
|
804
|
+
highest.push( layer );
|
|
805
|
+
assignments.push( ...layer.assignments );
|
|
801
806
|
}
|
|
802
|
-
return mergeTarget;
|
|
803
807
|
}
|
|
808
|
+
assignments.sort( compareAssignments );
|
|
809
|
+
const good = highest.every( layer => layer.assignments.length === 1 );
|
|
810
|
+
// TODO: use layer.file instead
|
|
811
|
+
const issue = !good || highest.length > 1 && 'unrelated';
|
|
812
|
+
// console.log('HI:',highest.map(l=>l.name),issue,issue&&assignments)
|
|
813
|
+
return { assignments, issue };
|
|
814
|
+
}
|
|
804
815
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
816
|
+
function compareAssignments( a, b ) {
|
|
817
|
+
const fileA = layers.realname( a._block );
|
|
818
|
+
const fileB = layers.realname( b._block );
|
|
819
|
+
if (fileA !== fileB)
|
|
820
|
+
return (fileA > fileB) ? 1 : -1;
|
|
821
|
+
return (a?.location?.line || 0) - (b?.location?.line || 0) ||
|
|
822
|
+
(a?.location?.col || 0) - (b?.location?.col || 0);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function applyAssignment( previousAnno, anno, art, annoName ) {
|
|
826
|
+
if (!previousAnno) {
|
|
827
|
+
if (!annotationHasEllipsis( anno ))
|
|
828
|
+
return anno;
|
|
829
|
+
if (anno.$priority) { // already complained about with Define
|
|
830
|
+
message( 'anno-unexpected-ellipsis-layers', // TODO: better location
|
|
831
|
+
[ anno.name.location, art ], { code: '...' } );
|
|
832
|
+
}
|
|
833
|
+
previousAnno = { val: [] };
|
|
834
|
+
}
|
|
835
|
+
else if (previousAnno.literal !== 'array') {
|
|
836
|
+
error( 'anno-mismatched-ellipsis', // TODO: better location
|
|
837
|
+
[ anno.name.location, art ], { code: '...' } );
|
|
838
|
+
previousAnno = { val: [] };
|
|
839
|
+
}
|
|
840
|
+
const previousValue = previousAnno.val;
|
|
841
|
+
let prevPos = 0;
|
|
842
|
+
const result = [];
|
|
843
|
+
for (const item of anno.val) {
|
|
844
|
+
const ell = item && item.literal === 'token' && item.val === '...';
|
|
845
|
+
if (!ell) {
|
|
846
|
+
result.push( item );
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
let upToSpec = item.upTo && checkUpToSpec( item.upTo, art, annoName, true );
|
|
850
|
+
while (prevPos < previousValue.length) {
|
|
851
|
+
const prevItem = previousValue[prevPos++];
|
|
852
|
+
result.push( prevItem );
|
|
853
|
+
if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo)) {
|
|
854
|
+
upToSpec = false;
|
|
855
|
+
break;
|
|
826
856
|
}
|
|
827
857
|
}
|
|
858
|
+
if (upToSpec) { // non-matched UP TO
|
|
859
|
+
warning( null, [ item.upTo.location, art ], { anno: annoName, code: '... up to' },
|
|
860
|
+
'The $(CODE) value does not match any item in the base annotation $(ANNO)' );
|
|
861
|
+
}
|
|
828
862
|
}
|
|
829
|
-
return result;
|
|
830
863
|
}
|
|
864
|
+
// console.log('TP:',previousValue.map(se),anno.val.map(se),'->',result.map(se))
|
|
865
|
+
return { val: result, literal: 'array' };
|
|
866
|
+
}
|
|
867
|
+
// function se(a) { return a.upTo ? [a.val,a.upTo.val] : a.val ; }
|
|
831
868
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
return true;
|
|
837
|
-
}
|
|
838
|
-
else if (literal === 'struct') {
|
|
839
|
-
return Object.values( upToSpec.struct ).every( checkUpToSpec );
|
|
840
|
-
}
|
|
841
|
-
else if (literal !== 'array' && literal !== 'boolean' && literal !== 'null') {
|
|
869
|
+
function checkUpToSpec( upToSpec, art, annoName, isFullUpTo ) {
|
|
870
|
+
const { literal } = upToSpec;
|
|
871
|
+
if (!isFullUpTo) { // inside struct of UP TO
|
|
872
|
+
if (literal !== 'struct' && literal !== 'array' )
|
|
842
873
|
return true;
|
|
843
|
-
}
|
|
844
|
-
error( null, [ upToSpec.location, art ],
|
|
845
|
-
{ anno: annoName, code: '... up to', '#': literal },
|
|
846
|
-
{
|
|
847
|
-
std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
|
|
848
|
-
array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
|
|
849
|
-
// eslint-disable-next-line max-len
|
|
850
|
-
struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
|
|
851
|
-
boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
|
|
852
|
-
null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
|
|
853
|
-
} );
|
|
854
|
-
return false;
|
|
855
874
|
}
|
|
875
|
+
else if (literal === 'struct') {
|
|
876
|
+
return Object.values( upToSpec.struct ).every( v => checkUpToSpec( v, art, annoName ) );
|
|
877
|
+
}
|
|
878
|
+
else if (literal !== 'array' && literal !== 'boolean' && literal !== 'null') {
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
error( null, [ upToSpec.location, art ],
|
|
882
|
+
{ anno: annoName, code: '... up to', '#': literal },
|
|
883
|
+
{
|
|
884
|
+
std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
|
|
885
|
+
array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
|
|
886
|
+
// eslint-disable-next-line max-len
|
|
887
|
+
struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
|
|
888
|
+
boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
|
|
889
|
+
null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
|
|
890
|
+
} );
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
856
893
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
return false;
|
|
860
|
-
if ('val' in upToSpec) {
|
|
861
|
-
if (previousItem.val === upToSpec.val) // enum, struct and ref have no val
|
|
862
|
-
return true;
|
|
863
|
-
const typeUpTo = typeof upToSpec.val;
|
|
864
|
-
const typePrev = typeof previousItem.val;
|
|
865
|
-
if (typeUpTo === 'number')
|
|
866
|
-
return typePrev === 'string' && previousItem.val === upToSpec.val.toString();
|
|
867
|
-
if (typePrev === 'number')
|
|
868
|
-
return typeUpTo === 'string' && upToSpec.val === previousItem.val.toString();
|
|
869
|
-
}
|
|
870
|
-
else if (upToSpec.path) {
|
|
871
|
-
return previousItem.path && normalizeRef( previousItem ) === normalizeRef( upToSpec );
|
|
872
|
-
}
|
|
873
|
-
else if (upToSpec.sym) {
|
|
874
|
-
return previousItem.sym && previousItem.sym.id === upToSpec.sym.id;
|
|
875
|
-
}
|
|
876
|
-
else if (upToSpec.struct && previousItem.struct) {
|
|
877
|
-
return Object.entries( upToSpec.struct )
|
|
878
|
-
.every( ([ n, v ]) => equalUpTo( previousItem.struct[n], v ) );
|
|
879
|
-
}
|
|
894
|
+
function equalUpTo( previousItem, upToSpec ) {
|
|
895
|
+
if (!previousItem)
|
|
880
896
|
return false;
|
|
897
|
+
if ('val' in upToSpec) {
|
|
898
|
+
if (previousItem.val === upToSpec.val) // enum, struct and ref have no val
|
|
899
|
+
return true;
|
|
900
|
+
const typeUpTo = typeof upToSpec.val;
|
|
901
|
+
const typePrev = typeof previousItem.val;
|
|
902
|
+
if (typeUpTo === 'number')
|
|
903
|
+
return typePrev === 'string' && previousItem.val === upToSpec.val.toString();
|
|
904
|
+
if (typePrev === 'number')
|
|
905
|
+
return typeUpTo === 'string' && upToSpec.val === previousItem.val.toString();
|
|
881
906
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
const ref = pathName( node.path );
|
|
885
|
-
return node.variant ? `${ ref }#${ node.variant.id }` : ref;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
function removeEllipsis(a, pos = findEllipsis( a )) {
|
|
889
|
-
let count = 0;
|
|
890
|
-
while (a.literal === 'array' && pos > -1) {
|
|
891
|
-
count++;
|
|
892
|
-
a.val.splice(pos, 1);
|
|
893
|
-
pos = findEllipsis( a );
|
|
894
|
-
}
|
|
895
|
-
return count;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
function findEllipsis(a) {
|
|
899
|
-
return (a.literal === 'array' && a.val)
|
|
900
|
-
? a.val.findIndex(v => v.literal === 'token' && v.val === '...') : -1;
|
|
907
|
+
else if (upToSpec.path) {
|
|
908
|
+
return previousItem.path && normalizeRef( previousItem ) === normalizeRef( upToSpec );
|
|
901
909
|
}
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
// collect assignments of upper layers (are in no _layerExtends)
|
|
905
|
-
const exts = Object.keys( layerAnnos ).map( layerExtends );
|
|
906
|
-
const allExtends = Object.assign( Object.create(null), ...exts );
|
|
907
|
-
const collected = [];
|
|
908
|
-
for (const name in layerAnnos) {
|
|
909
|
-
if (!(name in allExtends))
|
|
910
|
-
collected.push( prioritizedAnnos( layerAnnos[name].annos ) );
|
|
911
|
-
}
|
|
912
|
-
// inspect collected assignments - choose the one or signal error
|
|
913
|
-
const justOnePerLayer = collected.every( annos => annos.length === 1);
|
|
914
|
-
if (!justOnePerLayer || collected.length > 1) {
|
|
915
|
-
for (const annos of collected) {
|
|
916
|
-
for (const a of annos ) {
|
|
917
|
-
// Only the message ID is different.
|
|
918
|
-
if (justOnePerLayer) {
|
|
919
|
-
message( 'anno-duplicate-unrelated-layer',
|
|
920
|
-
[ a.name.location, art ], { anno: annoName },
|
|
921
|
-
'Duplicate assignment with $(ANNO)' );
|
|
922
|
-
}
|
|
923
|
-
else {
|
|
924
|
-
message( 'anno-duplicate', [ a.name.location, art ], { anno: annoName } );
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
return collected[0][0]; // just choose any one with error
|
|
910
|
+
else if (upToSpec.sym) {
|
|
911
|
+
return previousItem.sym && previousItem.sym.id === upToSpec.sym.id;
|
|
930
912
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
return layer && layer._layerExtends;
|
|
913
|
+
else if (upToSpec.struct && previousItem.struct) {
|
|
914
|
+
return Object.entries( upToSpec.struct )
|
|
915
|
+
.every( ([ n, v ]) => equalUpTo( previousItem.struct[n], v ) );
|
|
935
916
|
}
|
|
917
|
+
return false;
|
|
936
918
|
}
|
|
937
919
|
|
|
938
|
-
function
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
for (const a of annos) {
|
|
942
|
-
const p = annotationPriorities[a.$priority] || annotationPriorities.define;
|
|
943
|
-
if (p === prio) {
|
|
944
|
-
r.push(a);
|
|
945
|
-
}
|
|
946
|
-
else if (p > prio) {
|
|
947
|
-
r = [ a ];
|
|
948
|
-
prio = p;
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return r;
|
|
920
|
+
function normalizeRef( node ) { // see to-csn.js
|
|
921
|
+
const ref = pathName( node.path );
|
|
922
|
+
return node.variant ? `${ ref }#${ node.variant.id }` : ref;
|
|
952
923
|
}
|
|
953
924
|
|
|
954
925
|
// Phase 4 - queries and associations --------------------------------------
|
|
@@ -984,7 +955,7 @@ function resolve( model ) {
|
|
|
984
955
|
resolveBy( query.$orderBy, 'order-by-union', query.elements, query._parent );
|
|
985
956
|
if (query.orderBy) { // ORDER BY
|
|
986
957
|
// search in `query.elements` after having checked table aliases of the current query
|
|
987
|
-
resolveBy( query.orderBy, '
|
|
958
|
+
resolveBy( query.orderBy, 'order-by', query.elements );
|
|
988
959
|
// TODO: disallow resulting element ref if in expression!
|
|
989
960
|
// Necessary to check it in the compiler as it might work with other semantics on DB!
|
|
990
961
|
// (we could downgrade it to a warning if name is equal to unique source element name)
|
|
@@ -1292,7 +1263,7 @@ function resolve( model ) {
|
|
|
1292
1263
|
const typeArt = resolveType( art.type, user );
|
|
1293
1264
|
if (typeArt) {
|
|
1294
1265
|
resolveTypeArgumentsUnchecked( art, typeArt, user );
|
|
1295
|
-
checkTypeArguments( art );
|
|
1266
|
+
checkTypeArguments( art, typeArt );
|
|
1296
1267
|
}
|
|
1297
1268
|
}
|
|
1298
1269
|
|
|
@@ -1300,13 +1271,13 @@ function resolve( model ) {
|
|
|
1300
1271
|
* Check the type arguments on `artWithType`.
|
|
1301
1272
|
* If the effective type is an array or structured type, an error is emitted.
|
|
1302
1273
|
*/
|
|
1303
|
-
function checkTypeArguments( artWithType ) {
|
|
1274
|
+
function checkTypeArguments( artWithType, typeArt ) {
|
|
1304
1275
|
// Note: `_effectiveType` may point to `artWithType` itself, if the type is structured.
|
|
1305
1276
|
// Also: For enums, it points to the enum type, which is why this trick is needed.
|
|
1306
1277
|
// TODO(#8942): May not be necessary if effectiveType() is adapted. Furthermore, the enum
|
|
1307
1278
|
// trick may be removed if effectiveType() does not stop at enums.
|
|
1308
1279
|
const cyclic = new Set();
|
|
1309
|
-
let effectiveTypeArt = effectiveType(
|
|
1280
|
+
let effectiveTypeArt = effectiveType( typeArt );
|
|
1310
1281
|
while (effectiveTypeArt && effectiveTypeArt.enum && !cyclic.has(effectiveTypeArt)) {
|
|
1311
1282
|
cyclic.add(effectiveTypeArt);
|
|
1312
1283
|
const underlyingEnumType = directType(effectiveTypeArt);
|