@sap/cds-compiler 2.15.2 → 3.0.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 +66 -1590
- package/bin/cdsc.js +42 -46
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -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 +312 -143
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +80 -24
- package/lib/base/messages.js +103 -52
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +7 -5
- 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 +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +127 -10
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +63 -12
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +16 -14
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +194 -222
- package/lib/compiler/shared.js +56 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +7 -2
- package/lib/edm/annotations/genericTranslation.js +60 -6
- package/lib/edm/annotations/preprocessAnnotations.js +10 -11
- package/lib/edm/csn2edm.js +39 -41
- package/lib/edm/edm.js +22 -15
- package/lib/edm/edmPreprocessor.js +66 -69
- package/lib/edm/edmUtils.js +12 -62
- package/lib/gen/Dictionary.json +8 -6
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20717 -22376
- package/lib/json/from-csn.js +73 -68
- package/lib/json/to-csn.js +13 -10
- 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 +333 -259
- package/lib/language/language.g4 +600 -645
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +2 -1
- package/lib/model/csnUtils.js +183 -285
- package/lib/model/revealInternalProperties.js +32 -9
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +64 -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 +334 -339
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +60 -54
- package/lib/render/utils/common.js +15 -1
- 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/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- 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 +19 -22
- package/lib/transform/forOdataNew.js +13 -15
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +11 -9
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +6 -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/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/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
|
|
@@ -107,7 +104,7 @@ function resolve( model ) {
|
|
|
107
104
|
/** @type {any} may also be a boolean */
|
|
108
105
|
|
|
109
106
|
// behavior depending on option `deprecated`:
|
|
110
|
-
const enableExpandElements = !isDeprecatedEnabled( options, '
|
|
107
|
+
const enableExpandElements = !isDeprecatedEnabled( options, '_noElementsExpansion' );
|
|
111
108
|
// TODO: we should get rid of noElementsExpansion soon; both
|
|
112
109
|
// beta.nestedProjections and beta.universalCsn do not work with it.
|
|
113
110
|
|
|
@@ -211,9 +208,9 @@ function resolve( model ) {
|
|
|
211
208
|
info( 'query-from-many', [ toMany.location, query ], { art: toMany },
|
|
212
209
|
{
|
|
213
210
|
// eslint-disable-next-line max-len
|
|
214
|
-
std: '
|
|
211
|
+
std: 'Key properties are not propagated because a to-many association $(ART) is selected',
|
|
215
212
|
// eslint-disable-next-line max-len
|
|
216
|
-
element: '
|
|
213
|
+
element: 'Key properties are not propagated because a to-many association $(MEMBER) of $(ART) is selected',
|
|
217
214
|
} );
|
|
218
215
|
}
|
|
219
216
|
// Check that all keys from the source are projected:
|
|
@@ -484,10 +481,7 @@ function resolve( model ) {
|
|
|
484
481
|
if (ext.$extension) // extension for known artifact -> already applied
|
|
485
482
|
return;
|
|
486
483
|
annotateMembers( ext );
|
|
487
|
-
|
|
488
|
-
if (prop.charAt(0) === '@')
|
|
489
|
-
chooseAssignment( prop, ext );
|
|
490
|
-
}
|
|
484
|
+
chooseAnnotationsInArtifact( ext );
|
|
491
485
|
}
|
|
492
486
|
|
|
493
487
|
/**
|
|
@@ -714,241 +708,219 @@ function resolve( model ) {
|
|
|
714
708
|
}
|
|
715
709
|
|
|
716
710
|
function chooseAssignment( annoName, art ) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
711
|
+
let anno = art[annoName];
|
|
712
|
+
if (!Array.isArray( anno )) { // just one assignment -> use it
|
|
713
|
+
if (!annotationHasEllipsis( anno ))
|
|
714
|
+
return;
|
|
715
|
+
anno = [ anno ];
|
|
716
|
+
}
|
|
717
|
+
// console.log('ASSIGN:',art.name.absolute,annoName)
|
|
718
|
+
const scheduledAssignments = [];
|
|
719
|
+
// sort assignment according to layer (define is bottom layer):
|
|
720
|
+
const layeredAnnos = layeredAssignments( anno );
|
|
721
|
+
let cont = true;
|
|
722
|
+
while (cont) {
|
|
723
|
+
const { assignments, issue } = assignmentsOfHighestLayers( layeredAnnos );
|
|
724
|
+
let index = assignments.length;
|
|
725
|
+
cont = !!index; // safety
|
|
726
|
+
while (--index >= 0) {
|
|
727
|
+
const a = assignments[index];
|
|
728
|
+
scheduledAssignments.push( a );
|
|
729
|
+
if (!annotationHasEllipsis( a )) {
|
|
730
|
+
cont = false;
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
723
733
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
734
|
+
if (issue) {
|
|
735
|
+
// eslint-disable-next-line no-nested-ternary
|
|
736
|
+
const msg = (issue === true)
|
|
737
|
+
? 'anno-duplicate'
|
|
738
|
+
: (index >= 0) ? 'anno-duplicate-unrelated-layer' : 'anno-unstable-array';
|
|
739
|
+
for (const a of assignments) {
|
|
740
|
+
if (!a.$errorReported)
|
|
741
|
+
message( msg, [ a.name.location, art ], { anno: annoName } );
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
// else if (index > 0) -- if we allow multiple assignments in one file - the last wins
|
|
745
|
+
}
|
|
746
|
+
// Now apply the assignments - all but the first have a '...'
|
|
747
|
+
let result = null;
|
|
748
|
+
scheduledAssignments.reverse();
|
|
749
|
+
for (const a of scheduledAssignments)
|
|
750
|
+
result = applyAssignment( result, a, art, annoName );
|
|
751
|
+
art[annoName] = result.name ? result
|
|
752
|
+
: Object.assign( {}, scheduledAssignments[scheduledAssignments.length - 1], result );
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Group assignments by their layers. An assignment provided with a definition
|
|
756
|
+
// is considered to be provided in a layer named '', the lowest layer.
|
|
757
|
+
// TODO: make this usable for extend (elements), too =
|
|
758
|
+
// do not use $priority, make assignments on define do not have own _block
|
|
759
|
+
function layeredAssignments( assignment ) {
|
|
760
|
+
const layered = Object.create(null);
|
|
761
|
+
for (const a of assignment) {
|
|
762
|
+
const layer = a.$priority && layers.layer( a );
|
|
763
|
+
// just consider layer if Extend/Annotate, not Define
|
|
730
764
|
const name = (layer) ? layer.realname : '';
|
|
731
|
-
const done =
|
|
765
|
+
const done = layered[name];
|
|
732
766
|
if (done)
|
|
733
|
-
done.
|
|
767
|
+
done.assignments.push( a );
|
|
734
768
|
else
|
|
735
|
-
|
|
769
|
+
layered[name] = { name, layer, assignments: [ a ] };
|
|
770
|
+
// TODO: file - if set: unique in layer
|
|
736
771
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
return;
|
|
772
|
+
return layered;
|
|
773
|
+
}
|
|
740
774
|
|
|
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
|
-
}
|
|
775
|
+
// Return assignments of the highest layers.
|
|
776
|
+
// Also return whether there could be an issue:
|
|
777
|
+
// - false: there is just one assignment
|
|
778
|
+
// - 'unrelated': there is just one assignment per layer
|
|
779
|
+
// - true: there is at least one layer with two or more assignments
|
|
780
|
+
// TODO: make this usable for extend (elements), too
|
|
781
|
+
function assignmentsOfHighestLayers( layeredAnnos ) {
|
|
782
|
+
const layerNames = Object.keys( layeredAnnos );
|
|
783
|
+
// console.log('HIB:',layerNames)
|
|
784
|
+
if (layerNames.length <= 1) {
|
|
785
|
+
const name = layerNames[0];
|
|
786
|
+
const { assignments } = layeredAnnos[name] || { assignments: [] };
|
|
787
|
+
delete layeredAnnos[name];
|
|
788
|
+
return { assignments, issue: assignments.length > 1 };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// collect all layers which are lower than another layer
|
|
792
|
+
const allExtends = Object.create(null);
|
|
793
|
+
allExtends[''] = {}; // the "Define" layer
|
|
794
|
+
for (const name of layerNames) {
|
|
795
|
+
if (name) // not the "Define" layer
|
|
796
|
+
Object.assign( allExtends, layeredAnnos[name].layer._layerExtends );
|
|
797
|
+
}
|
|
798
|
+
// console.log('HIE:',Object.keys(allExtends))
|
|
799
|
+
const assignments = [];
|
|
800
|
+
const highest = [];
|
|
801
|
+
for (const name of layerNames) {
|
|
802
|
+
if (!(name in allExtends)) {
|
|
803
|
+
const layer = layeredAnnos[name];
|
|
804
|
+
delete layeredAnnos[name];
|
|
805
|
+
highest.push( layer );
|
|
806
|
+
assignments.push( ...layer.assignments );
|
|
801
807
|
}
|
|
802
|
-
return mergeTarget;
|
|
803
808
|
}
|
|
809
|
+
assignments.sort( compareAssignments );
|
|
810
|
+
const good = highest.every( layer => layer.assignments.length === 1 );
|
|
811
|
+
// TODO: use layer.file instead
|
|
812
|
+
const issue = !good || highest.length > 1 && 'unrelated';
|
|
813
|
+
// console.log('HI:',highest.map(l=>l.name),issue,issue&&assignments)
|
|
814
|
+
return { assignments, issue };
|
|
815
|
+
}
|
|
804
816
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
817
|
+
function compareAssignments( a, b ) {
|
|
818
|
+
const fileA = layers.realname( a._block );
|
|
819
|
+
const fileB = layers.realname( b._block );
|
|
820
|
+
if (fileA !== fileB)
|
|
821
|
+
return (fileA > fileB) ? 1 : -1;
|
|
822
|
+
return (a?.location?.line || 0) - (b?.location?.line || 0) ||
|
|
823
|
+
(a?.location?.col || 0) - (b?.location?.col || 0);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
function applyAssignment( previousAnno, anno, art, annoName ) {
|
|
827
|
+
if (!previousAnno) {
|
|
828
|
+
if (!annotationHasEllipsis( anno ))
|
|
829
|
+
return anno;
|
|
830
|
+
if (anno.$priority) { // already complained about with Define
|
|
831
|
+
message( 'anno-unexpected-ellipsis-layers', // TODO: better location
|
|
832
|
+
[ anno.name.location, art ], { code: '...' } );
|
|
833
|
+
}
|
|
834
|
+
previousAnno = { val: [] };
|
|
835
|
+
}
|
|
836
|
+
else if (previousAnno.literal !== 'array') {
|
|
837
|
+
error( 'anno-mismatched-ellipsis', // TODO: better location
|
|
838
|
+
[ anno.name.location, art ], { code: '...' } );
|
|
839
|
+
previousAnno = { val: [] };
|
|
840
|
+
}
|
|
841
|
+
const previousValue = previousAnno.val;
|
|
842
|
+
let prevPos = 0;
|
|
843
|
+
const result = [];
|
|
844
|
+
for (const item of anno.val) {
|
|
845
|
+
const ell = item && item.literal === 'token' && item.val === '...';
|
|
846
|
+
if (!ell) {
|
|
847
|
+
result.push( item );
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
let upToSpec = item.upTo && checkUpToSpec( item.upTo, art, annoName, true );
|
|
851
|
+
while (prevPos < previousValue.length) {
|
|
852
|
+
const prevItem = previousValue[prevPos++];
|
|
853
|
+
result.push( prevItem );
|
|
854
|
+
if (upToSpec && prevItem && equalUpTo( prevItem, item.upTo)) {
|
|
855
|
+
upToSpec = false;
|
|
856
|
+
break;
|
|
826
857
|
}
|
|
827
858
|
}
|
|
859
|
+
if (upToSpec) { // non-matched UP TO
|
|
860
|
+
warning( null, [ item.upTo.location, art ], { anno: annoName, code: '... up to' },
|
|
861
|
+
'The $(CODE) value does not match any item in the base annotation $(ANNO)' );
|
|
862
|
+
}
|
|
828
863
|
}
|
|
829
|
-
return result;
|
|
830
864
|
}
|
|
865
|
+
// console.log('TP:',previousValue.map(se),anno.val.map(se),'->',result.map(se))
|
|
866
|
+
return { val: result, literal: 'array' };
|
|
867
|
+
}
|
|
868
|
+
// function se(a) { return a.upTo ? [a.val,a.upTo.val] : a.val ; }
|
|
831
869
|
|
|
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') {
|
|
870
|
+
function checkUpToSpec( upToSpec, art, annoName, isFullUpTo ) {
|
|
871
|
+
const { literal } = upToSpec;
|
|
872
|
+
if (!isFullUpTo) { // inside struct of UP TO
|
|
873
|
+
if (literal !== 'struct' && literal !== 'array' )
|
|
842
874
|
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
875
|
}
|
|
876
|
+
else if (literal === 'struct') {
|
|
877
|
+
return Object.values( upToSpec.struct ).every( v => checkUpToSpec( v, art, annoName ) );
|
|
878
|
+
}
|
|
879
|
+
else if (literal !== 'array' && literal !== 'boolean' && literal !== 'null') {
|
|
880
|
+
return true;
|
|
881
|
+
}
|
|
882
|
+
error( null, [ upToSpec.location, art ],
|
|
883
|
+
{ anno: annoName, code: '... up to', '#': literal },
|
|
884
|
+
{
|
|
885
|
+
std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
|
|
886
|
+
array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
|
|
887
|
+
// eslint-disable-next-line max-len
|
|
888
|
+
struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
|
|
889
|
+
boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
|
|
890
|
+
null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
|
|
891
|
+
} );
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
856
894
|
|
|
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
|
-
}
|
|
895
|
+
function equalUpTo( previousItem, upToSpec ) {
|
|
896
|
+
if (!previousItem)
|
|
880
897
|
return false;
|
|
898
|
+
if ('val' in upToSpec) {
|
|
899
|
+
if (previousItem.val === upToSpec.val) // enum, struct and ref have no val
|
|
900
|
+
return true;
|
|
901
|
+
const typeUpTo = typeof upToSpec.val;
|
|
902
|
+
const typePrev = typeof previousItem.val;
|
|
903
|
+
if (typeUpTo === 'number')
|
|
904
|
+
return typePrev === 'string' && previousItem.val === upToSpec.val.toString();
|
|
905
|
+
if (typePrev === 'number')
|
|
906
|
+
return typeUpTo === 'string' && upToSpec.val === previousItem.val.toString();
|
|
881
907
|
}
|
|
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;
|
|
908
|
+
else if (upToSpec.path) {
|
|
909
|
+
return previousItem.path && normalizeRef( previousItem ) === normalizeRef( upToSpec );
|
|
901
910
|
}
|
|
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
|
|
911
|
+
else if (upToSpec.sym) {
|
|
912
|
+
return previousItem.sym && previousItem.sym.id === upToSpec.sym.id;
|
|
930
913
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
return layer && layer._layerExtends;
|
|
914
|
+
else if (upToSpec.struct && previousItem.struct) {
|
|
915
|
+
return Object.entries( upToSpec.struct )
|
|
916
|
+
.every( ([ n, v ]) => equalUpTo( previousItem.struct[n], v ) );
|
|
935
917
|
}
|
|
918
|
+
return false;
|
|
936
919
|
}
|
|
937
920
|
|
|
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;
|
|
921
|
+
function normalizeRef( node ) { // see to-csn.js
|
|
922
|
+
const ref = pathName( node.path );
|
|
923
|
+
return node.variant ? `${ ref }#${ node.variant.id }` : ref;
|
|
952
924
|
}
|
|
953
925
|
|
|
954
926
|
// Phase 4 - queries and associations --------------------------------------
|