@sap/cds-compiler 4.2.4 → 4.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/bin/cdsc.js +8 -0
- package/bin/cdshi.js +3 -3
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +19 -0
- package/lib/base/location.js +16 -0
- package/lib/base/message-registry.js +47 -16
- package/lib/base/messages.js +49 -38
- package/lib/base/model.js +1 -1
- package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
- package/lib/checks/existsMustEndInAssoc.js +27 -0
- package/lib/checks/onConditions.js +47 -1
- package/lib/checks/validator.js +10 -1
- package/lib/compiler/assert-consistency.js +23 -15
- package/lib/compiler/base.js +31 -14
- package/lib/compiler/builtins.js +21 -20
- package/lib/compiler/checks.js +36 -49
- package/lib/compiler/define.js +71 -91
- package/lib/compiler/extend.js +27 -25
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +67 -87
- package/lib/compiler/kick-start.js +9 -5
- package/lib/compiler/populate.js +32 -30
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +29 -25
- package/lib/compiler/shared.js +57 -31
- package/lib/compiler/tweak-assocs.js +203 -22
- package/lib/compiler/utils.js +0 -18
- package/lib/gen/Dictionary.json +10 -4
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/languageParser.js +3 -3
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +63 -28
- package/lib/json/to-csn.js +23 -13
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/errorStrategy.js +5 -1
- package/lib/language/genericAntlrParser.js +67 -61
- package/lib/main.d.ts +26 -1
- package/lib/main.js +2 -1
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +28 -0
- package/lib/model/revealInternalProperties.js +3 -9
- package/lib/optionProcessor.js +17 -1
- package/lib/render/toCdl.js +1 -1
- package/lib/transform/db/associations.js +3 -4
- package/lib/transform/db/backlinks.js +293 -0
- package/lib/transform/db/expansion.js +9 -7
- package/lib/transform/db/flattening.js +3 -2
- package/lib/transform/db/rewriteCalculatedElements.js +1 -67
- package/lib/transform/db/transformExists.js +3 -58
- package/lib/transform/db/views.js +8 -14
- package/lib/transform/effective/.eslintrc.json +4 -0
- package/lib/transform/effective/associations.js +101 -0
- package/lib/transform/effective/main.js +88 -0
- package/lib/transform/effective/misc.js +61 -0
- package/lib/transform/effective/queries.js +42 -0
- package/lib/transform/effective/types.js +121 -0
- package/lib/transform/forRelationalDB.js +12 -235
- package/lib/transform/localized.js +22 -3
- package/lib/transform/parseExpr.js +7 -3
- package/lib/transform/transformUtils.js +5 -22
- package/lib/transform/translateAssocsToJoins.js +44 -39
- package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
- package/package.json +1 -2
- package/lib/language/language.g4 +0 -3260
package/lib/compiler/checks.js
CHANGED
|
@@ -10,16 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
const builtins = require('../compiler/builtins');
|
|
14
13
|
const {
|
|
15
14
|
forEachGeneric,
|
|
16
15
|
forEachDefinition,
|
|
17
16
|
forEachMember,
|
|
18
17
|
forEachMemberRecursively,
|
|
19
|
-
isBetaEnabled,
|
|
20
18
|
} = require('../base/model');
|
|
21
19
|
const { CompilerAssertion } = require('../base/error');
|
|
22
|
-
const { pathName } = require('./utils');
|
|
23
20
|
const { typeParameters } = require('./builtins');
|
|
24
21
|
|
|
25
22
|
const $location = Symbol.for( 'cds.$location' );
|
|
@@ -94,8 +91,8 @@ function check( model ) {
|
|
|
94
91
|
const isKey = parentProps.key?.val || elem.key?.val;
|
|
95
92
|
const isVirtual = parentProps.virtual?.val || elem.virtual?.val;
|
|
96
93
|
if (isKey && isVirtual) {
|
|
97
|
-
error( 'def-unexpected-key', [
|
|
98
|
-
{ '#': 'virtual',
|
|
94
|
+
error( 'def-unexpected-key', [ (parentProps.key || elem.key).location, elem ],
|
|
95
|
+
{ '#': 'virtual', prop: 'key' } );
|
|
99
96
|
}
|
|
100
97
|
}
|
|
101
98
|
|
|
@@ -116,7 +113,7 @@ function check( model ) {
|
|
|
116
113
|
|
|
117
114
|
|
|
118
115
|
function checkName( construct ) { // TODO: move to define.js
|
|
119
|
-
if (model.options.$skipNameCheck)
|
|
116
|
+
if (model.options.$skipNameCheck || !construct._main)
|
|
120
117
|
return;
|
|
121
118
|
// TODO: Move a corrected version of this check to definer (but do not rely on it!):
|
|
122
119
|
// The code below misses to consider CSN input!
|
|
@@ -463,15 +460,7 @@ function check( model ) {
|
|
|
463
460
|
}
|
|
464
461
|
}
|
|
465
462
|
if (elem.default) {
|
|
466
|
-
if (
|
|
467
|
-
error( 'type-unsupported-default', [ elem.default.location, elem ], {
|
|
468
|
-
'#': isComposition( model, elem ) ? 'comp' : 'std',
|
|
469
|
-
}, {
|
|
470
|
-
std: 'Unsupported default value on an association',
|
|
471
|
-
comp: 'Unsupported default value on a composition',
|
|
472
|
-
} );
|
|
473
|
-
}
|
|
474
|
-
else if (elem.targetAspect || elem.on || fkCount !== 1) {
|
|
463
|
+
if (elem.targetAspect || elem.on || fkCount !== 1) {
|
|
475
464
|
const variant = (elem.targetAspect && 'targetAspect') || (elem.on && 'onCond') || 'multi';
|
|
476
465
|
error( 'type-unexpected-default', [ elem.default.location, elem ], {
|
|
477
466
|
'#': variant, keyword: 'default', count: fkCount,
|
|
@@ -628,10 +617,9 @@ function check( model ) {
|
|
|
628
617
|
|
|
629
618
|
function checkSelectItemValue( elem ) {
|
|
630
619
|
checkExpressionAssociationUsage( elem.value, elem, false );
|
|
631
|
-
//
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
if (elem.value?.op?.val === 'cast' && !elem.type) {
|
|
620
|
+
// To avoid duplicate messages, only run this check if the type wasn't inferred from
|
|
621
|
+
// the cast, as otherwise we will check it twice (once here, once via element).
|
|
622
|
+
if (elem.value?.op?.val === 'cast' && elem.type?.$inferred !== 'cast') {
|
|
635
623
|
requireExplicitTypeInSqlCast( elem.value, elem );
|
|
636
624
|
checkTypeArguments( elem.value, elem );
|
|
637
625
|
}
|
|
@@ -813,18 +801,24 @@ function check( model ) {
|
|
|
813
801
|
// definitions from 'model'. Report errors on 'options.messages.
|
|
814
802
|
//
|
|
815
803
|
// TODO: rework completely!
|
|
804
|
+
// TODO: if we have such a check, consider #variant, anno.@anno, anno@anno
|
|
816
805
|
|
|
817
806
|
// Has been slightly adapted for model.vocabularies but comments need to be
|
|
818
807
|
// adapted, etc.
|
|
819
808
|
function checkAnnotationAssignment1( art, anno ) {
|
|
820
809
|
// Sanity checks (ignore broken assignments)
|
|
821
|
-
if (!anno.name?.
|
|
810
|
+
if (!anno.name?.id)
|
|
822
811
|
return;
|
|
812
|
+
// Just a little workaround to adapt to changed `name`s, not nice coding:
|
|
813
|
+
const hashIndex = anno.name.id.indexOf( '#' );
|
|
814
|
+
const path = (hashIndex > 0 ? anno.name.id.substring( 0, hashIndex ) : anno.name.id)
|
|
815
|
+
.split( '.' ).map( id => ({ id }) );
|
|
816
|
+
|
|
823
817
|
// Annotation artifact for longest path step of annotation path
|
|
824
818
|
let fromArtifact = null;
|
|
825
819
|
let pathStepsFound = 0;
|
|
826
|
-
for (let i =
|
|
827
|
-
const absoluteName =
|
|
820
|
+
for (let i = path.length; i > 0; i--) {
|
|
821
|
+
const absoluteName = path.slice( 0, i ).map( p => p.id ).join( '.' );
|
|
828
822
|
if (model.vocabularies[absoluteName]) {
|
|
829
823
|
fromArtifact = model.vocabularies[absoluteName];
|
|
830
824
|
pathStepsFound = i;
|
|
@@ -837,7 +831,7 @@ function check( model ) {
|
|
|
837
831
|
return;
|
|
838
832
|
}
|
|
839
833
|
|
|
840
|
-
const { artifact, endOfPath } = resolvePathFrom(
|
|
834
|
+
const { artifact, endOfPath } = resolvePathFrom( path.slice( pathStepsFound ),
|
|
841
835
|
fromArtifact );
|
|
842
836
|
|
|
843
837
|
// Check what we actually want to check
|
|
@@ -859,7 +853,7 @@ function check( model ) {
|
|
|
859
853
|
// Element must exist in annotation
|
|
860
854
|
if (!elementDecl) {
|
|
861
855
|
warning( null, [ anno.location || anno.name.location, art ],
|
|
862
|
-
{ name:
|
|
856
|
+
{ name: anno.name.id, anno: annoDecl.name.id },
|
|
863
857
|
'Element $(NAME) not found for annotation $(ANNO)' );
|
|
864
858
|
return;
|
|
865
859
|
}
|
|
@@ -870,14 +864,14 @@ function check( model ) {
|
|
|
870
864
|
|
|
871
865
|
|
|
872
866
|
// Must have literal or path unless it is a boolean
|
|
873
|
-
if (!anno.literal && !anno.path &&
|
|
874
|
-
if (elementDecl.type?._artifact
|
|
867
|
+
if (!anno.literal && !anno.path && elementDecl._effectiveType?.category !== 'boolean') {
|
|
868
|
+
if (elementDecl.type?._artifact) {
|
|
875
869
|
warning( 'anno-expecting-value', [ anno.location || anno.name.location, art ],
|
|
876
870
|
{ '#': 'type', type: elementDecl.type._artifact } );
|
|
877
871
|
}
|
|
878
872
|
else {
|
|
879
873
|
warning( 'anno-expecting-value', [ anno.location || anno.name.location, art ],
|
|
880
|
-
{ '#': 'std', anno: anno.name.
|
|
874
|
+
{ '#': 'std', anno: anno.name.id } );
|
|
881
875
|
}
|
|
882
876
|
|
|
883
877
|
return;
|
|
@@ -896,7 +890,7 @@ function check( model ) {
|
|
|
896
890
|
if (value.path)
|
|
897
891
|
return;
|
|
898
892
|
|
|
899
|
-
const anno = annoDef.name.
|
|
893
|
+
const anno = annoDef.name.id;
|
|
900
894
|
const loc = [ value.location || value.name.location, art ];
|
|
901
895
|
|
|
902
896
|
// Array expected?
|
|
@@ -925,47 +919,51 @@ function check( model ) {
|
|
|
925
919
|
|
|
926
920
|
// Handle each (primitive) expected element type separately
|
|
927
921
|
// TODO: Don't rely on name; use actual type
|
|
928
|
-
const type =
|
|
929
|
-
if (
|
|
922
|
+
const type = elementDecl._effectiveType;
|
|
923
|
+
if (!type)
|
|
924
|
+
return;
|
|
925
|
+
if (type.category === 'string') {
|
|
930
926
|
if (value.literal !== 'string' && value.literal !== 'enum' &&
|
|
931
927
|
!elementDecl._effectiveType.enum) {
|
|
932
928
|
warning( null, loc, { type, anno },
|
|
933
929
|
'A string value is required for type $(TYPE) for annotation $(ANNO)' );
|
|
934
930
|
}
|
|
935
931
|
}
|
|
936
|
-
else if (
|
|
932
|
+
else if (type.category === 'binary') {
|
|
937
933
|
if (value.literal !== 'string' && value.literal !== 'x') {
|
|
938
934
|
warning( null, loc, { type, anno },
|
|
939
935
|
'A hexadecimal string value is required for type $(TYPE) for annotation $(ANNO)' );
|
|
940
936
|
}
|
|
941
937
|
}
|
|
942
|
-
else if (
|
|
938
|
+
else if (type.category === 'decimal' || type.category === 'integer') {
|
|
943
939
|
if (value.literal !== 'number' && value.literal !== 'enum' &&
|
|
944
940
|
!elementDecl._effectiveType.enum) {
|
|
945
941
|
warning( null, loc, { type, anno },
|
|
946
942
|
'A numerical value is required for type $(TYPE) for annotation $(ANNO)' );
|
|
947
943
|
}
|
|
948
944
|
}
|
|
949
|
-
else if (
|
|
945
|
+
else if (type.category === 'dateTime') {
|
|
950
946
|
if (value.literal !== 'date' && value.literal !== 'time' &&
|
|
951
947
|
value.literal !== 'timestamp' && value.literal !== 'string') {
|
|
948
|
+
// Hm, actually date and time cannot be mixed
|
|
952
949
|
warning( null, loc, { type, anno },
|
|
953
950
|
// eslint-disable-next-line max-len
|
|
954
951
|
'A date/time value or a string is required for type $(TYPE) for annotation $(ANNO)' );
|
|
955
952
|
}
|
|
956
953
|
}
|
|
957
|
-
else if (
|
|
954
|
+
else if (type.category === 'boolean') {
|
|
958
955
|
if (value.literal && value.literal !== 'boolean') {
|
|
959
956
|
warning( null, loc, { type, anno },
|
|
960
957
|
'A boolean value is required for type $(TYPE) for annotation $(ANNO)' );
|
|
961
958
|
}
|
|
962
959
|
}
|
|
963
|
-
else if (
|
|
964
|
-
warning( null, loc, { type, anno },
|
|
960
|
+
else if (type.target || type.category === 'geo') {
|
|
961
|
+
warning( null, loc, { type: (type.target ? 'cds.Association' : type), anno },
|
|
965
962
|
'Type $(TYPE) can\'t be assigned a value for annotation $(ANNO)' );
|
|
963
|
+
// TODO: complain at definition instead
|
|
966
964
|
}
|
|
967
|
-
else if (!
|
|
968
|
-
throw new CompilerAssertion(`Unknown primitive type name: ${ type }`);
|
|
965
|
+
else if (!type.enum) {
|
|
966
|
+
throw new CompilerAssertion(`Unknown primitive type name: ${ type.name.id }`);
|
|
969
967
|
}
|
|
970
968
|
|
|
971
969
|
// Check enums
|
|
@@ -1027,17 +1025,6 @@ function check( model ) {
|
|
|
1027
1025
|
from._effectiveType?.elements || [];
|
|
1028
1026
|
return resolvePathFrom( path.slice(1), nextStepEnv[path[0].id], result );
|
|
1029
1027
|
}
|
|
1030
|
-
|
|
1031
|
-
// TODO: remove
|
|
1032
|
-
// Return the absolute name of the final type of 'node'. May return 'undefined'
|
|
1033
|
-
// for anonymous types. DO NOT USE THIS function, it has several assumptions
|
|
1034
|
-
// which are not necessarily true.
|
|
1035
|
-
function getFinalTypeNameOf( node ) {
|
|
1036
|
-
let type = node._effectiveType;
|
|
1037
|
-
if (type.type)
|
|
1038
|
-
type = type.type._artifact;
|
|
1039
|
-
return type?.name?.absolute;
|
|
1040
|
-
}
|
|
1041
1028
|
}
|
|
1042
1029
|
|
|
1043
1030
|
/**
|
package/lib/compiler/define.js
CHANGED
|
@@ -124,6 +124,7 @@ const {
|
|
|
124
124
|
forEachInOrder,
|
|
125
125
|
forEachMember,
|
|
126
126
|
} = require('../base/model');
|
|
127
|
+
const { weakLocation } = require('../base/location');
|
|
127
128
|
const shuffleGen = require('../base/shuffle');
|
|
128
129
|
const {
|
|
129
130
|
dictAdd, dictAddArray, dictForEach, pushToDict,
|
|
@@ -135,7 +136,6 @@ const {
|
|
|
135
136
|
storeExtension,
|
|
136
137
|
dependsOnSilent,
|
|
137
138
|
pathName,
|
|
138
|
-
splitIntoPath,
|
|
139
139
|
isDirectComposition,
|
|
140
140
|
} = require('./utils');
|
|
141
141
|
const { compareLayer } = require('./moduleLayers');
|
|
@@ -226,8 +226,12 @@ function define( model ) {
|
|
|
226
226
|
if (!src.kind)
|
|
227
227
|
src.kind = 'source';
|
|
228
228
|
|
|
229
|
-
let namespace = src
|
|
230
|
-
let prefix =
|
|
229
|
+
let { namespace } = src;
|
|
230
|
+
let prefix = '';
|
|
231
|
+
if (namespace?.path && !namespace.path.broken) {
|
|
232
|
+
namespace.id = pathName( namespace.path );
|
|
233
|
+
prefix = `${ namespace.id }.`;
|
|
234
|
+
}
|
|
231
235
|
if (isInReservedNamespace( prefix )) {
|
|
232
236
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], { name: 'cds' },
|
|
233
237
|
'The namespace $(NAME) is reserved for CDS builtins' );
|
|
@@ -240,12 +244,12 @@ function define( model ) {
|
|
|
240
244
|
src.artifacts = shuffleDict( src.artifacts );
|
|
241
245
|
addPathPrefixes( src.artifacts, prefix ); // before addUsing
|
|
242
246
|
}
|
|
243
|
-
else if (src.usings ||
|
|
247
|
+
else if (src.usings || namespace) {
|
|
244
248
|
src.artifacts = Object.create( null );
|
|
245
249
|
}
|
|
246
250
|
if (src.usings)
|
|
247
251
|
shuffleArray( src.usings ).forEach( u => addUsing( u, src ) );
|
|
248
|
-
if (namespace)
|
|
252
|
+
if (namespace?.id) // successfully set a full name for namespace
|
|
249
253
|
addNamespace( namespace, src );
|
|
250
254
|
if (src.artifacts) { // addArtifact needs usings for context extensions
|
|
251
255
|
src.artifacts = shuffleDict( src.artifacts );
|
|
@@ -253,7 +257,7 @@ function define( model ) {
|
|
|
253
257
|
}
|
|
254
258
|
}
|
|
255
259
|
else if (src.definitions) { // CSN input
|
|
256
|
-
prefix = '';
|
|
260
|
+
prefix = ''; // also for addVocabulary() below
|
|
257
261
|
dictForEach( shuffleDict( src.definitions ), def => addDefinition( def, src, prefix ) );
|
|
258
262
|
}
|
|
259
263
|
if (src.vocabularies) {
|
|
@@ -267,11 +271,8 @@ function define( model ) {
|
|
|
267
271
|
}
|
|
268
272
|
|
|
269
273
|
function addDefinition( art, block, prefix ) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
art.name.absolute = (!art.name.path) ? art.name.id : prefix + pathName( art.name.path );
|
|
273
|
-
}
|
|
274
|
-
const { absolute } = art.name;
|
|
274
|
+
art.name.id ??= prefix + pathName( art.name.path );
|
|
275
|
+
const absolute = art.name.id;
|
|
275
276
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
276
277
|
if (absolute === 'cds' || isInReservedNamespace( absolute )) {
|
|
277
278
|
error( 'reserved-namespace-cds', [ art.name.location, art ], { name: 'cds' },
|
|
@@ -299,24 +300,21 @@ function define( model ) {
|
|
|
299
300
|
for (const name in artifacts) {
|
|
300
301
|
const d = artifacts[name];
|
|
301
302
|
const a = Array.isArray( d ) ? d[0] : d;
|
|
302
|
-
|
|
303
|
-
a.name.absolute = prefix + name;
|
|
303
|
+
a.name.id ??= prefix + pathName( a.name.path );
|
|
304
304
|
const index = name.indexOf( '.' );
|
|
305
305
|
if (index < 0)
|
|
306
306
|
continue; // also for newly added (i.e. does not matter whether visited or not)
|
|
307
|
-
const
|
|
308
|
-
if (artifacts[
|
|
307
|
+
const using = name.substring( 0, index );
|
|
308
|
+
if (artifacts[using])
|
|
309
309
|
continue;
|
|
310
310
|
// TODO: enable optional locations
|
|
311
311
|
const location = a.name.path && a.name.path[0].location || a.location;
|
|
312
|
-
const absolute = prefix +
|
|
313
|
-
artifacts[
|
|
312
|
+
const absolute = prefix + using;
|
|
313
|
+
artifacts[using] = {
|
|
314
314
|
kind: 'using', // !, not namespace - we do not know artifact yet
|
|
315
|
-
name: {
|
|
316
|
-
id, absolute, location, $inferred: 'as',
|
|
317
|
-
},
|
|
315
|
+
name: { id: using, location, $inferred: 'as' },
|
|
318
316
|
// TODO: use global ref (in general - all uses of splitIntoPath)
|
|
319
|
-
extern: {
|
|
317
|
+
extern: { location, id: absolute },
|
|
320
318
|
location,
|
|
321
319
|
$inferred: 'path-prefix',
|
|
322
320
|
};
|
|
@@ -342,37 +340,33 @@ function define( model ) {
|
|
|
342
340
|
const { path } = decl.extern;
|
|
343
341
|
if (path.broken || !path[0]) // syntax error
|
|
344
342
|
return;
|
|
343
|
+
decl.extern.id = pathName( path );
|
|
345
344
|
if (!decl.name)
|
|
346
345
|
decl.name = { ...path[path.length - 1], $inferred: 'as' };
|
|
347
|
-
decl.name.absolute = pathName( path );
|
|
348
346
|
const name = decl.name.id;
|
|
349
347
|
// TODO: check name: no "."
|
|
350
348
|
const found = src.artifacts[name];
|
|
351
|
-
|
|
352
|
-
|
|
349
|
+
// a real `using` declaration is “nicer” than a compiler-generated one:
|
|
350
|
+
if (found && found.$inferred === 'path-prefix' && found.extern.id === decl.extern.id)
|
|
353
351
|
src.artifacts[name] = decl;
|
|
354
352
|
else
|
|
355
353
|
dictAddArray( src.artifacts, name, decl );
|
|
356
354
|
}
|
|
357
355
|
|
|
358
356
|
// must be called after addUsing().
|
|
359
|
-
function addNamespace(
|
|
360
|
-
const absolute = pathName( path );
|
|
361
|
-
if (path.broken) // parsing may have failed
|
|
362
|
-
return;
|
|
357
|
+
function addNamespace( namespace, src ) {
|
|
363
358
|
// create using for own namespace:
|
|
364
|
-
|
|
359
|
+
// TODO: should we really do that in v5? See also initNamespaceAndUsing().
|
|
360
|
+
const last = namespace.path[namespace.path.length - 1];
|
|
365
361
|
const { id } = last;
|
|
366
362
|
if (src.artifacts[id] || last.id.includes( '.' ))
|
|
367
363
|
// not used as we have a definition/using with that name, or dotted last path id
|
|
368
364
|
return;
|
|
369
365
|
src.artifacts[id] = {
|
|
370
366
|
kind: 'using',
|
|
371
|
-
name: {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
extern: src.namespace,
|
|
375
|
-
location: src.namespace.location,
|
|
367
|
+
name: { id, location: last.location, $inferred: 'as' },
|
|
368
|
+
extern: namespace,
|
|
369
|
+
location: namespace.location,
|
|
376
370
|
$inferred: 'namespace',
|
|
377
371
|
};
|
|
378
372
|
}
|
|
@@ -381,7 +375,7 @@ function define( model ) {
|
|
|
381
375
|
return;
|
|
382
376
|
addDefinition( art, block, prefix );
|
|
383
377
|
if (art.artifacts) {
|
|
384
|
-
const p = `${ art.name.
|
|
378
|
+
const p = `${ art.name.id }.`;
|
|
385
379
|
// path prefixes (usings) must be added before extensions in artifacts:
|
|
386
380
|
addPathPrefixes( art.artifacts, p );
|
|
387
381
|
dictForEach( art.artifacts, a => addArtifact( a, art, p ) );
|
|
@@ -397,12 +391,12 @@ function define( model ) {
|
|
|
397
391
|
if (!absolute) // broken path
|
|
398
392
|
return;
|
|
399
393
|
delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
|
|
400
|
-
ext.name.
|
|
394
|
+
ext.name.id = absolute; // definition might not be there yet, no _artifact link
|
|
401
395
|
const location = { file: '' }; // stupid required location
|
|
402
396
|
const late = model.$collectedExtensions[absolute] ||
|
|
403
397
|
(model.$collectedExtensions[absolute] = {
|
|
404
398
|
kind: 'annotate',
|
|
405
|
-
name: { absolute, location },
|
|
399
|
+
name: { id: absolute, location },
|
|
406
400
|
$inferred: '',
|
|
407
401
|
location,
|
|
408
402
|
});
|
|
@@ -415,7 +409,7 @@ function define( model ) {
|
|
|
415
409
|
model.$blocks = Object.create( null );
|
|
416
410
|
// Set block number for debugging (--raw-output):
|
|
417
411
|
// eslint-disable-next-line no-multi-assign
|
|
418
|
-
ext
|
|
412
|
+
ext.$effectiveSeqNo = model.$blocks[absolute] = (model.$blocks[absolute] || 0) + 1;
|
|
419
413
|
// add "namespace" for the case that ext.artifacts is empty (TODO: later)
|
|
420
414
|
// now add all definitions in ext.artifacts:
|
|
421
415
|
const prefix = `${ absolute }.`;
|
|
@@ -449,13 +443,8 @@ function define( model ) {
|
|
|
449
443
|
function addVocabulary( vocab, block, prefix ) {
|
|
450
444
|
setLink( vocab, '_block', block );
|
|
451
445
|
const { name } = vocab;
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
vocab.name.absolute = (!vocab.name.path)
|
|
455
|
-
? vocab.name.id
|
|
456
|
-
: prefix + pathName( vocab.name.path );
|
|
457
|
-
}
|
|
458
|
-
dictAdd( model.vocabularies, name.absolute, vocab );
|
|
446
|
+
name.id ??= prefix + pathName( name.path );
|
|
447
|
+
dictAdd( model.vocabularies, name.id, vocab );
|
|
459
448
|
}
|
|
460
449
|
|
|
461
450
|
/**
|
|
@@ -509,7 +498,8 @@ function define( model ) {
|
|
|
509
498
|
|
|
510
499
|
// TODO: message ids
|
|
511
500
|
function checkRedefinition( art ) {
|
|
512
|
-
if (!art.$duplicates || art
|
|
501
|
+
if (!art.$duplicates || !art.name.id ||
|
|
502
|
+
art.$errorReported === 'syntax-duplicate-extend' ||
|
|
513
503
|
art.$errorReported === 'syntax-duplicate-annotate')
|
|
514
504
|
return;
|
|
515
505
|
if (art._main) {
|
|
@@ -522,7 +512,7 @@ function define( model ) {
|
|
|
522
512
|
// TODO: better messages with definitions with the same name as builtin,
|
|
523
513
|
// especially if there is just one
|
|
524
514
|
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
525
|
-
name: art.name.
|
|
515
|
+
name: art.name.id,
|
|
526
516
|
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
527
517
|
} );
|
|
528
518
|
}
|
|
@@ -533,24 +523,20 @@ function define( model ) {
|
|
|
533
523
|
return;
|
|
534
524
|
if (src.namespace) {
|
|
535
525
|
const decl = src.namespace;
|
|
536
|
-
|
|
537
|
-
if (path.broken) // parsing may have failed
|
|
526
|
+
if (!decl.id) // parsing may have failed
|
|
538
527
|
return;
|
|
539
|
-
|
|
540
|
-
const absolute = pathName( path );
|
|
541
|
-
if (!model.definitions[absolute]) {
|
|
542
|
-
// TODO: do we really need this namespace entry - try without (msg change)
|
|
543
|
-
const location = path.location || decl.location;
|
|
528
|
+
if (!model.definitions[decl.id]) {
|
|
544
529
|
// TODO: make it possible to have no location
|
|
545
|
-
const ns = { kind: 'namespace', name:
|
|
546
|
-
model.definitions[
|
|
530
|
+
const ns = { kind: 'namespace', name: decl, location: decl.location };
|
|
531
|
+
model.definitions[decl.id] = ns;
|
|
547
532
|
initArtifactParentLink( ns, model.definitions );
|
|
548
533
|
}
|
|
549
|
-
const
|
|
534
|
+
const last = decl.path[decl.path.length - 1];
|
|
535
|
+
const builtin = model.$builtins[last.id];
|
|
550
536
|
if (builtin && !builtin.internal &&
|
|
551
|
-
src.artifacts[id] && src.artifacts[id].extern === decl) {
|
|
537
|
+
src.artifacts[last.id] && src.artifacts[last.id].extern === decl) {
|
|
552
538
|
warning( 'ref-shadowed-builtin', [ decl.location, null ], // no home artifact
|
|
553
|
-
{ id, art:
|
|
539
|
+
{ id: last.id, art: decl.id, code: `using ${ builtin.name.id };` },
|
|
554
540
|
'$(ID) now refers to $(ART) - consider $(CODE)' );
|
|
555
541
|
}
|
|
556
542
|
// setArtifactLink( decl, model.definitions[absolute] ); // TODO: necessary?
|
|
@@ -602,53 +588,51 @@ function define( model ) {
|
|
|
602
588
|
initMembers( art, art, block );
|
|
603
589
|
}
|
|
604
590
|
|
|
605
|
-
function initArtifactParentLink( art, definitions ) {
|
|
591
|
+
function initArtifactParentLink( art, definitions, path, pathIndex ) {
|
|
606
592
|
setLink( art, '_parent', null );
|
|
607
|
-
const {
|
|
608
|
-
const dot =
|
|
593
|
+
const { id } = art.name;
|
|
594
|
+
const dot = id.lastIndexOf( '.' );
|
|
609
595
|
if (dot < 0)
|
|
610
596
|
return;
|
|
611
|
-
|
|
612
|
-
const prefix = absolute.substring( 0, dot );
|
|
597
|
+
const prefix = id.substring( 0, dot );
|
|
613
598
|
let parent = definitions[prefix];
|
|
614
599
|
if (!parent) {
|
|
615
|
-
|
|
616
|
-
|
|
600
|
+
path ??= art.name.path;
|
|
601
|
+
pathIndex ??= path?.length - 1;
|
|
602
|
+
const pathItemOrName = (path && pathIndex) ? path[--pathIndex] : art.name;
|
|
603
|
+
const location = weakLocation( pathItemOrName.location );
|
|
604
|
+
parent = { kind: 'namespace', name: { id: prefix, location }, location };
|
|
617
605
|
definitions[prefix] = parent;
|
|
618
|
-
initArtifactParentLink( parent, definitions );
|
|
606
|
+
initArtifactParentLink( parent, definitions, path, pathIndex );
|
|
619
607
|
}
|
|
620
608
|
setLink( art, '_parent', parent );
|
|
621
609
|
if (!parent._subArtifacts)
|
|
622
610
|
setLink( parent, '_subArtifacts', Object.create( null ) );
|
|
623
611
|
if (art.$duplicates !== true) // no redef or "first def"
|
|
624
|
-
parent._subArtifacts[
|
|
612
|
+
parent._subArtifacts[id.substring( dot + 1 )] = art; // not dictAdd()
|
|
625
613
|
}
|
|
626
614
|
|
|
627
615
|
// Init special things: -------------------------------------------------------
|
|
628
616
|
|
|
629
617
|
function initDollarSelf( art ) {
|
|
630
|
-
const selfname = '$self';
|
|
631
618
|
// TODO: use setMemberParent() ?
|
|
632
|
-
const name = art.name || art._outer.name;
|
|
633
619
|
const self = {
|
|
634
|
-
name: { id:
|
|
620
|
+
name: { id: '$self', location: art.location },
|
|
635
621
|
kind: '$self',
|
|
636
622
|
location: art.location,
|
|
637
623
|
};
|
|
638
|
-
if (name.element) // $self for anonymous aspect
|
|
639
|
-
self.name.element = name.element;
|
|
640
624
|
setLink( self, '_parent', art );
|
|
641
625
|
setLink( self, '_main', art ); // used on main artifact
|
|
642
626
|
setLink( self, '_origin', art );
|
|
643
627
|
art.$tableAliases = Object.create( null );
|
|
644
|
-
art.$tableAliases
|
|
628
|
+
art.$tableAliases.$self = self;
|
|
645
629
|
}
|
|
646
630
|
|
|
647
631
|
function initDollarParameters( art ) {
|
|
648
|
-
// TODO: remove $parameters in
|
|
632
|
+
// TODO: remove $parameters in v5?
|
|
649
633
|
// TODO: use setMemberParent() ?
|
|
650
634
|
const parameters = {
|
|
651
|
-
name: { id: '$parameters'
|
|
635
|
+
name: { id: '$parameters' },
|
|
652
636
|
kind: '$parameters',
|
|
653
637
|
location: art.location,
|
|
654
638
|
};
|
|
@@ -687,7 +671,7 @@ function define( model ) {
|
|
|
687
671
|
initMixins( query, art );
|
|
688
672
|
if (!query.$tableAliases.$self) { // same as $projection
|
|
689
673
|
const self = {
|
|
690
|
-
name: {
|
|
674
|
+
name: { id: '$self', location: query.location },
|
|
691
675
|
kind: '$self',
|
|
692
676
|
location: query.location,
|
|
693
677
|
};
|
|
@@ -727,10 +711,9 @@ function define( model ) {
|
|
|
727
711
|
(art.kind === '$tableAlias' ? art._parent._$next : art ) );
|
|
728
712
|
setLink( query, '_block', art._block );
|
|
729
713
|
query.kind = 'select';
|
|
730
|
-
query.name = { location: query.location };
|
|
731
|
-
setMemberParent( query,
|
|
714
|
+
query.name = { location: query.location, id: main.$queries.length + 1 };
|
|
715
|
+
setMemberParent( query, null, main );
|
|
732
716
|
// console.log(art.kind,art.name,query.name,query._$next.name)
|
|
733
|
-
// if (query.name.query === 1 && query.name.absolute === 'S') throw new CompilerAssertion();
|
|
734
717
|
main.$queries.push( query );
|
|
735
718
|
setLink( query, '_parent', art ); // _parent should point to alias/main/query
|
|
736
719
|
query.$tableAliases = Object.create( null ); // table aliases and mixin definitions
|
|
@@ -794,13 +777,13 @@ function define( model ) {
|
|
|
794
777
|
} );
|
|
795
778
|
}
|
|
796
779
|
if (table.on) { // after processing args to get the $tableAliases
|
|
797
|
-
setMemberParent( table, query.name.
|
|
780
|
+
setMemberParent( table, query.name.id, query ); // sets _parent,_main
|
|
798
781
|
initSubQuery( table ); // init sub queries in ON
|
|
799
782
|
const aliases = Object.keys( table.$tableAliases || {} );
|
|
800
783
|
// Use first tabalias name on the right side of the join to name the
|
|
801
784
|
// (internal) query, should only be relevant for --raw-output, not for
|
|
802
785
|
// user messages or references - TODO: correct if join on left?
|
|
803
|
-
table.name.
|
|
786
|
+
table.name.id = aliases[1] || aliases[0] || '<unknown>';
|
|
804
787
|
setLink( table, '_user', query ); // TODO: do not set kind/name
|
|
805
788
|
setLink( table, '_$next', query._$next );
|
|
806
789
|
// TODO: probably set this to query if we switch to name restriction in JOIN
|
|
@@ -815,6 +798,8 @@ function define( model ) {
|
|
|
815
798
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc, tableAlias ) => {
|
|
816
799
|
if (tableAlias.$inferred === '$internal') {
|
|
817
800
|
const semanticLoc = tableAlias.query?.name ? tableAlias.query : tableAlias;
|
|
801
|
+
// TODO: the semanticLoc query is not initialized yet, and thus cannot
|
|
802
|
+
// be used here
|
|
818
803
|
error( 'name-missing-alias', [ tableAlias.location, semanticLoc ],
|
|
819
804
|
{ '#': 'duplicate', code: 'as ‹alias›' } );
|
|
820
805
|
}
|
|
@@ -878,7 +863,6 @@ function define( model ) {
|
|
|
878
863
|
const mixin = query.mixin[name];
|
|
879
864
|
if (!(mixin.$duplicates)) {
|
|
880
865
|
setMemberParent( mixin, name, query );
|
|
881
|
-
mixin.name.alias = mixin.name.id;
|
|
882
866
|
setLink( mixin, '_block', art._block );
|
|
883
867
|
// TODO: do some initMembers() ? If people had annotation
|
|
884
868
|
// assignments on the mixin... (also for future mixin definitions
|
|
@@ -1121,12 +1105,7 @@ function define( model ) {
|
|
|
1121
1105
|
// another entity - might induce auto-redirection):
|
|
1122
1106
|
if (inEntity && !targetAspect.elements.up_) {
|
|
1123
1107
|
const up = {
|
|
1124
|
-
name: {
|
|
1125
|
-
id: 'up_',
|
|
1126
|
-
alias: 'up_',
|
|
1127
|
-
element: obj.name.element,
|
|
1128
|
-
absolute: obj.name.absolute,
|
|
1129
|
-
},
|
|
1108
|
+
name: { id: 'up_' },
|
|
1130
1109
|
kind: '$navElement',
|
|
1131
1110
|
location: obj.location,
|
|
1132
1111
|
};
|
|
@@ -1200,13 +1179,14 @@ function define( model ) {
|
|
|
1200
1179
|
if (boundSelfParamType === true) { // first try
|
|
1201
1180
|
const def = model.definitions.$self;
|
|
1202
1181
|
if (def) {
|
|
1203
|
-
// TODO
|
|
1182
|
+
// TODO v5: bring this always, probably even as error
|
|
1204
1183
|
warning( 'name-deprecated-for-artifacts', [ def.location, def ], { name: '$self' },
|
|
1205
1184
|
'Do not use $(NAME) as name for an artifact definition' );
|
|
1206
1185
|
boundSelfParamType = false;
|
|
1207
1186
|
return;
|
|
1208
1187
|
}
|
|
1209
|
-
|
|
1188
|
+
const location = { file: '' };
|
|
1189
|
+
boundSelfParamType = { name: { id: '$self', location }, location };
|
|
1210
1190
|
}
|
|
1211
1191
|
const first = params[Object.keys( params )[0] || ''];
|
|
1212
1192
|
const type = first?.type || first?.items?.type; // this sequence = no derived type
|