@sap/cds-compiler 2.10.4 → 2.12.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 +136 -0
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +58 -35
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +16 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +10 -36
- package/lib/api/options.js +17 -8
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +64 -11
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +6 -4
- package/lib/base/optionProcessorHelper.js +148 -86
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/sql-snippets.js +93 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/checks/validator.js +8 -0
- package/lib/compiler/assert-consistency.js +14 -5
- package/lib/compiler/base.js +64 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +34 -10
- package/lib/compiler/definer.js +91 -112
- package/lib/compiler/index.js +30 -30
- package/lib/compiler/propagator.js +8 -4
- package/lib/compiler/resolver.js +279 -63
- package/lib/compiler/shared.js +65 -230
- package/lib/compiler/utils.js +191 -0
- package/lib/edm/annotations/genericTranslation.js +35 -18
- package/lib/edm/annotations/preprocessAnnotations.js +1 -1
- package/lib/edm/csn2edm.js +4 -3
- package/lib/edm/edm.js +8 -8
- package/lib/edm/edmPreprocessor.js +61 -59
- package/lib/edm/edmUtils.js +14 -15
- package/lib/gen/Dictionary.json +82 -40
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +19 -1
- package/lib/gen/language.tokens +80 -73
- package/lib/gen/languageLexer.interp +27 -1
- package/lib/gen/languageLexer.js +925 -826
- package/lib/gen/languageLexer.tokens +72 -65
- package/lib/gen/languageParser.js +4817 -4102
- package/lib/json/from-csn.js +57 -26
- package/lib/json/to-csn.js +244 -51
- package/lib/language/antlrParser.js +12 -1
- package/lib/language/docCommentParser.js +1 -1
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +106 -30
- package/lib/language/language.g4 +200 -70
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +220 -21
- package/lib/main.js +6 -3
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +218 -86
- package/lib/model/csnUtils.js +99 -178
- package/lib/model/enrichCsn.js +84 -43
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +2 -2
- package/lib/render/manageConstraints.js +1 -1
- package/lib/render/toCdl.js +202 -82
- package/lib/render/toHdbcds.js +194 -135
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +91 -51
- package/lib/render/utils/common.js +24 -5
- package/lib/render/utils/sql.js +6 -4
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/associations.js +389 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +275 -119
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +10 -9
- package/lib/transform/db/flattening.js +23 -8
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +106 -25
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +90 -1036
- package/lib/transform/forOdataNew.js +11 -3
- package/lib/transform/localized.js +5 -14
- package/lib/transform/odata/generateForeignKeyElements.js +2 -2
- package/lib/transform/transformUtilsNew.js +34 -20
- package/lib/transform/translateAssocsToJoins.js +15 -23
- package/lib/transform/universalCsnEnricher.js +217 -47
- package/lib/utils/file.js +13 -6
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +55 -27
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/compiler/shared.js
CHANGED
|
@@ -1,86 +1,32 @@
|
|
|
1
1
|
// Compiler functions and utilities shared across all phases
|
|
2
|
-
|
|
2
|
+
// TODO: rename to paths.js and move non resolve-paths functions to somewhere else
|
|
3
3
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
const { searchName } = require('../base/messages');
|
|
7
|
-
const {
|
|
7
|
+
const { dictAddArray } = require('../base/dictionaries');
|
|
8
8
|
const { setProp } = require('../base/model');
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
definitions: 'absolute',
|
|
12
|
-
elements: 'element',
|
|
13
|
-
enum: 'enum',
|
|
14
|
-
foreignKeys: 'key',
|
|
15
|
-
actions: 'action',
|
|
16
|
-
params: 'param',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const kindProperties = {
|
|
20
|
-
// TODO: also foreignKeys ?
|
|
21
|
-
namespace: { artifacts: true }, // on-the-fly context
|
|
22
|
-
context: { artifacts: true, normalized: 'namespace' },
|
|
23
|
-
service: { artifacts: true, normalized: 'namespace' },
|
|
24
|
-
entity: { elements: true, actions: true, params: () => false },
|
|
25
|
-
select: { normalized: 'select', elements: true },
|
|
26
|
-
$join: { normalized: 'select' },
|
|
27
|
-
$tableAlias: { normalized: 'alias' }, // table alias in select
|
|
28
|
-
$self: { normalized: 'alias' }, // table alias in select
|
|
29
|
-
$navElement: { normalized: 'element' },
|
|
30
|
-
$inline: { normalized: 'element' }, // column with inline property
|
|
31
|
-
event: { elements: true },
|
|
32
|
-
type: { elements: propExists, enum: propExists },
|
|
33
|
-
aspect: { elements: propExists },
|
|
34
|
-
annotation: { elements: propExists, enum: propExists },
|
|
35
|
-
enum: { normalized: 'element' },
|
|
36
|
-
element: { elements: propExists, enum: propExists, dict: 'elements' },
|
|
37
|
-
mixin: { normalized: 'alias' },
|
|
38
|
-
action: {
|
|
39
|
-
params: () => false, elements: () => false, enum: () => false, dict: 'actions',
|
|
40
|
-
}, // no extend params, only annotate
|
|
41
|
-
function: {
|
|
42
|
-
params: () => false, elements: () => false, enum: () => false, normalized: 'action',
|
|
43
|
-
}, // no extend params, only annotate
|
|
44
|
-
key: { normalized: 'element' },
|
|
45
|
-
param: { elements: () => false, enum: () => false, dict: 'params' },
|
|
46
|
-
source: { artifacts: true }, // TODO -> $source
|
|
47
|
-
using: {},
|
|
48
|
-
extend: {
|
|
49
|
-
isExtension: true,
|
|
50
|
-
noDep: 'special',
|
|
51
|
-
elements: true, /* only for parse-cdl */
|
|
52
|
-
actions: true, /* only for parse-cdl */
|
|
53
|
-
},
|
|
54
|
-
annotate: {
|
|
55
|
-
isExtension: true, noDep: 'special', elements: true, enum: true, actions: true, params: true,
|
|
56
|
-
},
|
|
57
|
-
builtin: {}, // = CURRENT_DATE, TODO: improve
|
|
58
|
-
$parameters: {}, // $parameters in query entities
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
function propExists( prop, parent ) {
|
|
62
|
-
const obj = parent.returns || parent;
|
|
63
|
-
return (obj.items || obj.targetAspect || obj)[prop];
|
|
64
|
-
}
|
|
10
|
+
const { setLink, dependsOn, pathName } = require('./utils');
|
|
65
11
|
|
|
66
12
|
function artifactsEnv( art ) {
|
|
67
13
|
return art._subArtifacts || Object.create(null);
|
|
68
14
|
}
|
|
69
15
|
|
|
70
16
|
/**
|
|
71
|
-
* Main export function of this file.
|
|
72
|
-
* "define" and "resolve"
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
17
|
+
* Main export function of this file. Attach "resolve" functions shared for phase
|
|
18
|
+
* "define" and "resolve" to `model.$functions`, where argument `model` is the XSN.
|
|
19
|
+
*
|
|
20
|
+
* Before calling these functions, make sure that the following function
|
|
21
|
+
* in model.$volatileFunctions is set:
|
|
22
|
+
* - `environment`: a function which returns the search environment defined by
|
|
23
|
+
* its argument, e.g. a function which returns the dictionary of subartifacts.
|
|
76
24
|
*
|
|
77
25
|
* @param {XSN.Model} model
|
|
78
|
-
* @param {(a, b?, c?) => any} environment
|
|
79
|
-
* @returns {object} Commonly used "resolve" functions.
|
|
80
26
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const options = model
|
|
27
|
+
// TODO: yes, this function will be renamed
|
|
28
|
+
function fns( model ) {
|
|
29
|
+
const { options } = model;
|
|
84
30
|
const {
|
|
85
31
|
info, warning, error, message,
|
|
86
32
|
} = model.$messageFunctions;
|
|
@@ -204,13 +150,15 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
204
150
|
},
|
|
205
151
|
};
|
|
206
152
|
|
|
207
|
-
|
|
153
|
+
const VolatileFns = model.$volatileFunctions;
|
|
154
|
+
Object.assign( model.$functions, {
|
|
208
155
|
resolveUncheckedPath,
|
|
209
156
|
resolvePath,
|
|
210
157
|
resolveTypeArguments,
|
|
211
158
|
defineAnnotations,
|
|
212
159
|
attachAndEmitValidNames,
|
|
213
|
-
};
|
|
160
|
+
} );
|
|
161
|
+
return;
|
|
214
162
|
|
|
215
163
|
function checkConstRef( art ) {
|
|
216
164
|
return ![ 'builtin', 'param' ].includes( art.kind );
|
|
@@ -224,16 +172,25 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
224
172
|
return !(art.elements && !art.query && !art.type && !art.params);
|
|
225
173
|
}
|
|
226
174
|
|
|
175
|
+
/**
|
|
176
|
+
* @returns {boolean|string}
|
|
177
|
+
*/
|
|
227
178
|
function checkTypeRef( art ) {
|
|
228
179
|
if (art.kind === 'type' || art.kind === 'element')
|
|
229
180
|
return false;
|
|
230
181
|
return ![ 'entity', 'aspect', 'event' ].includes( art.kind ) || 'sloppy';
|
|
231
182
|
}
|
|
232
183
|
|
|
184
|
+
/**
|
|
185
|
+
* @returns {boolean|string}
|
|
186
|
+
*/
|
|
233
187
|
function checkActionParamTypeRef( art ) {
|
|
234
188
|
return !(art.kind === 'entity' && art._service) && checkTypeRef( art );
|
|
235
189
|
}
|
|
236
190
|
|
|
191
|
+
/**
|
|
192
|
+
* @returns {boolean|string}
|
|
193
|
+
*/
|
|
237
194
|
function checkEventTypeRef( art ) {
|
|
238
195
|
return art.kind !== 'event' && checkActionParamTypeRef( art );
|
|
239
196
|
}
|
|
@@ -242,6 +199,9 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
242
199
|
return art.kind !== 'entity';
|
|
243
200
|
}
|
|
244
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @returns {boolean|string}
|
|
204
|
+
*/
|
|
245
205
|
function checkTargetRef( art ) {
|
|
246
206
|
if (art.kind === 'entity' || art.kind === 'aspect')
|
|
247
207
|
return false;
|
|
@@ -257,7 +217,7 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
257
217
|
// TODO: better error location if error for main
|
|
258
218
|
if (elem._main.kind !== 'entity' )
|
|
259
219
|
return true; // elem not starting at entity
|
|
260
|
-
environment( art ); // sets _effectiveType on art
|
|
220
|
+
VolatileFns.environment( art ); // sets _effectiveType on art
|
|
261
221
|
return !(art._effectiveType || art).target;
|
|
262
222
|
}
|
|
263
223
|
|
|
@@ -345,7 +305,7 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
345
305
|
// first step: only use _combined of real query - TODO:
|
|
346
306
|
// reject if not visible, but not allow more (!)
|
|
347
307
|
(query._combined || query._parent._combined) ||
|
|
348
|
-
environment( user._main ? user._parent : user );
|
|
308
|
+
VolatileFns.environment( user._main ? user._parent : user );
|
|
349
309
|
}
|
|
350
310
|
}
|
|
351
311
|
|
|
@@ -439,7 +399,7 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
439
399
|
}
|
|
440
400
|
else {
|
|
441
401
|
dependsOn( user, art._main, location );
|
|
442
|
-
environment( art, location, user );
|
|
402
|
+
VolatileFns.environment( art, location, user );
|
|
443
403
|
// Without on-demand resolve, we can simply signal 'undefined "x"'
|
|
444
404
|
// instead of 'illegal cycle' in the following case:
|
|
445
405
|
// element elem: type of elem.x;
|
|
@@ -513,7 +473,7 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
513
473
|
function getPathRoot( path, spec, user, env, extDict, msgArt ) {
|
|
514
474
|
if (!spec.envFn && user._pathHead) {
|
|
515
475
|
// TODO: not necessarily for explicit ON condition in expand
|
|
516
|
-
environment( user._pathHead ); // make sure _origin is set
|
|
476
|
+
VolatileFns.environment( user._pathHead ); // make sure _origin is set
|
|
517
477
|
return user._pathHead._origin;
|
|
518
478
|
}
|
|
519
479
|
const head = path[0];
|
|
@@ -570,8 +530,10 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
570
530
|
if (extDict && (!spec.dollar || head.id[0] !== '$')) {
|
|
571
531
|
const r = extDict[head.id];
|
|
572
532
|
if (Array.isArray(r)) {
|
|
573
|
-
if (r[0].kind === '$navElement') {
|
|
574
|
-
|
|
533
|
+
if (r[0].kind === '$navElement' && r.every( e => !e._parent.$duplicates )) {
|
|
534
|
+
// only complain about ambiguous source elements if we do not have
|
|
535
|
+
// duplicate table aliases, only mention non-ambiguous source elems
|
|
536
|
+
const names = r.filter( e => !e.$duplicates )
|
|
575
537
|
.map( e => `${ e.name.alias }.${ e.name.element }` );
|
|
576
538
|
if (names.length) {
|
|
577
539
|
error( 'ref-ambiguous', [ head.location, user ], { id: head.id, names },
|
|
@@ -658,31 +620,26 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
658
620
|
continue;
|
|
659
621
|
}
|
|
660
622
|
|
|
661
|
-
const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : environment;
|
|
623
|
+
const fn = (spec.envFn && artItemsCount >= 0) ? spec.envFn : VolatileFns.environment;
|
|
662
624
|
const env = fn( art, item.location, user, spec.assoc );
|
|
663
|
-
|
|
664
|
-
// do not check any elements of the path, e.g. $session - but still don't return path-head
|
|
665
|
-
if (art && art.$uncheckedElements) {
|
|
666
|
-
if (env && env[item.id]) // something like $user.id/$user.locale
|
|
667
|
-
return env[item.id];
|
|
668
|
-
|
|
669
|
-
// $user.foo - build our own valid path step obj
|
|
670
|
-
// Important: Don't directly modify item!
|
|
671
|
-
const obj = {
|
|
672
|
-
location: item.location,
|
|
673
|
-
kind: 'builtin',
|
|
674
|
-
name: { id: item.id, element: path.map(p => p.id).join('.') },
|
|
675
|
-
};
|
|
676
|
-
setLink(obj, art, '_parent');
|
|
677
|
-
return obj;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
625
|
const sub = setLink( item, env && env[item.id] );
|
|
681
626
|
|
|
682
|
-
if (!sub)
|
|
683
|
-
|
|
684
|
-
|
|
627
|
+
if (!sub) {
|
|
628
|
+
// element was not found in environment
|
|
629
|
+
if (sub === 0)
|
|
630
|
+
return 0;
|
|
631
|
+
if (art.$uncheckedElements) { // magic variable / replacement variable
|
|
632
|
+
signalNotFound( 'ref-unknown-var', [ item.location, user ], [ env ],
|
|
633
|
+
{ id: path.map(n => n.id).join('.') } );
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
errorNotFound( item, env );
|
|
637
|
+
}
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
else if (Array.isArray(sub)) { // redefinitions
|
|
685
641
|
return false;
|
|
642
|
+
}
|
|
686
643
|
|
|
687
644
|
if (nav) { // we have already "pseudo-followed" a managed association
|
|
688
645
|
// We currently rely on the check that targetElement references do
|
|
@@ -764,7 +721,7 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
764
721
|
{ param: 'Entity $(ART) has no parameter $(MEMBER)' } );
|
|
765
722
|
}
|
|
766
723
|
else {
|
|
767
|
-
signalNotFound( 'ref-undefined-element', [ item.location, user ],
|
|
724
|
+
signalNotFound( spec.undefinedDef || 'ref-undefined-element', [ item.location, user ],
|
|
768
725
|
[ env ], { art: searchName( art, item.id, 'element' ) } );
|
|
769
726
|
}
|
|
770
727
|
return null;
|
|
@@ -847,7 +804,9 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
847
804
|
}
|
|
848
805
|
// TODO: block should be construct._block
|
|
849
806
|
if (construct.$annotations && construct.$annotations.doc )
|
|
850
|
-
art.doc = construct.$annotations.doc;
|
|
807
|
+
art.doc = construct.$annotations.doc; // e.g. through `annotate` statement in CDL
|
|
808
|
+
else if (construct.doc)
|
|
809
|
+
art.doc = construct.doc; // e.g. through `extensions` array in CSN
|
|
851
810
|
if (!construct.$annotations) {
|
|
852
811
|
if (!block || block.$frontend !== 'json')
|
|
853
812
|
return; // namespace, or in CDL source without @annos:
|
|
@@ -861,7 +820,7 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
861
820
|
setProp( a, '_block', block );
|
|
862
821
|
a.$priority = priority;
|
|
863
822
|
if (construct !== art)
|
|
864
|
-
|
|
823
|
+
addAnnotation( art, annoProp, a );
|
|
865
824
|
}
|
|
866
825
|
}
|
|
867
826
|
}
|
|
@@ -912,143 +871,19 @@ function fns( model, environment = artifactsEnv ) {
|
|
|
912
871
|
// TODO: _parent, _main is set later (if we have ElementRef), or do we
|
|
913
872
|
// set _artifact?
|
|
914
873
|
anno.$priority = priority;
|
|
915
|
-
|
|
874
|
+
addAnnotation( art, annoProp, anno );
|
|
916
875
|
}
|
|
917
876
|
}
|
|
918
877
|
}
|
|
919
878
|
|
|
920
|
-
//
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
// The link (_artifact,_effectiveType,...) usually has the artifact as value.
|
|
927
|
-
// Falsy values are:
|
|
928
|
-
// - undefined: not computed yet, parse error, no ref
|
|
929
|
-
// - null: no valid reference, param:true if that is not allowed
|
|
930
|
-
// - false (only complete ref): multiple definitions, rejected
|
|
931
|
-
// - 0 (for _effectiveType only): circular reference
|
|
932
|
-
function setLink( obj, value = null, prop = '_artifact' ) {
|
|
933
|
-
Object.defineProperty( obj, prop, { value, configurable: true, writable: true } );
|
|
934
|
-
return value;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
938
|
-
const elem = {
|
|
939
|
-
name: { location: location || origin.name.location, id: name },
|
|
940
|
-
kind: origin.kind,
|
|
941
|
-
location: location || origin.location,
|
|
942
|
-
};
|
|
943
|
-
if (origin.name.$inferred)
|
|
944
|
-
elem.name.$inferred = origin.name.$inferred;
|
|
945
|
-
if (parent)
|
|
946
|
-
setMemberParent( elem, name, parent, prop ); // TODO: redef in template
|
|
947
|
-
setProp( elem, '_origin', origin );
|
|
948
|
-
// TODO: should we use silent dependencies also for other things, like
|
|
949
|
-
// included elements? (Currently for $inferred: 'expand-element' only)
|
|
950
|
-
if (silentDep)
|
|
951
|
-
dependsOnSilent( elem, origin );
|
|
952
|
-
else
|
|
953
|
-
dependsOn( elem, origin, location );
|
|
954
|
-
return elem;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
function setMemberParent( elem, name, parent, prop ) {
|
|
958
|
-
if (prop) { // extension or structure include
|
|
959
|
-
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
960
|
-
const p = parent.items || parent.targetAspect || parent;
|
|
961
|
-
if (!(prop in p))
|
|
962
|
-
p[prop] = Object.create(null);
|
|
963
|
-
dictAdd( p[prop], name, elem );
|
|
964
|
-
}
|
|
965
|
-
if (parent._outer)
|
|
966
|
-
parent = parent._outer;
|
|
967
|
-
setProp( elem, '_parent', parent );
|
|
968
|
-
setProp( elem, '_main', parent._main || parent );
|
|
969
|
-
elem.name.absolute = elem._main.name.absolute;
|
|
970
|
-
if (name == null)
|
|
971
|
-
return;
|
|
972
|
-
const normalized = kindProperties[elem.kind].normalized || elem.kind;
|
|
973
|
-
[ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
|
|
974
|
-
if (normalized === kind)
|
|
975
|
-
elem.name[kind] = (parent.name[kind] != null && kind !== 'select' && kind !== 'alias') ? `${ parent.name[kind] }.${ name }` : name;
|
|
976
|
-
|
|
977
|
-
else if (parent.name[kind] != null)
|
|
978
|
-
elem.name[kind] = parent.name[kind];
|
|
979
|
-
|
|
980
|
-
else
|
|
981
|
-
delete elem.name[kind];
|
|
982
|
-
});
|
|
983
|
-
// try { throw new Error('Foo') } catch (e) { elem.name.stack = e; };
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/**
|
|
987
|
-
* Adds a dependency user -> art with the given location.
|
|
988
|
-
*
|
|
989
|
-
* @param {XSN.Artifact} user
|
|
990
|
-
* @param {XSN.Artifact} art
|
|
991
|
-
* @param {XSN.Location} location
|
|
992
|
-
*/
|
|
993
|
-
function dependsOn( user, art, location ) {
|
|
994
|
-
if (!user._deps)
|
|
995
|
-
setProp( user, '_deps', [] );
|
|
996
|
-
user._deps.push( { art, location } );
|
|
997
|
-
}
|
|
998
|
-
|
|
999
|
-
/**
|
|
1000
|
-
* Same as "dependsOn" but the dependency from user -> art is silent,
|
|
1001
|
-
* i.e. not reported to the user.
|
|
1002
|
-
*
|
|
1003
|
-
* @param {XSN.Artifact} user
|
|
1004
|
-
* @param {XSN.Artifact} art
|
|
1005
|
-
*/
|
|
1006
|
-
function dependsOnSilent( user, art ) {
|
|
1007
|
-
if (!user._deps)
|
|
1008
|
-
setProp( user, '_deps', [] );
|
|
1009
|
-
user._deps.push( { art } );
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
function storeExtension( elem, name, prop, parent, block ) {
|
|
1013
|
-
if (prop === 'enum')
|
|
1014
|
-
prop = 'elements';
|
|
1015
|
-
setProp( elem, '_block', block );
|
|
1016
|
-
const kind = `_${ elem.kind }`; // _extend or _annotate
|
|
1017
|
-
if (!parent[kind])
|
|
1018
|
-
setProp( parent, kind, {} );
|
|
1019
|
-
// if (name === '' && prop === 'params') {
|
|
1020
|
-
// pushToDict( parent[kind], 'returns', elem ); // not really a dict
|
|
1021
|
-
// return;
|
|
1022
|
-
// }
|
|
1023
|
-
if (!parent[kind][prop])
|
|
1024
|
-
parent[kind][prop] = Object.create(null);
|
|
1025
|
-
pushToDict( parent[kind][prop], name, elem );
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
/** @type {(a: any, b: any) => boolean} */
|
|
1029
|
-
const testFunctionPlaceholder = () => true;
|
|
1030
|
-
|
|
1031
|
-
// Return path step if the path navigates along an association whose final type
|
|
1032
|
-
// satisfies function `test`; "navigates along" = last path item not considered
|
|
1033
|
-
// without truthy optional argument `alsoTestLast`.
|
|
1034
|
-
function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
|
|
1035
|
-
for (const item of ref.path || []) {
|
|
1036
|
-
const art = item && item._artifact; // item can be null with parse error
|
|
1037
|
-
if (art && art._effectiveType && art._effectiveType.target && test( art._effectiveType, item ))
|
|
1038
|
-
return (alsoTestLast || item !== ref.path[ref.path.length - 1]) && item;
|
|
1039
|
-
}
|
|
1040
|
-
return false;
|
|
879
|
+
// Add annotation to definition - overwriting $inferred annos
|
|
880
|
+
function addAnnotation( art, annoProp, anno ) {
|
|
881
|
+
const old = art[annoProp];
|
|
882
|
+
if (old && old.$inferred)
|
|
883
|
+
delete art[annoProp];
|
|
884
|
+
dictAddArray( art, annoProp, anno );
|
|
1041
885
|
}
|
|
1042
886
|
|
|
1043
887
|
module.exports = {
|
|
1044
|
-
dictKinds,
|
|
1045
|
-
kindProperties,
|
|
1046
888
|
fns,
|
|
1047
|
-
setLink,
|
|
1048
|
-
linkToOrigin,
|
|
1049
|
-
dependsOn,
|
|
1050
|
-
dependsOnSilent,
|
|
1051
|
-
setMemberParent,
|
|
1052
|
-
storeExtension,
|
|
1053
|
-
withAssociation,
|
|
1054
889
|
};
|
package/lib/compiler/utils.js
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
'use strict';
|
|
10
10
|
|
|
11
|
+
const { dictAdd, pushToDict } = require('../base/dictionaries');
|
|
12
|
+
const { kindProperties } = require('./base');
|
|
13
|
+
|
|
11
14
|
// for links, i.e., properties starting with an underscore '_':
|
|
12
15
|
|
|
13
16
|
function pushLink( obj, prop, value ) {
|
|
@@ -27,6 +30,13 @@ function annotationIsFalse( anno ) { // falsy, but not null (u
|
|
|
27
30
|
return anno && (anno.val === false || anno.val === 0 || anno.val === '');
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
/**
|
|
34
|
+
* @param {XSN.Artifact} art
|
|
35
|
+
* @param {string} anno
|
|
36
|
+
* @param {XSN.Location} [location]
|
|
37
|
+
* @param {*} [val]
|
|
38
|
+
* @param {string} [literal]
|
|
39
|
+
*/
|
|
30
40
|
function annotateWith( art, anno, location = art.location, val = true, literal = 'boolean' ) {
|
|
31
41
|
if (art[anno]) // do not overwrite user-defined including null
|
|
32
42
|
return;
|
|
@@ -38,10 +48,191 @@ function annotateWith( art, anno, location = art.location, val = true, literal =
|
|
|
38
48
|
};
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
// TODO: define setLink() like the current setProp(), we might have setArtifactLink()
|
|
52
|
+
// Do not share this function with CSN processors!
|
|
53
|
+
|
|
54
|
+
// The link (_artifact,_effectiveType,...) usually has the artifact as value.
|
|
55
|
+
// Falsy values are:
|
|
56
|
+
// - undefined: not computed yet, parse error, no ref
|
|
57
|
+
// - null: no valid reference, param:true if that is not allowed
|
|
58
|
+
// - false (only complete ref): multiple definitions, rejected
|
|
59
|
+
// - 0 (for _effectiveType only): circular reference
|
|
60
|
+
function setLink( obj, value = null, prop = '_artifact' ) {
|
|
61
|
+
Object.defineProperty( obj, prop, { value, configurable: true, writable: true } );
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Like `obj.prop = value`, but not contained in JSON / CSN
|
|
67
|
+
* It's important to set enumerable explicitly to false (although 'false' is the default),
|
|
68
|
+
* as else, if the property already exists, it keeps the old setting for enumerable.
|
|
69
|
+
*
|
|
70
|
+
* @param {object} obj
|
|
71
|
+
* @param {string} prop
|
|
72
|
+
* @param {any} value
|
|
73
|
+
*/
|
|
74
|
+
function setProp(obj, prop, value) {
|
|
75
|
+
const descriptor = {
|
|
76
|
+
value,
|
|
77
|
+
configurable: true,
|
|
78
|
+
writable: true,
|
|
79
|
+
enumerable: false,
|
|
80
|
+
};
|
|
81
|
+
Object.defineProperty( obj, prop, descriptor );
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function linkToOrigin( origin, name, parent, prop, location, silentDep ) {
|
|
86
|
+
const elem = {
|
|
87
|
+
name: { location: location || origin.name.location, id: name },
|
|
88
|
+
kind: origin.kind,
|
|
89
|
+
location: location || origin.location,
|
|
90
|
+
};
|
|
91
|
+
if (origin.name.$inferred)
|
|
92
|
+
elem.name.$inferred = origin.name.$inferred;
|
|
93
|
+
if (parent)
|
|
94
|
+
setMemberParent( elem, name, parent, prop ); // TODO: redef in template
|
|
95
|
+
setProp( elem, '_origin', origin );
|
|
96
|
+
// TODO: should we use silent dependencies also for other things, like
|
|
97
|
+
// included elements? (Currently for $inferred: 'expand-element' only)
|
|
98
|
+
if (silentDep)
|
|
99
|
+
dependsOnSilent( elem, origin );
|
|
100
|
+
else
|
|
101
|
+
dependsOn( elem, origin, location );
|
|
102
|
+
return elem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setMemberParent( elem, name, parent, prop ) {
|
|
106
|
+
if (prop) { // extension or structure include
|
|
107
|
+
// TODO: consider nested ARRAY OF and RETURNS, COMPOSITION OF type
|
|
108
|
+
const p = parent.items || parent.targetAspect || parent;
|
|
109
|
+
if (!(prop in p))
|
|
110
|
+
p[prop] = Object.create(null);
|
|
111
|
+
dictAdd( p[prop], name, elem );
|
|
112
|
+
}
|
|
113
|
+
if (parent._outer)
|
|
114
|
+
parent = parent._outer;
|
|
115
|
+
setProp( elem, '_parent', parent );
|
|
116
|
+
setProp( elem, '_main', parent._main || parent );
|
|
117
|
+
elem.name.absolute = elem._main.name.absolute;
|
|
118
|
+
if (name == null)
|
|
119
|
+
return;
|
|
120
|
+
const normalized = kindProperties[elem.kind].normalized || elem.kind;
|
|
121
|
+
[ 'element', 'alias', 'select', 'param', 'action' ].forEach( ( kind ) => {
|
|
122
|
+
if (normalized === kind)
|
|
123
|
+
elem.name[kind] = (parent.name[kind] != null && kind !== 'select' && kind !== 'alias') ? `${ parent.name[kind] }.${ name }` : name;
|
|
124
|
+
|
|
125
|
+
else if (parent.name[kind] != null)
|
|
126
|
+
elem.name[kind] = parent.name[kind];
|
|
127
|
+
|
|
128
|
+
else
|
|
129
|
+
delete elem.name[kind];
|
|
130
|
+
});
|
|
131
|
+
// try { throw new Error('Foo') } catch (e) { elem.name.stack = e; };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Adds a dependency user -> art with the given location.
|
|
136
|
+
*
|
|
137
|
+
* @param {XSN.Artifact} user
|
|
138
|
+
* @param {XSN.Artifact} art
|
|
139
|
+
* @param {XSN.Location} location
|
|
140
|
+
*/
|
|
141
|
+
function dependsOn( user, art, location ) {
|
|
142
|
+
if (!user._deps)
|
|
143
|
+
setProp( user, '_deps', [] );
|
|
144
|
+
user._deps.push( { art, location } );
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Same as "dependsOn" but the dependency from user -> art is silent,
|
|
149
|
+
* i.e. not reported to the user.
|
|
150
|
+
*
|
|
151
|
+
* @param {XSN.Artifact} user
|
|
152
|
+
* @param {XSN.Artifact} art
|
|
153
|
+
*/
|
|
154
|
+
function dependsOnSilent( user, art ) {
|
|
155
|
+
if (!user._deps)
|
|
156
|
+
setProp( user, '_deps', [] );
|
|
157
|
+
user._deps.push( { art } );
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function storeExtension( elem, name, prop, parent, block ) {
|
|
161
|
+
if (prop === 'enum')
|
|
162
|
+
prop = 'elements';
|
|
163
|
+
setProp( elem, '_block', block );
|
|
164
|
+
const kind = `_${ elem.kind }`; // _extend or _annotate
|
|
165
|
+
if (!parent[kind])
|
|
166
|
+
setProp( parent, kind, {} );
|
|
167
|
+
// if (name === '' && prop === 'params') {
|
|
168
|
+
// pushToDict( parent[kind], 'returns', elem ); // not really a dict
|
|
169
|
+
// return;
|
|
170
|
+
// }
|
|
171
|
+
if (!parent[kind][prop])
|
|
172
|
+
parent[kind][prop] = Object.create(null);
|
|
173
|
+
pushToDict( parent[kind][prop], name, elem );
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** @type {(a: any, b: any) => boolean} */
|
|
177
|
+
const testFunctionPlaceholder = () => true;
|
|
178
|
+
|
|
179
|
+
// Return path step if the path navigates along an association whose final type
|
|
180
|
+
// satisfies function `test`; "navigates along" = last path item not considered
|
|
181
|
+
// without truthy optional argument `alsoTestLast`.
|
|
182
|
+
function withAssociation( ref, test = testFunctionPlaceholder, alsoTestLast = false ) {
|
|
183
|
+
for (const item of ref.path || []) {
|
|
184
|
+
const art = item && item._artifact; // item can be null with parse error
|
|
185
|
+
if (art && art._effectiveType && art._effectiveType.target && test( art._effectiveType, item ))
|
|
186
|
+
return (alsoTestLast || item !== ref.path[ref.path.length - 1]) && item;
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Return string 'A.B.C' for parsed source `A.B.C` (is vector of ids with
|
|
193
|
+
* locations).
|
|
194
|
+
*
|
|
195
|
+
* @param {XSN.Path} path
|
|
196
|
+
*/
|
|
197
|
+
function pathName(path) {
|
|
198
|
+
return (path.broken) ? '' : path.map( id => id.id ).join('.');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Generates an XSN path out of the given name. Path segments are delimited by a dot.
|
|
203
|
+
* Each segment will have the given location assigned.
|
|
204
|
+
*
|
|
205
|
+
* @param {CSN.Location} location
|
|
206
|
+
* @param {string} name
|
|
207
|
+
* @returns {XSN.Path}
|
|
208
|
+
*/
|
|
209
|
+
function splitIntoPath( location, name ) {
|
|
210
|
+
return name.split('.').map( id => ({ id, location }) );
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {CSN.Location} location
|
|
215
|
+
* @param {...any} args
|
|
216
|
+
*/
|
|
217
|
+
function augmentPath( location, ...args ) {
|
|
218
|
+
return { path: args.map( id => ({ id, location }) ), location };
|
|
219
|
+
}
|
|
220
|
+
|
|
41
221
|
|
|
42
222
|
module.exports = {
|
|
43
223
|
pushLink,
|
|
44
224
|
annotationVal,
|
|
45
225
|
annotationIsFalse,
|
|
46
226
|
annotateWith,
|
|
227
|
+
setLink,
|
|
228
|
+
setProp,
|
|
229
|
+
linkToOrigin,
|
|
230
|
+
dependsOn,
|
|
231
|
+
dependsOnSilent,
|
|
232
|
+
setMemberParent,
|
|
233
|
+
storeExtension,
|
|
234
|
+
withAssociation,
|
|
235
|
+
pathName,
|
|
236
|
+
augmentPath,
|
|
237
|
+
splitIntoPath,
|
|
47
238
|
};
|