@sap/cds-compiler 2.7.0 → 2.11.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 +167 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +17 -33
- package/lib/api/options.js +25 -13
- package/lib/api/validate.js +33 -9
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +26 -2
- package/lib/base/messages.js +25 -9
- package/lib/base/model.js +5 -3
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +18 -5
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +5 -2
- package/lib/compiler/definer.js +145 -120
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +207 -47
- package/lib/compiler/shared.js +47 -200
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +94 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +302 -115
- package/lib/edm/edmUtils.js +31 -12
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5308 -4308
- package/lib/json/from-csn.js +59 -30
- package/lib/json/to-csn.js +354 -105
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +81 -14
- package/lib/language/language.g4 +163 -31
- package/lib/main.d.ts +136 -17
- package/lib/main.js +7 -1
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +115 -32
- package/lib/model/csnUtils.js +71 -33
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -16
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +60 -17
- package/lib/render/toHdbcds.js +122 -74
- package/lib/render/toSql.js +57 -32
- package/lib/render/utils/common.js +6 -10
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +9 -6
- package/lib/transform/db/expansion.js +19 -7
- package/lib/transform/db/flattening.js +31 -7
- package/lib/transform/db/transformExists.js +344 -66
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +65 -436
- package/lib/transform/forOdataNew.js +21 -10
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +44 -38
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +55 -9
- package/lib/transform/translateAssocsToJoins.js +11 -17
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
package/lib/compiler/definer.js
CHANGED
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
// Sub Phase 2 (initXYZ)
|
|
89
89
|
// - set _parent, _main (later: _service?) links, and _block links of members
|
|
90
90
|
// - add _subArtifacts dictionary and "namespace artifacts" for name resolution
|
|
91
|
-
// - duplicate checks
|
|
91
|
+
// - duplicate checks
|
|
92
92
|
// - structure checks ?
|
|
93
93
|
// - annotation assignments
|
|
94
94
|
// - POST: resolvePath() can be called for artifact references (if complete model)
|
|
@@ -101,9 +101,11 @@
|
|
|
101
101
|
// to avoid consequential or repeated errors.
|
|
102
102
|
// - But: The same artifact is added to multiple dictionaries.
|
|
103
103
|
// - Solution part 1: $duplicates as property of the artifact or member
|
|
104
|
-
// for
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
|
|
105
|
+
// dictionary of the whole model, `$tableAliases` dictionary of queries.
|
|
106
|
+
// - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
|
|
107
|
+
// dictionary, `_combined` dictionary for query search, `$tableAliases`
|
|
108
|
+
// of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
|
|
107
109
|
|
|
108
110
|
'use strict';
|
|
109
111
|
|
|
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
|
|
|
111
113
|
const {
|
|
112
114
|
isDeprecatedEnabled, isBetaEnabled,
|
|
113
115
|
setProp, forEachGeneric, forEachInOrder,
|
|
114
|
-
|
|
116
|
+
forEachDefinition,
|
|
115
117
|
forEachMemberRecursivelyWithQuery,
|
|
116
118
|
} = require('../base/model');
|
|
117
119
|
const {
|
|
@@ -120,20 +122,20 @@ const {
|
|
|
120
122
|
const {
|
|
121
123
|
dictLocation,
|
|
122
124
|
} = require('../base/location');
|
|
125
|
+
const { kindProperties, dictKinds } = require('./base');
|
|
123
126
|
const {
|
|
124
|
-
annotationVal,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
dictKinds,
|
|
128
|
-
kindProperties,
|
|
129
|
-
fns,
|
|
127
|
+
annotationVal,
|
|
128
|
+
annotationIsFalse,
|
|
129
|
+
annotateWith,
|
|
130
130
|
linkToOrigin,
|
|
131
131
|
setMemberParent,
|
|
132
132
|
storeExtension,
|
|
133
133
|
dependsOnSilent,
|
|
134
|
-
|
|
134
|
+
augmentPath,
|
|
135
|
+
splitIntoPath,
|
|
136
|
+
} = require('./utils');
|
|
135
137
|
const { compareLayer, layer } = require('./moduleLayers');
|
|
136
|
-
const { initBuiltins } = require('./builtins');
|
|
138
|
+
const { initBuiltins, isInReservedNamespace } = require('./builtins');
|
|
137
139
|
const setLink = setProp;
|
|
138
140
|
|
|
139
141
|
/**
|
|
@@ -148,7 +150,7 @@ const setLink = setProp;
|
|
|
148
150
|
*
|
|
149
151
|
* @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
|
|
150
152
|
*/
|
|
151
|
-
function
|
|
153
|
+
function define( model ) {
|
|
152
154
|
const { options } = model;
|
|
153
155
|
// Get simplified "resolve" functionality and the message function:
|
|
154
156
|
const {
|
|
@@ -160,24 +162,29 @@ function getDefinerFunctions( model ) {
|
|
|
160
162
|
resolveTypeArguments,
|
|
161
163
|
defineAnnotations,
|
|
162
164
|
attachAndEmitValidNames,
|
|
163
|
-
} =
|
|
164
|
-
|
|
165
|
-
let addTextsLanguageAssoc = false;
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
define,
|
|
165
|
+
} = model.$functions;
|
|
166
|
+
Object.assign( model.$functions, {
|
|
169
167
|
initArtifact,
|
|
170
168
|
lateExtensions,
|
|
171
169
|
projectionAncestor,
|
|
172
170
|
hasTruthyProp,
|
|
171
|
+
} );
|
|
172
|
+
// During the definer, we can only resolve artifact references, i.e,
|
|
173
|
+
// after a `.`, we only search in the `_subArtifacts` dictionary:
|
|
174
|
+
model.$volatileFunctions.environment = function artifactsEnv( art ) {
|
|
175
|
+
return art._subArtifacts || Object.create(null);
|
|
173
176
|
};
|
|
174
177
|
|
|
178
|
+
const extensionsDict = Object.create(null);
|
|
179
|
+
let addTextsLanguageAssoc = false;
|
|
180
|
+
return doDefine();
|
|
181
|
+
|
|
175
182
|
/**
|
|
176
183
|
* Main function of the definer.
|
|
177
184
|
*
|
|
178
185
|
* @returns {XSN.Model}
|
|
179
186
|
*/
|
|
180
|
-
function
|
|
187
|
+
function doDefine() {
|
|
181
188
|
if (options.deprecated &&
|
|
182
189
|
messages.every( m => m.messageId !== 'api-deprecated-option' )) {
|
|
183
190
|
warning( 'api-deprecated-option', {},
|
|
@@ -206,8 +213,6 @@ function getDefinerFunctions( model ) {
|
|
|
206
213
|
|
|
207
214
|
if (options.parseCdl) {
|
|
208
215
|
initExtensionsWithoutApplying();
|
|
209
|
-
// Check for redefinitions
|
|
210
|
-
Object.keys( model.definitions ).forEach( preProcessArtifact );
|
|
211
216
|
// If no extensions shall be applied then we can skip further
|
|
212
217
|
// artifact processing and return the model with an `extensions` property.
|
|
213
218
|
return model;
|
|
@@ -215,8 +220,8 @@ function getDefinerFunctions( model ) {
|
|
|
215
220
|
|
|
216
221
|
applyExtensions();
|
|
217
222
|
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
const commonLanguagesEntity // TODO: remove beta after a grace period
|
|
224
|
+
= (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
|
|
220
225
|
model.definitions['sap.common.Languages'];
|
|
221
226
|
addTextsLanguageAssoc = !!(commonLanguagesEntity && commonLanguagesEntity.elements &&
|
|
222
227
|
commonLanguagesEntity.elements.code);
|
|
@@ -237,13 +242,13 @@ function getDefinerFunctions( model ) {
|
|
|
237
242
|
* @param {XSN.AST} src
|
|
238
243
|
*/
|
|
239
244
|
function addSource( src ) {
|
|
240
|
-
// handle sub model from
|
|
245
|
+
// handle sub model from parser
|
|
241
246
|
if (!src.kind)
|
|
242
247
|
src.kind = 'source';
|
|
243
248
|
|
|
244
249
|
let namespace = src.namespace && src.namespace.path;
|
|
245
250
|
let prefix = namespace ? `${ pathName( namespace ) }.` : '';
|
|
246
|
-
if (
|
|
251
|
+
if (isInReservedNamespace(prefix)) {
|
|
247
252
|
error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
|
|
248
253
|
// TODO: use $(NAME)
|
|
249
254
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -279,8 +284,7 @@ function getDefinerFunctions( model ) {
|
|
|
279
284
|
function addDefinition( art, block ) {
|
|
280
285
|
const { absolute } = art.name;
|
|
281
286
|
// TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
|
|
282
|
-
if (absolute === 'cds' ||
|
|
283
|
-
absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
|
|
287
|
+
if (absolute === 'cds' || isInReservedNamespace(absolute)) {
|
|
284
288
|
error( 'reserved-namespace-cds', [ art.name.location, art ], {},
|
|
285
289
|
// TODO: use $(NAME)
|
|
286
290
|
'The namespace "cds" is reserved for CDS builtins' );
|
|
@@ -304,7 +308,7 @@ function getDefinerFunctions( model ) {
|
|
|
304
308
|
}
|
|
305
309
|
else {
|
|
306
310
|
setLink( art, '_block', block );
|
|
307
|
-
// dictAdd might set $duplicates to true
|
|
311
|
+
// dictAdd might set $duplicates to true
|
|
308
312
|
dictAdd( model.definitions, absolute, art );
|
|
309
313
|
return true;
|
|
310
314
|
}
|
|
@@ -443,6 +447,23 @@ function getDefinerFunctions( model ) {
|
|
|
443
447
|
|
|
444
448
|
// Phase 2 ("init") --------------------------------------------------------
|
|
445
449
|
|
|
450
|
+
function checkRedefinition( art ) {
|
|
451
|
+
if (!art.$duplicates)
|
|
452
|
+
return;
|
|
453
|
+
if (art._main) {
|
|
454
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
455
|
+
name: art.name.id,
|
|
456
|
+
'#': kindProperties[art.kind].normalized || art.kind,
|
|
457
|
+
} );
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
error( 'duplicate-definition', [ art.name.location, art ], {
|
|
461
|
+
name: art.name.absolute,
|
|
462
|
+
'#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
|
|
463
|
+
} );
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
446
467
|
function initNamespaceAndUsing( src ) {
|
|
447
468
|
if (src.namespace) {
|
|
448
469
|
const decl = src.namespace;
|
|
@@ -487,12 +508,13 @@ function getDefinerFunctions( model ) {
|
|
|
487
508
|
if (!reInit)
|
|
488
509
|
initParentLink( art, model.definitions );
|
|
489
510
|
const block = art._block;
|
|
511
|
+
checkRedefinition( art );
|
|
490
512
|
defineAnnotations( art, art, block );
|
|
491
513
|
initMembers( art, art, block );
|
|
492
514
|
initDollarSelf( art ); // $self
|
|
493
515
|
if (art.params)
|
|
494
516
|
initParams( art ); // $parameters
|
|
495
|
-
if (art.includes && !(art.name.absolute in extensionsDict))
|
|
517
|
+
if (art.includes && !(art.name.absolute in extensionsDict)) // TODO: in next phase?
|
|
496
518
|
extensionsDict[art.name.absolute] = []; // structure with includes must be "extended"
|
|
497
519
|
|
|
498
520
|
if (!art.query)
|
|
@@ -512,6 +534,7 @@ function getDefinerFunctions( model ) {
|
|
|
512
534
|
|
|
513
535
|
function initVocabulary( art ) {
|
|
514
536
|
initParentLink( art, model.vocabularies );
|
|
537
|
+
checkRedefinition( art );
|
|
515
538
|
const block = art._block;
|
|
516
539
|
defineAnnotations( art, art, block );
|
|
517
540
|
initMembers( art, art, block );
|
|
@@ -553,20 +576,6 @@ function getDefinerFunctions( model ) {
|
|
|
553
576
|
|
|
554
577
|
// From here til EOF, reexamine code ---------------------------------------
|
|
555
578
|
|
|
556
|
-
// currently called from preProcessArtifact(), do be called in "init"
|
|
557
|
-
function checkRedefinitions( obj, name, prop ) {
|
|
558
|
-
forEachMember( obj, checkRedefinitions, obj.targetAspect );
|
|
559
|
-
if (!obj.$duplicates)
|
|
560
|
-
return;
|
|
561
|
-
if (obj.name.location.file === '<built-in>') {
|
|
562
|
-
// builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
|
|
563
|
-
// The error shall only be printed for the user-defined conflicting artifact.
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
error( 'duplicate-definition', [ obj.name.location, obj ],
|
|
567
|
-
{ name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
|
|
568
|
-
}
|
|
569
|
-
|
|
570
579
|
function initDollarSelf( art ) {
|
|
571
580
|
const selfname = '$self';
|
|
572
581
|
// TODO: use setMemberParent() ?
|
|
@@ -607,14 +616,7 @@ function getDefinerFunctions( model ) {
|
|
|
607
616
|
if (query.on)
|
|
608
617
|
initExprForQuery( query.on, query );
|
|
609
618
|
// TODO: MIXIN with name = ...subquery (not yet supported anyway)
|
|
610
|
-
|
|
611
|
-
if (elem && (elem.value || elem.expand)) {
|
|
612
|
-
setProp( elem, '_block', query._block );
|
|
613
|
-
defineAnnotations( elem, elem, query._block );
|
|
614
|
-
initExprForQuery( elem.value, query );
|
|
615
|
-
initExpandInline( elem );
|
|
616
|
-
}
|
|
617
|
-
}
|
|
619
|
+
initSelectItems( query, query.columns );
|
|
618
620
|
if (query.where)
|
|
619
621
|
initExprForQuery( query.where, query );
|
|
620
622
|
if (query.having)
|
|
@@ -622,22 +624,41 @@ function getDefinerFunctions( model ) {
|
|
|
622
624
|
initMembers( query, query, query._block );
|
|
623
625
|
}
|
|
624
626
|
|
|
625
|
-
function
|
|
626
|
-
// TODO: forbid with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
627
|
-
|
|
628
|
-
|
|
627
|
+
function initSelectItems( parent, columns ) {
|
|
628
|
+
// TODO: forbid expand/inline with :param, global:true, in ref-where, outside queries (CSN), ...
|
|
629
|
+
let wildcard = null;
|
|
630
|
+
for (const col of columns || parent.expand || parent.inline || []) {
|
|
631
|
+
if (!col) // parse error
|
|
629
632
|
continue;
|
|
630
|
-
if (
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
633
|
+
if (!columns) {
|
|
634
|
+
if (parent.value)
|
|
635
|
+
setProp( col, '_pathHead', parent ); // also set for '*' in expand/inline
|
|
636
|
+
else if (parent._pathHead)
|
|
637
|
+
setProp( col, '_pathHead', parent._pathHead );
|
|
638
|
+
}
|
|
639
|
+
if (col.val === '*') {
|
|
640
|
+
if (!wildcard) {
|
|
641
|
+
wildcard = col;
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
// a late syntax error (this code also runs with parse-cdl), i.e.
|
|
645
|
+
// no semantic loc (wouldn't be available for expand/inline anyway)
|
|
646
|
+
error( 'syntax-duplicate-clause', [ col.location, null ],
|
|
647
|
+
{ prop: '*', line: wildcard.location.line, col: wildcard.location.col },
|
|
648
|
+
'You have provided a $(PROP) already at line $(LINE), column $(COL)' );
|
|
649
|
+
// TODO: extra text variants for expand/inline? - probably not
|
|
650
|
+
col.val = null; // do not consider it for expandWildcard()
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
else if (col.value || col.expand) {
|
|
654
|
+
setProp( col, '_block', parent._block );
|
|
655
|
+
defineAnnotations( col, col, parent._block ); // TODO: complain with inline
|
|
656
|
+
// TODO: allow sub queries? at least in top-level expand without parallel ref
|
|
657
|
+
if (columns)
|
|
658
|
+
initExprForQuery( col.value, parent );
|
|
659
|
+
initSelectItems( col );
|
|
638
660
|
}
|
|
639
661
|
}
|
|
640
|
-
// TODO: allow sub queries in top-level expand without parallel ref
|
|
641
662
|
}
|
|
642
663
|
|
|
643
664
|
function initExprForQuery( expr, query ) {
|
|
@@ -656,9 +677,38 @@ function getDefinerFunctions( model ) {
|
|
|
656
677
|
}
|
|
657
678
|
else if (expr.path && expr.$expected === 'exists') {
|
|
658
679
|
expr.$expected = 'approved-exists';
|
|
680
|
+
approveExistsInChildren(expr);
|
|
659
681
|
}
|
|
660
682
|
}
|
|
661
683
|
|
|
684
|
+
/**
|
|
685
|
+
* If we have a valid top-level exists, exists in filters of sub-expressions can be translated,
|
|
686
|
+
* since we will have a top-level subquery after exists-processing in the forHanaNew.
|
|
687
|
+
*
|
|
688
|
+
* Recursively drill down into:
|
|
689
|
+
* - the .path
|
|
690
|
+
* - the .args
|
|
691
|
+
* - the .where.args
|
|
692
|
+
*
|
|
693
|
+
* Any $expected === 'exists' encountered along the way are turned into 'approved-exists'
|
|
694
|
+
*
|
|
695
|
+
* working: exists toE[exists toE] -> select from E where exists toE
|
|
696
|
+
* not working: toE[exists toE] -> we don't support subqueries in filters
|
|
697
|
+
*
|
|
698
|
+
* @param {object} exprOrPathElement starts w/ an expr but then subelem from .path or .where.args
|
|
699
|
+
*/
|
|
700
|
+
function approveExistsInChildren(exprOrPathElement) {
|
|
701
|
+
if (exprOrPathElement.$expected === 'exists')
|
|
702
|
+
exprOrPathElement.$expected = 'approved-exists';
|
|
703
|
+
// Drill down
|
|
704
|
+
if (exprOrPathElement.args)
|
|
705
|
+
exprOrPathElement.args.forEach(elem => approveExistsInChildren(elem));
|
|
706
|
+
else if (exprOrPathElement.where && exprOrPathElement.where.args)
|
|
707
|
+
exprOrPathElement.where.args.forEach(elem => approveExistsInChildren(elem));
|
|
708
|
+
else if (exprOrPathElement.path)
|
|
709
|
+
exprOrPathElement.path.forEach(elem => approveExistsInChildren(elem));
|
|
710
|
+
}
|
|
711
|
+
|
|
662
712
|
// table is table expression in FROM, becomes an alias
|
|
663
713
|
function initTableExpression( table, query, joinParents ) {
|
|
664
714
|
if (!table) // parse error
|
|
@@ -706,6 +756,8 @@ function getDefinerFunctions( model ) {
|
|
|
706
756
|
// ? ta._joinParent.args[ta.$joinArgsIndex] // in JOIN
|
|
707
757
|
// : ta._parent.from ) // directly in FROM
|
|
708
758
|
// Note for --raw-output: _joinParent pointing to CROSS JOIN node has not name
|
|
759
|
+
if (!tab) // parse error; time for #6241
|
|
760
|
+
return; // (parser method to only add non-null to array)
|
|
709
761
|
setProp( tab, '_joinParent', table );
|
|
710
762
|
tab.$joinArgsIndex = index;
|
|
711
763
|
initTableExpression( tab, query, joinParents );
|
|
@@ -730,12 +782,13 @@ function getDefinerFunctions( model ) {
|
|
|
730
782
|
setMemberParent( table, table.name.id, query );
|
|
731
783
|
setProp( table, '_block', query._block );
|
|
732
784
|
dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
|
|
733
|
-
error( 'duplicate-definition', [ loc, table ], { name, '#': '
|
|
785
|
+
error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
|
|
734
786
|
} );
|
|
735
787
|
// also add to JOIN nodes for name restrictions:
|
|
736
788
|
for (const p of joinParents) {
|
|
737
|
-
//
|
|
738
|
-
|
|
789
|
+
// for JOIN alias restriction, we cannot use $duplicates, as it is
|
|
790
|
+
// already used for duplicate aliases of queries:
|
|
791
|
+
dictAddArray( p.$tableAliases, table.name.id, table );
|
|
739
792
|
}
|
|
740
793
|
if (table.name.id[0] === '$') {
|
|
741
794
|
warning( 'syntax-dollar-ident', [ table.name.location, table ], {
|
|
@@ -822,7 +875,7 @@ function getDefinerFunctions( model ) {
|
|
|
822
875
|
// assignments on the mixin... (also for future mixin definitions
|
|
823
876
|
// with generated values)
|
|
824
877
|
dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
|
|
825
|
-
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '
|
|
878
|
+
error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
|
|
826
879
|
} );
|
|
827
880
|
if (mixin.name.id[0] === '$') {
|
|
828
881
|
warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
|
|
@@ -859,6 +912,7 @@ function getDefinerFunctions( model ) {
|
|
|
859
912
|
* (which is basically the component name of the `parent` element plus a dot).
|
|
860
913
|
*/
|
|
861
914
|
function initMembers( construct, parent, block, initExtensions = false ) {
|
|
915
|
+
// TODO: split extend from init
|
|
862
916
|
const isQueryExtension = kindProperties[construct.kind].isExtension &&
|
|
863
917
|
(parent._main || parent).query;
|
|
864
918
|
let obj = construct;
|
|
@@ -914,7 +968,7 @@ function getDefinerFunctions( model ) {
|
|
|
914
968
|
forEachInOrder( construct, 'params', init );
|
|
915
969
|
const { returns } = construct;
|
|
916
970
|
if (returns) {
|
|
917
|
-
returns.kind = 'param';
|
|
971
|
+
returns.kind = (kindProperties[construct.kind].isExtension) ? construct.kind : 'param';
|
|
918
972
|
init( returns, '' ); // '' is special name for returns parameter
|
|
919
973
|
}
|
|
920
974
|
return;
|
|
@@ -948,6 +1002,7 @@ function getDefinerFunctions( model ) {
|
|
|
948
1002
|
setProp( elem, '_block', bl );
|
|
949
1003
|
setMemberParent( elem, name, parent, construct !== parent && prop );
|
|
950
1004
|
// console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
|
|
1005
|
+
checkRedefinition( elem );
|
|
951
1006
|
defineAnnotations( elem, elem, bl );
|
|
952
1007
|
initMembers( elem, elem, bl, initExtensions );
|
|
953
1008
|
|
|
@@ -983,6 +1038,7 @@ function getDefinerFunctions( model ) {
|
|
|
983
1038
|
}
|
|
984
1039
|
|
|
985
1040
|
function checkDefinitions( construct, parent, prop, dict = construct[prop] ) {
|
|
1041
|
+
// TODO: do differently, see also annotateMembers() in resolver
|
|
986
1042
|
// To have been checked by parsers:
|
|
987
1043
|
// - artifacts (CDL-only anyway) only inside [extend] context|service
|
|
988
1044
|
if (!dict)
|
|
@@ -1421,6 +1477,12 @@ function getDefinerFunctions( model ) {
|
|
|
1421
1477
|
|
|
1422
1478
|
model.extensions.push(annotationArtifact);
|
|
1423
1479
|
extendArtifact( exts, annotationArtifact ); // also sets _artifact link in extensions
|
|
1480
|
+
// if one of the annotate statement mentions 'returns', assume it
|
|
1481
|
+
// TODO: with warning/info?
|
|
1482
|
+
for (const ext of exts) {
|
|
1483
|
+
if (ext.$syntax === 'returns')
|
|
1484
|
+
annotationArtifact.$syntax = 'returns';
|
|
1485
|
+
}
|
|
1424
1486
|
}
|
|
1425
1487
|
}
|
|
1426
1488
|
}
|
|
@@ -1704,6 +1766,7 @@ function getDefinerFunctions( model ) {
|
|
|
1704
1766
|
}
|
|
1705
1767
|
|
|
1706
1768
|
function extendMembers( extensions, art, noExtend ) {
|
|
1769
|
+
// TODO: do the whole extension stuff lazily if the elements are requested
|
|
1707
1770
|
const elemExtensions = [];
|
|
1708
1771
|
extensions.sort( compareLayer );
|
|
1709
1772
|
for (const ext of extensions) {
|
|
@@ -1749,7 +1812,12 @@ function getDefinerFunctions( model ) {
|
|
|
1749
1812
|
[ 'elements', 'actions' ].forEach( (prop) => {
|
|
1750
1813
|
const dict = art._extend && art._extend[prop];
|
|
1751
1814
|
for (const name in dict) {
|
|
1752
|
-
|
|
1815
|
+
let obj = art;
|
|
1816
|
+
if (obj.targetAspect)
|
|
1817
|
+
obj = obj.targetAspect;
|
|
1818
|
+
while (obj.items)
|
|
1819
|
+
obj = obj.items;
|
|
1820
|
+
const validDict = obj[prop] || prop === 'elements' && obj.enum;
|
|
1753
1821
|
const member = validDict[name];
|
|
1754
1822
|
if (!member)
|
|
1755
1823
|
extendNothing( dict[name], prop, name, art, validDict );
|
|
@@ -1900,24 +1968,6 @@ function getDefinerFunctions( model ) {
|
|
|
1900
1968
|
}
|
|
1901
1969
|
}
|
|
1902
1970
|
|
|
1903
|
-
// TODO: move to "init" phase
|
|
1904
|
-
/**
|
|
1905
|
-
* Check whether redefinitions of the given artifact name exist and
|
|
1906
|
-
* adapt to `targetAspect`.
|
|
1907
|
-
*
|
|
1908
|
-
* @param {string} name
|
|
1909
|
-
*/
|
|
1910
|
-
function preProcessArtifact( name ) {
|
|
1911
|
-
const art = model.definitions[name];
|
|
1912
|
-
if (Array.isArray(art.$duplicates)) {
|
|
1913
|
-
// A definition name containing a `.` is not invalid (TODO: starting or
|
|
1914
|
-
// ending with a dot is invalid and could be checked here)
|
|
1915
|
-
for (const a of art.$duplicates)
|
|
1916
|
-
checkRedefinitions( a, name, 'definitions' );
|
|
1917
|
-
}
|
|
1918
|
-
checkRedefinitions( art, name, 'definitions' );
|
|
1919
|
-
}
|
|
1920
|
-
|
|
1921
1971
|
/**
|
|
1922
1972
|
* Process "composition of" artifacts.
|
|
1923
1973
|
*
|
|
@@ -1988,8 +2038,8 @@ function getDefinerFunctions( model ) {
|
|
|
1988
2038
|
|
|
1989
2039
|
if (isKey && isLocalized) { // key with localized is wrong - ignore localized
|
|
1990
2040
|
const errpos = elem.localized || elem.type || elem.name;
|
|
1991
|
-
warning( 'localized-key', [ errpos.location, elem ], {},
|
|
1992
|
-
'Keyword
|
|
2041
|
+
warning( 'localized-key', [ errpos.location, elem ], { keyword: 'localized' },
|
|
2042
|
+
'Keyword $(KEYWORD) is ignored for primary keys' );
|
|
1993
2043
|
}
|
|
1994
2044
|
}
|
|
1995
2045
|
if (textElems.length <= keys)
|
|
@@ -2131,9 +2181,9 @@ function getDefinerFunctions( model ) {
|
|
|
2131
2181
|
});
|
|
2132
2182
|
}
|
|
2133
2183
|
}
|
|
2134
|
-
|
|
2184
|
+
if (hasTruthyProp( orig, 'localized' )) { // use location of LOCALIZED keyword
|
|
2135
2185
|
const localized = orig.localized || orig.type || orig.name;
|
|
2136
|
-
elem.localized = { val:
|
|
2186
|
+
elem.localized = { val: null, $inferred: 'localized', location: localized.location };
|
|
2137
2187
|
}
|
|
2138
2188
|
}
|
|
2139
2189
|
if (fioriEnabled)
|
|
@@ -2268,26 +2318,6 @@ function pathName(path) {
|
|
|
2268
2318
|
return path.map( id => id.id ).join('.');
|
|
2269
2319
|
}
|
|
2270
2320
|
|
|
2271
|
-
/**
|
|
2272
|
-
* Generates an XSN path out of the given name. Path segments are delimited by a dot.
|
|
2273
|
-
* Each segment will have the given location assigned.
|
|
2274
|
-
*
|
|
2275
|
-
* @param {CSN.Location} location
|
|
2276
|
-
* @param {string} name
|
|
2277
|
-
* @returns {XSN.Path}
|
|
2278
|
-
*/
|
|
2279
|
-
function splitIntoPath( location, name ) {
|
|
2280
|
-
return name.split('.').map( id => ({ id, location }) );
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
/**
|
|
2284
|
-
* @param {CSN.Location} location
|
|
2285
|
-
* @param {...any} args
|
|
2286
|
-
*/
|
|
2287
|
-
function augmentPath( location, ...args ) {
|
|
2288
|
-
return { path: args.map( id => ({ id, location }) ), location };
|
|
2289
|
-
}
|
|
2290
|
-
|
|
2291
2321
|
function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
2292
2322
|
const args = relations.map( eq );
|
|
2293
2323
|
return (args.length === 1)
|
|
@@ -2316,9 +2346,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
|
|
|
2316
2346
|
// these function could be used to a future lib/compiler/utils.js, but DO NOT
|
|
2317
2347
|
// SHARE with utility functions for CSN processors
|
|
2318
2348
|
|
|
2319
|
-
module.exports = {
|
|
2320
|
-
define: model => getDefinerFunctions( model ).define(),
|
|
2321
|
-
getDefinerFunctions,
|
|
2322
|
-
augmentPath,
|
|
2323
|
-
splitIntoPath,
|
|
2324
|
-
};
|
|
2349
|
+
module.exports = { define };
|
package/lib/compiler/index.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
// Main XSN-based compiler functions
|
|
2
2
|
|
|
3
|
+
// ...
|
|
4
|
+
|
|
5
|
+
// How functions are shared across the Core Compiler sub modules:
|
|
6
|
+
|
|
7
|
+
// - Shared XSN-related functions which do not use a context are in utils.js,
|
|
8
|
+
// they are `require`d as usual at the beginning of sub modules.
|
|
9
|
+
// - The XSN is the only context which context-dependent functions can depend on.
|
|
10
|
+
// - Sharing such a function is by adding it to `‹xsn›.$functions`,
|
|
11
|
+
// e.g. `resolvePath` and similar will be attached to the XSN.
|
|
12
|
+
// - Functions which might be overwritten in a next sub module
|
|
13
|
+
// are added to `‹xsn›.$volatileFunctions`, currently just `environment`.
|
|
14
|
+
|
|
3
15
|
'use strict';
|
|
4
16
|
|
|
5
17
|
const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
|
|
@@ -8,6 +20,7 @@ const parseCsn = require('../json/from-csn');
|
|
|
8
20
|
|
|
9
21
|
const assertConsistency = require('./assert-consistency');
|
|
10
22
|
const moduleLayers = require('./moduleLayers');
|
|
23
|
+
const { fns } = require('./shared');
|
|
11
24
|
const { define } = require('./definer');
|
|
12
25
|
const resolve = require('./resolver');
|
|
13
26
|
const propagator = require('./propagator');
|
|
@@ -110,7 +123,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
110
123
|
// fileCache = Object.assign( Object.create(null), fileCache );
|
|
111
124
|
dir = path.resolve(dir);
|
|
112
125
|
const a = processFilenames( filenames, dir );
|
|
113
|
-
a.fileContentDict = Object.create(null);
|
|
114
126
|
|
|
115
127
|
const model = { sources: a.sources, options };
|
|
116
128
|
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
@@ -149,7 +161,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
|
|
|
149
161
|
}
|
|
150
162
|
else {
|
|
151
163
|
try {
|
|
152
|
-
a.fileContentDict[filename] = source;
|
|
153
164
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
154
165
|
a.sources[filename] = ast;
|
|
155
166
|
ast.location = { file: rel };
|
|
@@ -220,7 +231,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
220
231
|
// absolute file names - they start with `/` or `\` or similar
|
|
221
232
|
dir = path.resolve(dir);
|
|
222
233
|
const a = processFilenames( filenames, dir );
|
|
223
|
-
a.fileContentDict = Object.create(null);
|
|
224
234
|
|
|
225
235
|
const model = { sources: a.sources, options };
|
|
226
236
|
model.$messageFunctions = createMessageFunctions( options, 'compile', model );
|
|
@@ -281,7 +291,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
|
|
|
281
291
|
}
|
|
282
292
|
else {
|
|
283
293
|
try {
|
|
284
|
-
a.fileContentDict[filename] = source;
|
|
285
294
|
const ast = parseX( source, rel, options, model.$messageFunctions );
|
|
286
295
|
a.sources[filename] = ast;
|
|
287
296
|
ast.location = { file: rel };
|
|
@@ -416,6 +425,9 @@ function compileDoX( model ) {
|
|
|
416
425
|
throwWithError();
|
|
417
426
|
return model;
|
|
418
427
|
}
|
|
428
|
+
model.$functions = {};
|
|
429
|
+
model.$volatileFunctions = {};
|
|
430
|
+
fns( model ); // attach (mostly) paths functions
|
|
419
431
|
define( model );
|
|
420
432
|
// do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
|
|
421
433
|
// TODO: do not use this function for parseCdl anyway…
|
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
setProp,
|
|
9
9
|
isDeprecatedEnabled,
|
|
10
10
|
} = require( '../base/model');
|
|
11
|
-
const { linkToOrigin, withAssociation } = require('./
|
|
11
|
+
const { linkToOrigin, withAssociation } = require('./utils');
|
|
12
12
|
// const { refString } = require( '../base/messages')
|
|
13
13
|
|
|
14
14
|
function propagate( model ) {
|
|
@@ -179,6 +179,8 @@ function propagate( model ) {
|
|
|
179
179
|
// accessed at their type being a main artifact
|
|
180
180
|
function expensive( prop, target, source ) {
|
|
181
181
|
// console.log(prop,source.name,'->',target.kind,target.name);
|
|
182
|
+
if (source.kind === 'builtin')
|
|
183
|
+
return;
|
|
182
184
|
if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
|
|
183
185
|
// foreignKeys must always be copied with target to avoid any confusion
|
|
184
186
|
// whether we have to generated implicit keys
|
|
@@ -211,7 +213,8 @@ function propagate( model ) {
|
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
function onlyViaParent( prop, target, source ) {
|
|
214
|
-
if (target.$inferred === 'proxy'
|
|
216
|
+
if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
|
|
217
|
+
// assocs and enums do not have 'include'
|
|
215
218
|
always( prop, target, source );
|
|
216
219
|
}
|
|
217
220
|
|