@sap/cds-compiler 2.11.2 → 2.13.6
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 +175 -2
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +23 -17
- package/bin/cdsse.js +2 -2
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +32 -79
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +2 -1
- package/lib/backends.js +16 -26
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +10 -19
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +75 -9
- package/lib/base/messages.js +31 -35
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/unknownMagic.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +12 -8
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +42 -21
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +46 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +100 -65
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +215 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +189 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +144 -53
- package/lib/language/language.g4 +424 -200
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +550 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +322 -198
- package/lib/model/csnUtils.js +226 -370
- package/lib/model/enrichCsn.js +124 -69
- package/lib/model/revealInternalProperties.js +29 -7
- package/lib/model/sortViews.js +10 -2
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +8 -3
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +203 -122
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +161 -82
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +212 -0
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +556 -106
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +103 -28
- package/lib/transform/db/views.js +92 -44
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +98 -783
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +13 -30
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +8 -3
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2349
- package/lib/compiler/resolver.js +0 -2922
- package/lib/transform/db/helpers.js +0 -58
- package/lib/transform/universalCsnEnricher.js +0 -67
package/lib/model/csnRefs.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// a reference is context-dependent, especially if queries are involved. This
|
|
5
5
|
// module provides the corresponding resolve/inspect functions.
|
|
6
6
|
//
|
|
7
|
+
// This module should work with both client-style CSN and Universal CSN.
|
|
8
|
+
//
|
|
7
9
|
// See below for preconditions / things to consider – the functions in this
|
|
8
10
|
// module do not issue user-friendly messages for invalid references in a CSN,
|
|
9
11
|
// such messages are (hopefully) issued by the compile() function.
|
|
@@ -41,18 +43,18 @@
|
|
|
41
43
|
//
|
|
42
44
|
// 1. a well-formed CSN with valid references;
|
|
43
45
|
// 2. a compiled model, i.e. a CSN with all inferred information provided by
|
|
44
|
-
// the compile() function
|
|
45
|
-
//
|
|
46
|
+
// the compile() function for the CSN flavors `client` or `universal`
|
|
47
|
+
// (including the (non-)enumerable `elements` property in `client` CSN);
|
|
46
48
|
// 3. no (relevant) CSN changes between the calls of the same instance of
|
|
47
49
|
// inspectRef() - to enable caching.
|
|
48
50
|
//
|
|
49
51
|
// If any of these conditions are not given, our functions usually simply
|
|
50
52
|
// throws an exception (which might even be a plain TypeError), but it might
|
|
51
|
-
// also
|
|
53
|
+
// also just return any value. CSN processors can provide user-friendly error
|
|
52
54
|
// messages by calling the Core Compiler in case of exceptions. For details,
|
|
53
55
|
// see internalDoc/CoreCompiler.md#use-of-the-core-compiler-for-csn-processors.
|
|
54
56
|
|
|
55
|
-
// During a transformation, care must be
|
|
57
|
+
// During a transformation, care must be taken to adhere to these conditions.
|
|
56
58
|
// E.g. a structure flattening function cannot create an element `s_x` and
|
|
57
59
|
// delete `s` and then still expects inspectRef() to be able to resolve a
|
|
58
60
|
// reference `['s', 'x']`.
|
|
@@ -93,7 +95,7 @@
|
|
|
93
95
|
//
|
|
94
96
|
// The names in further path items are searched in the “navigation” environment
|
|
95
97
|
// of the path so far - it does not need to depend on the reference context (as
|
|
96
|
-
// we do not check the
|
|
98
|
+
// we do not check the validity here):
|
|
97
99
|
//
|
|
98
100
|
// 1. We search in the elements of the target entity for associations and
|
|
99
101
|
// compositions, and in the elements of the current object otherwise.
|
|
@@ -157,10 +159,29 @@
|
|
|
157
159
|
// hierarchy, query number, table aliases and links from a column to its
|
|
158
160
|
// respective inferred element.
|
|
159
161
|
|
|
162
|
+
// Properties in cache:
|
|
163
|
+
//
|
|
164
|
+
// - _effectiveType on def/member/items: cached result of effectiveType()
|
|
165
|
+
// - _origin on def/member/items: the "prototype"
|
|
166
|
+
// - $origin on def/member/items: whether implicit _origin refs have been set for direct members
|
|
167
|
+
// - _parent: not yet used (for debugging)
|
|
168
|
+
//
|
|
169
|
+
// - _env on non-string path item: environment provided by the ref so far,
|
|
170
|
+
// next path item is element in it
|
|
171
|
+
// - _ref on non-string `type` or `from` ref, or on alias: the referred def/elem
|
|
172
|
+
//
|
|
173
|
+
// - $queries on def: array of all queries of an entity
|
|
174
|
+
// - $queryNumber: the index position +1 of a query inside the $queries array
|
|
175
|
+
// - $aliases on query: dictionary of alias names to cache with _ref/_select and elements
|
|
176
|
+
// - _select: value of the `SELECT` property of a query (or value of `projection`)
|
|
177
|
+
// - elements: the elements of the query (original CSN elements from query or main)
|
|
178
|
+
// - _element on query column: the corresponding element
|
|
179
|
+
|
|
160
180
|
'use strict';
|
|
161
181
|
|
|
162
182
|
const BUILTIN_TYPE = {};
|
|
163
183
|
const { locationString } = require('../base/location');
|
|
184
|
+
const { ModelError, CompilerAssertion } = require("../base/error");
|
|
164
185
|
|
|
165
186
|
// Properties in which artifact or members are defined - next property in the
|
|
166
187
|
// "csnPath" is the name or index of that property; 'args' (its value can be a
|
|
@@ -171,19 +192,24 @@ const artifactProperties = [ 'elements', 'columns', 'keys', 'mixin', 'enum',
|
|
|
171
192
|
// Mapping the “reference context string” to the reference semantics
|
|
172
193
|
// - lexical: false | Function - determines where to look first for “lexical names”
|
|
173
194
|
// - dynamic: String - describes the dynamic environment (if in query)
|
|
195
|
+
// - assoc: String, with dynamic: 'global' - what to do with assoc steps
|
|
196
|
+
// * 'static': visit elements of anonymous aspect if not last ref item
|
|
197
|
+
// * 'target': always follow target, including last ref item
|
|
198
|
+
// * other (& not provided) = follow target if not last ref item
|
|
174
199
|
const referenceSemantics = {
|
|
175
|
-
type: { lexical: false, dynamic: 'global' },
|
|
176
|
-
includes: { lexical: false, dynamic: 'global' },
|
|
177
|
-
target: { lexical: false, dynamic: 'global' },
|
|
178
|
-
targetAspect: { lexical: false, dynamic: 'global' },
|
|
179
|
-
from: { lexical: false, dynamic: 'global' },
|
|
200
|
+
type: { lexical: false, dynamic: 'global' }, // TODO: assoc: 'static', see Issue #8458
|
|
201
|
+
includes: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
|
|
202
|
+
target: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
|
|
203
|
+
targetAspect: { lexical: false, dynamic: 'global', assoc: 'static' },
|
|
204
|
+
from: { lexical: false, dynamic: 'global', assoc: 'target' },
|
|
180
205
|
keys: { lexical: false, dynamic: 'target' },
|
|
206
|
+
keys_origin: { lexical: false, dynamic: 'target' },
|
|
181
207
|
excluding: { lexical: false, dynamic: 'source' },
|
|
182
208
|
expand: { lexical: justDollar, dynamic: 'expand' }, // ...using baseEnv
|
|
183
209
|
inline: { lexical: justDollar, dynamic: 'inline' }, // ...using baseEnv
|
|
184
210
|
ref_where: { lexical: justDollar , dynamic: 'ref-target'}, // ...using baseEnv
|
|
185
211
|
on: { lexical: justDollar, dynamic: 'query' }, // assoc defs, redirected to
|
|
186
|
-
// there are also '
|
|
212
|
+
// there are also 'on_join' and 'on_mixin' with default semantics
|
|
187
213
|
orderBy: { lexical: query => query, dynamic: 'query' },
|
|
188
214
|
orderBy_set: { lexical: query => query.$next, dynamic: 'query' }, // to outer SELECT (from UNION)
|
|
189
215
|
// default: { lexical: query => query, dynamic: 'source' }
|
|
@@ -195,10 +221,15 @@ function justDollar() {
|
|
|
195
221
|
|
|
196
222
|
/**
|
|
197
223
|
* @param {CSN.Model} csn
|
|
224
|
+
* @param {boolean|string} [universalReady]
|
|
198
225
|
*/
|
|
199
|
-
function csnRefs( csn ) {
|
|
226
|
+
function csnRefs( csn, universalReady ) {
|
|
200
227
|
const cache = new WeakMap();
|
|
201
|
-
|
|
228
|
+
setCache( BUILTIN_TYPE, '_origin', null );
|
|
229
|
+
if (universalReady === 'init-all') {
|
|
230
|
+
for (const art of Object.values( csn.definitions || {}))
|
|
231
|
+
initDefinition( art );
|
|
232
|
+
}
|
|
202
233
|
// Functions which set the new `baseEnv`:
|
|
203
234
|
resolveRef.expandInline = function resolve_expandInline( ref, ...args ) {
|
|
204
235
|
return cached( ref, '_env', () => navigationEnv( resolveRef( ref, ...args ).art ) );
|
|
@@ -210,7 +241,15 @@ function csnRefs( csn ) {
|
|
|
210
241
|
} );
|
|
211
242
|
}
|
|
212
243
|
return {
|
|
213
|
-
effectiveType,
|
|
244
|
+
effectiveType,
|
|
245
|
+
artifactRef,
|
|
246
|
+
getOrigin,
|
|
247
|
+
inspectRef,
|
|
248
|
+
queryOrMain,
|
|
249
|
+
getColumn: ( elem ) => getCache( elem, '_column' ),
|
|
250
|
+
getElement: ( col ) => getCache( col, '_element' ),
|
|
251
|
+
initDefinition,
|
|
252
|
+
targetAspect,
|
|
214
253
|
__getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
|
|
215
254
|
};
|
|
216
255
|
|
|
@@ -239,7 +278,7 @@ function csnRefs( csn ) {
|
|
|
239
278
|
return setCache( art, '_effectiveType', art );
|
|
240
279
|
|
|
241
280
|
if (getCache( art, '_effectiveType' ) === 0)
|
|
242
|
-
throw new
|
|
281
|
+
throw new ModelError( 'Circular type reference');
|
|
243
282
|
const type = getCache( art, '_effectiveType' ) || art;
|
|
244
283
|
chain.forEach( a => setCache( a, '_effectiveType', type ) );
|
|
245
284
|
return type;
|
|
@@ -248,21 +287,19 @@ function csnRefs( csn ) {
|
|
|
248
287
|
/**
|
|
249
288
|
* @param {CSN.Artifact} art
|
|
250
289
|
*/
|
|
251
|
-
function navigationEnv( art ) {
|
|
252
|
-
let
|
|
290
|
+
function navigationEnv( art, staticAssoc ) {
|
|
291
|
+
let env = effectiveType( art );
|
|
253
292
|
// here, we do not care whether it is semantically ok to navigate into sub
|
|
254
293
|
// elements of array items (that is the task of the core compiler /
|
|
255
294
|
// semantic check)
|
|
256
|
-
while (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
cached( env, '$origin', _a => setImplicitOrigin( env, origin ) );
|
|
265
|
-
return env;
|
|
295
|
+
while (env.items)
|
|
296
|
+
env = effectiveType( env.items );
|
|
297
|
+
const target = (staticAssoc ? targetAspect( env ) : env.target);
|
|
298
|
+
if (typeof target !== 'string')
|
|
299
|
+
return target || env;
|
|
300
|
+
const def = csn.definitions[target];
|
|
301
|
+
initDefinition( def );
|
|
302
|
+
return def;
|
|
266
303
|
}
|
|
267
304
|
|
|
268
305
|
/**
|
|
@@ -274,125 +311,158 @@ function csnRefs( csn ) {
|
|
|
274
311
|
* could not be found.
|
|
275
312
|
*/
|
|
276
313
|
function artifactRef( ref, notFound ) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
314
|
+
// TODO: what about type ref?
|
|
315
|
+
if (typeof ref === 'string') {
|
|
316
|
+
const main = csn.definitions[ref];
|
|
317
|
+
if (main)
|
|
318
|
+
return initDefinition( main );
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
const art = cached( ref, '_ref', artifactPathRef );
|
|
322
|
+
if (art)
|
|
323
|
+
return art;
|
|
324
|
+
}
|
|
325
|
+
if (notFound !== undefined && typeof ref === 'string')
|
|
326
|
+
return notFound; // is only meant for builtins
|
|
327
|
+
// Backend bug workaround, TODO: delete next 2 lines
|
|
282
328
|
if (notFound !== undefined)
|
|
283
329
|
return notFound;
|
|
284
|
-
throw new
|
|
330
|
+
throw new ModelError( 'Undefined reference' );
|
|
285
331
|
}
|
|
286
332
|
|
|
287
333
|
function artifactPathRef( ref ) {
|
|
288
334
|
const [ head, ...tail ] = ref.ref;
|
|
289
335
|
let art = csn.definitions[pathId( head )];
|
|
336
|
+
initDefinition( art );
|
|
290
337
|
for (const elem of tail) {
|
|
291
|
-
const env = navigationEnv( art );
|
|
338
|
+
const env = navigationEnv( art ); // TODO: second argument true, see Issue #8458
|
|
292
339
|
art = env.elements[pathId( elem )];
|
|
293
340
|
}
|
|
294
341
|
return art;
|
|
295
342
|
}
|
|
296
343
|
|
|
297
|
-
function
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
344
|
+
function artifactFromRef( ref ) {
|
|
345
|
+
const [ head, ...tail ] = ref.ref;
|
|
346
|
+
let art = csn.definitions[pathId( head )];
|
|
347
|
+
initDefinition( art );
|
|
348
|
+
for (const elem of tail) {
|
|
349
|
+
const env = navigationEnv( art );
|
|
350
|
+
art = env.elements[pathId( elem )];
|
|
351
|
+
}
|
|
352
|
+
return navigationEnv( art );
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Return target when resolving references in 'keys'
|
|
356
|
+
function assocTarget( art, refCtx ) {
|
|
357
|
+
// Call contexts:
|
|
358
|
+
// 1. normal definition of association with explicit foreign keys
|
|
359
|
+
// 2. auto-redirected association with renaming of foreign keys
|
|
360
|
+
// (currently: `keys` always available on inherited associations)
|
|
361
|
+
// 3. user-induced redirection (in 'cast') with explicit foreign keys
|
|
362
|
+
// 4. original provided association def inside $origin with explicit foreign keys
|
|
363
|
+
// (outside $origin like 2)
|
|
364
|
+
const targetName = refCtx !== 'keys_origin' && art.target
|
|
365
|
+
|| art.$origin && art.$origin.target
|
|
366
|
+
|| art.cast.target;
|
|
367
|
+
const target = csn.definitions[targetName];
|
|
368
|
+
initDefinition( target );
|
|
369
|
+
return target;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function getOrigin( art ) {
|
|
373
|
+
return cached( art, '_origin', getOriginRaw );
|
|
302
374
|
}
|
|
303
375
|
|
|
304
376
|
function getOriginRaw( art ) {
|
|
305
377
|
if (art.type) // TODO: make robust against "linked" = only direct
|
|
306
378
|
return artifactRef( art.type, BUILTIN_TYPE );
|
|
307
|
-
if (
|
|
308
|
-
return
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
for (const elem of tail) {
|
|
318
|
-
origin = originNavigation( origin, elem, isAction );
|
|
319
|
-
isAction = false;
|
|
379
|
+
if (typeof art.$origin === 'object') // null, […], {…}
|
|
380
|
+
return getOriginExplicit( art.$origin );
|
|
381
|
+
|
|
382
|
+
const parent = getCache( art, '_parent' );
|
|
383
|
+
if (parent === undefined && universalReady) {
|
|
384
|
+
const { $location } = art;
|
|
385
|
+
const location = $location &&
|
|
386
|
+
(typeof $location === 'string' ? $location : locationString( $location ));
|
|
387
|
+
const def = Object.keys( art ).join('+') + (location ? ':' + location : '');
|
|
388
|
+
throw new Error( `Inspecting non-initialized CSN node {${def}}` );
|
|
320
389
|
}
|
|
321
|
-
|
|
390
|
+
const step = getCache( art, '$origin$step' );
|
|
391
|
+
if (!step)
|
|
392
|
+
return null;
|
|
393
|
+
const origin = cached( parent, '_origin', getOriginRaw );
|
|
394
|
+
return originNavigation( origin, step );
|
|
322
395
|
}
|
|
323
396
|
|
|
324
|
-
function
|
|
325
|
-
if (
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (isAction)
|
|
334
|
-
return art.actions[elem];
|
|
335
|
-
// TODO: if we use effectiveType(), we might have more implicit prototypes
|
|
336
|
-
// we might then need a function like effectiveArtifact,
|
|
337
|
-
// which cares about actions/params
|
|
338
|
-
// let origin = cached( art, '_origin', getOriginRaw );
|
|
339
|
-
// while (art.items) {
|
|
340
|
-
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
341
|
-
// art = art.items;
|
|
342
|
-
// origin = cached( art, '_origin', getOriginRaw );
|
|
343
|
-
// }
|
|
344
|
-
// if (origin)
|
|
345
|
-
// cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
346
|
-
return (art.elements || art.enum || (art.targetAspect || art.target).elements)[elem];
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// From the current CSN object, set implicit origin for the next navigation step
|
|
350
|
-
// Currently (TODO: ?) `elements` only, i.e. what is needed for name resolution.
|
|
351
|
-
function setImplicitOrigin( art, origin ) {
|
|
352
|
-
setMembersImplicit( art.actions, origin.actions );
|
|
353
|
-
setMembersImplicit( art.params, origin.params );
|
|
354
|
-
if (art.returns) {
|
|
355
|
-
art = art.returns;
|
|
356
|
-
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
357
|
-
return true; // not implicit or shortened
|
|
358
|
-
origin = effectiveType( origin.returns );
|
|
359
|
-
setCache( art, '_origin', origin );
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
while (art.items) {
|
|
363
|
-
art = art.items;
|
|
364
|
-
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
365
|
-
return true; // not implicit or shortened
|
|
366
|
-
origin = effectiveType( origin.items );
|
|
367
|
-
setCache( art, '_origin', origin );
|
|
368
|
-
}
|
|
369
|
-
setMembersImplicit( art.elements, origin.elements );
|
|
370
|
-
// The enum base type is _not_ where we find the origins of the enum symbols.
|
|
371
|
-
// A derived type of an enum type with individual annotations on a symbol
|
|
372
|
-
// has both 'type' and '$origin'.
|
|
373
|
-
if (!art.type || art.$origin)
|
|
374
|
-
setMembersImplicit( art.enum, origin.enum );
|
|
375
|
-
return true;
|
|
397
|
+
function getOriginExplicit( $origin ) { // null, […], {…}
|
|
398
|
+
if (!$origin)
|
|
399
|
+
return null;
|
|
400
|
+
if (!Array.isArray( $origin )) // anonymous prototype in $origin
|
|
401
|
+
return getOriginExplicit( $origin.$origin );
|
|
402
|
+
const [ head, ...tail ] = $origin;
|
|
403
|
+
const main = csn.definitions[head];
|
|
404
|
+
initDefinition( main );
|
|
405
|
+
return tail.reduce( originNavigation, main );
|
|
376
406
|
}
|
|
377
407
|
|
|
378
|
-
function
|
|
379
|
-
if (!
|
|
380
|
-
return;
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
408
|
+
function originNavigation( art, step ) {
|
|
409
|
+
if (!step)
|
|
410
|
+
return null;
|
|
411
|
+
if (!effectiveType( art ))
|
|
412
|
+
throw new TypeError( 'Cyclic type definition' );
|
|
413
|
+
if (typeof step === 'string')
|
|
414
|
+
return navigationEnv( art, true ).elements[step];
|
|
415
|
+
|
|
416
|
+
if (step.action)
|
|
417
|
+
return effectiveArtFor( art, 'actions' )[step.action];
|
|
418
|
+
if (step.param)
|
|
419
|
+
return effectiveArtFor( art, 'params' )[step.param];
|
|
420
|
+
if (step.returns)
|
|
421
|
+
return effectiveArtFor( art, 'returns' );
|
|
422
|
+
if (step.enum)
|
|
423
|
+
return navigationEnv( art, true ).enum[step.enum];
|
|
424
|
+
if (step.items)
|
|
425
|
+
return effectiveType( art ).items;
|
|
426
|
+
if (step.target)
|
|
427
|
+
return targetAspect( effectiveType( art ) );
|
|
428
|
+
throw Error( `Illegal navigation step ${ Object.keys(step)[0]}` );
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function effectiveArtFor( art, property ) {
|
|
432
|
+
while (!art[property])
|
|
433
|
+
art = getOrigin( art );
|
|
434
|
+
return art[property];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function initDefinition( main ) {
|
|
438
|
+
// TODO: some --test-mode check that the argument is in ‹csn›.definitions ?
|
|
439
|
+
if (getCache( main, '$queries' ) !== undefined) // already computed
|
|
440
|
+
return main;
|
|
441
|
+
traverseDef( main, null, null, null, initNode );
|
|
442
|
+
const queries = cached( main, '$queries', allQueries );
|
|
443
|
+
for (const qcache of queries || []) {
|
|
444
|
+
const { _select } = qcache;
|
|
445
|
+
traverseType( _select, main, null, null, initNode ); // also inits elements
|
|
446
|
+
if (_select.mixin) {
|
|
447
|
+
for (const n of Object.keys( _select.mixin ))
|
|
448
|
+
setCache( _select.mixin[n], '_parent', _select ); // relevant initNode() part
|
|
449
|
+
}
|
|
385
450
|
}
|
|
451
|
+
return main;
|
|
386
452
|
}
|
|
387
453
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
454
|
+
function initNode( art, parent, kind, name ) {
|
|
455
|
+
setCache( art, '_parent', parent );
|
|
456
|
+
if (art.type || !kind || kind === 'target') // with type, top-level, query or mixin
|
|
457
|
+
return;
|
|
458
|
+
const { $origin } = art;
|
|
459
|
+
if (typeof $origin === 'object') // null, […], {…}
|
|
460
|
+
return;
|
|
461
|
+
const step = $origin || name;
|
|
462
|
+
if (parent.$origin ||
|
|
463
|
+
parent.type && kind !== 'enum' && parent.$origin !== null ||
|
|
464
|
+
getCache( parent, '$origin$step' ))
|
|
465
|
+
setCache( art, '$origin$step', (kind === 'element' ? step : { [kind]: step }) );
|
|
396
466
|
}
|
|
397
467
|
|
|
398
468
|
/**
|
|
@@ -433,111 +503,108 @@ function csnRefs( csn ) {
|
|
|
433
503
|
function resolveRef( ref, refCtx, main, query, parent, baseEnv ) {
|
|
434
504
|
const path = (typeof ref === 'string') ? [ ref ] : ref.ref;
|
|
435
505
|
if (!Array.isArray( path ))
|
|
436
|
-
throw new
|
|
506
|
+
throw new ModelError( 'References must look like {ref:[...]}' );
|
|
437
507
|
|
|
438
508
|
const head = pathId( path[0] );
|
|
509
|
+
if (main) // TODO: improve, for csnpath starting with art
|
|
510
|
+
initDefinition( main );
|
|
439
511
|
if (ref.param)
|
|
440
|
-
return resolvePath( path, main.params[head], 'param' );
|
|
512
|
+
return resolvePath( path, main.params[head], main, 'param' );
|
|
441
513
|
|
|
442
514
|
const semantics = referenceSemantics[refCtx] || {};
|
|
443
515
|
if (semantics.dynamic === 'global' || ref.global)
|
|
444
|
-
return resolvePath( path, csn.definitions[head], 'global',
|
|
516
|
+
return resolvePath( path, csn.definitions[head], null, 'global', semantics.assoc );
|
|
517
|
+
|
|
445
518
|
|
|
446
|
-
const origin = cached( main, '_origin', getOriginRaw )
|
|
447
|
-
if (origin)
|
|
448
|
-
cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
|
|
449
|
-
cached( main, '$queries', allQueries );
|
|
450
519
|
let qcache = query && cache.get( query.projection || query );
|
|
451
|
-
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
452
|
-
// CSN and again inspect some refs without calling csnRefs() before!
|
|
453
|
-
// WORKAROUND: if no cached query, a backend has changed the CSN - re-eval cache
|
|
454
|
-
if (query && !qcache) {
|
|
455
|
-
setCache( main, '$queries', allQueries( main ) );
|
|
456
|
-
qcache = cache.get( query.projection || query );
|
|
457
|
-
}
|
|
458
520
|
// first the lexical scopes (due to query hierarchy) and $magic: ---------
|
|
459
521
|
if (semantics.lexical !== false) {
|
|
460
522
|
const tryAlias = path.length > 1 || ref.expand || ref.inline;
|
|
461
|
-
let
|
|
462
|
-
while (
|
|
463
|
-
const alias = tryAlias &&
|
|
523
|
+
let ncache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
|
|
524
|
+
while (ncache) {
|
|
525
|
+
const alias = tryAlias && ncache.$aliases[head];
|
|
464
526
|
if (alias)
|
|
465
|
-
return resolvePath( path, alias._select || alias._ref,
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
527
|
+
return resolvePath( path, alias._select || alias._ref, null,
|
|
528
|
+
'alias', ncache.$queryNumber );
|
|
529
|
+
const mixin = ncache._select.mixin && ncache._select.mixin[head];
|
|
530
|
+
if (mixin && {}.hasOwnProperty.call( ncache._select.mixin, head )) {
|
|
531
|
+
setCache( mixin, '_parent', qcache._select );
|
|
532
|
+
return resolvePath( path, mixin, null, 'mixin', ncache.$queryNumber );
|
|
533
|
+
}
|
|
534
|
+
ncache = ncache.$next;
|
|
470
535
|
}
|
|
471
536
|
if (head.charAt(0) === '$') {
|
|
472
537
|
if (head !== '$self' && head !== '$projection')
|
|
473
538
|
return { scope: '$magic' };
|
|
474
539
|
const self = qcache && qcache.$queryNumber > 1 ? qcache._select : main;
|
|
475
|
-
return resolvePath( path, self, '$self' );
|
|
540
|
+
return resolvePath( path, self, null, '$self' );
|
|
476
541
|
}
|
|
477
542
|
}
|
|
478
543
|
// now the dynamic environment: ------------------------------------------
|
|
479
544
|
if (semantics.dynamic === 'target') { // ref in keys
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
// target
|
|
483
|
-
const target = csn.definitions[parent.target || parent.cast.target];
|
|
484
|
-
return resolvePath( path, target.elements[head], 'target' );
|
|
545
|
+
const target = assocTarget( parent, refCtx );
|
|
546
|
+
return resolvePath( path, target.elements[head], target, 'target' );
|
|
485
547
|
}
|
|
486
548
|
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
487
|
-
return resolvePath( path, baseEnv.elements[head], semantics.dynamic );
|
|
549
|
+
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
488
550
|
if (!query) // outside queries - TODO: items?
|
|
489
|
-
return resolvePath( path, parent.elements[head], 'parent' );
|
|
551
|
+
return resolvePath( path, parent.elements[head], parent, 'parent' );
|
|
490
552
|
|
|
491
553
|
if (semantics.dynamic === 'query')
|
|
492
554
|
// TODO: for ON condition in expand, would need to use cached _element
|
|
493
|
-
return resolvePath( path, qcache.elements[head], 'query' );
|
|
555
|
+
return resolvePath( path, qcache.elements[head], null, 'query' );
|
|
494
556
|
for (const name in qcache.$aliases) {
|
|
495
|
-
const
|
|
557
|
+
const alias = qcache.$aliases[name];
|
|
558
|
+
const found = alias.elements[head];
|
|
496
559
|
if (found)
|
|
497
|
-
return resolvePath( path, found, 'source', name )
|
|
560
|
+
return resolvePath( path, found, alias._ref, 'source', name )
|
|
498
561
|
}
|
|
499
562
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
500
|
-
throw new
|
|
563
|
+
throw new ModelError ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
501
564
|
}
|
|
502
565
|
|
|
503
566
|
/**
|
|
504
567
|
* @param {CSN.Path} path
|
|
505
568
|
* @param {CSN.Artifact} art
|
|
506
569
|
* @param {string} [scope]
|
|
570
|
+
* @param [extraInfo]
|
|
507
571
|
*/
|
|
508
|
-
function resolvePath( path, art, scope, extraInfo ) {
|
|
572
|
+
function resolvePath( path, art, parent, scope, extraInfo ) {
|
|
573
|
+
const staticAssoc = extraInfo === 'static' && scope === 'global';
|
|
509
574
|
/** @type {{idx, art?, env?}[]} */
|
|
510
575
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
511
576
|
// TODO: backends should be changed to enable uncommenting:
|
|
512
577
|
// if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
|
|
513
|
-
// throw new
|
|
578
|
+
// throw new ModelError ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
|
|
514
579
|
links[0].art = art;
|
|
515
580
|
for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
|
|
516
|
-
|
|
517
|
-
links[i - 1].env =
|
|
581
|
+
parent = navigationEnv( art, staticAssoc );
|
|
582
|
+
links[i - 1].env = parent;
|
|
518
583
|
if (typeof path[i - 1] !== 'string')
|
|
519
|
-
setCache( path[i - 1], '_env',
|
|
520
|
-
art =
|
|
584
|
+
setCache( path[i - 1], '_env', parent );
|
|
585
|
+
art = parent.elements[pathId( path[i] )];
|
|
521
586
|
if (!art) {
|
|
522
587
|
const env = links[i - 1].env;
|
|
523
588
|
const loc = env.name && env.name.$location || env.$location;
|
|
524
|
-
throw new
|
|
589
|
+
throw new ModelError ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
|
|
525
590
|
}
|
|
526
591
|
links[i].art = art;
|
|
527
592
|
}
|
|
528
593
|
const last = path[path.length - 1];
|
|
529
|
-
const fromRef = scope === 'global' && extraInfo;
|
|
594
|
+
const fromRef = scope === 'global' && extraInfo === 'target';
|
|
530
595
|
if (fromRef || typeof last !== 'string') {
|
|
531
596
|
const env = navigationEnv( art );
|
|
532
597
|
links[links.length - 1].env = env;
|
|
533
|
-
if (fromRef)
|
|
598
|
+
if (fromRef) {
|
|
534
599
|
art = env;
|
|
600
|
+
parent = null;
|
|
601
|
+
}
|
|
535
602
|
if (typeof last !== 'string')
|
|
536
603
|
setCache( last, '_env', env )
|
|
537
604
|
}
|
|
538
|
-
return (extraInfo &&
|
|
539
|
-
? { links, art, scope, $env: extraInfo }
|
|
540
|
-
: { links, art, scope };
|
|
605
|
+
return (extraInfo && scope !== 'global')
|
|
606
|
+
? { links, art, parent, scope, $env: extraInfo }
|
|
607
|
+
: { links, art, parent, scope };
|
|
541
608
|
}
|
|
542
609
|
|
|
543
610
|
/**
|
|
@@ -556,8 +623,8 @@ function csnRefs( csn ) {
|
|
|
556
623
|
if (query.ref) { // ref in from
|
|
557
624
|
// console.log('SQ:',query,cache.get(query))
|
|
558
625
|
const as = query.as || implicitAs( query.ref );
|
|
559
|
-
const _ref =
|
|
560
|
-
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
626
|
+
const _ref = cached( query, '_from', artifactFromRef )
|
|
627
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements, _parent: query };
|
|
561
628
|
}
|
|
562
629
|
else {
|
|
563
630
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -570,15 +637,18 @@ function csnRefs( csn ) {
|
|
|
570
637
|
if (select) {
|
|
571
638
|
cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
|
|
572
639
|
qcache._select = select;
|
|
640
|
+
qcache._parent = main;
|
|
573
641
|
all.push( qcache );
|
|
574
642
|
}
|
|
575
643
|
}
|
|
576
644
|
} );
|
|
577
645
|
all.forEach( function initElements( qcache, index ) {
|
|
646
|
+
qcache._parent = main;
|
|
578
647
|
qcache.$queryNumber = index + 1;
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
648
|
+
const { elements } = (index ? qcache._select : main);
|
|
649
|
+
qcache.elements = elements;
|
|
650
|
+
const { columns } = qcache._select;
|
|
651
|
+
if (elements && columns)
|
|
582
652
|
columns.map( c => initColumnElement( c, qcache ) );
|
|
583
653
|
} );
|
|
584
654
|
return all;
|
|
@@ -593,7 +663,7 @@ function csnRefs( csn ) {
|
|
|
593
663
|
function getQueryCache( parentQuery ) {
|
|
594
664
|
if (!parentQuery)
|
|
595
665
|
return { $aliases: Object.create(null) };
|
|
596
|
-
const pcache = cache.get( parentQuery );
|
|
666
|
+
const pcache = cache.get( parentQuery.projection || parentQuery );
|
|
597
667
|
if (!parentQuery.SET) // SELECT / projection: real sub query
|
|
598
668
|
return { $aliases: Object.create(null), $next: pcache };
|
|
599
669
|
// the parent query is a SET: that is not a sub query
|
|
@@ -617,7 +687,8 @@ function csnRefs( csn ) {
|
|
|
617
687
|
while (type.items)
|
|
618
688
|
type = type.items;
|
|
619
689
|
const elem = setCache( col, '_element', type.elements[as] );
|
|
620
|
-
//
|
|
690
|
+
if (elem) // TODO to.sql: something is strange if this is not set
|
|
691
|
+
setCache( elem, '_column', col );
|
|
621
692
|
if (col.expand)
|
|
622
693
|
col.expand.map( c => initColumnElement( c, elem ) );
|
|
623
694
|
}
|
|
@@ -659,8 +730,8 @@ function csnRefs( csn ) {
|
|
|
659
730
|
// i.e. a value with an `elements` property.
|
|
660
731
|
// TODO: only used in forHanaNew - move somewhere else
|
|
661
732
|
/**
|
|
662
|
-
* @param {
|
|
663
|
-
* @param {
|
|
733
|
+
* @param {object} query node (object with SET or SELECT property)
|
|
734
|
+
* @param {object} main definition
|
|
664
735
|
*/
|
|
665
736
|
function queryOrMain( query, main ) {
|
|
666
737
|
while (query.SET)
|
|
@@ -675,7 +746,7 @@ function queryOrMain( query, main ) {
|
|
|
675
746
|
// 'projection'), but `leading` can be its `query` property:
|
|
676
747
|
if ((leading === query || leading === query.query) && main.elements)
|
|
677
748
|
return main;
|
|
678
|
-
throw new
|
|
749
|
+
throw new ModelError( `Query elements not available: ${ Object.keys( query ).join('+') }`);
|
|
679
750
|
}
|
|
680
751
|
|
|
681
752
|
/**
|
|
@@ -684,7 +755,7 @@ function queryOrMain( query, main ) {
|
|
|
684
755
|
* @param {CSN.Query} query
|
|
685
756
|
* @param {CSN.QuerySelect} fromSelect
|
|
686
757
|
* @param {CSN.Query} parentQuery
|
|
687
|
-
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
|
|
758
|
+
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched, parentQuery: CSN.Query) => void} callback
|
|
688
759
|
*/
|
|
689
760
|
function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
690
761
|
const select = query.SELECT || query.projection;
|
|
@@ -739,6 +810,52 @@ function traverseExpr( expr, parentQuery, callback ) {
|
|
|
739
810
|
}
|
|
740
811
|
}
|
|
741
812
|
|
|
813
|
+
function traverseDef( node, parent, kind, name, callback ) {
|
|
814
|
+
callback ( node, parent, kind, name );
|
|
815
|
+
if (node.params) {
|
|
816
|
+
for (const n of Object.keys( node.params ))
|
|
817
|
+
traverseType( node.params[n], node, 'param', n, callback );
|
|
818
|
+
}
|
|
819
|
+
if (node.returns)
|
|
820
|
+
traverseType( node.returns, node, 'returns', true, callback );
|
|
821
|
+
traverseType( node, parent, kind, name, callback );
|
|
822
|
+
if (node.actions) {
|
|
823
|
+
for (const n of Object.keys( node.actions ))
|
|
824
|
+
traverseDef( node.actions[n], node, 'action', n, callback )
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function traverseType( node, parent, kind, name, callback ) {
|
|
829
|
+
callback ( node, parent, kind, name );
|
|
830
|
+
const target = targetAspect( node );
|
|
831
|
+
if (target && typeof target === 'object' && target.elements) {
|
|
832
|
+
callback ( target, node, 'target', true );
|
|
833
|
+
node = target;
|
|
834
|
+
}
|
|
835
|
+
else if (node.items) {
|
|
836
|
+
let items = 0;
|
|
837
|
+
while (node.items) {
|
|
838
|
+
callback ( node.items, node, 'items', ++items );
|
|
839
|
+
node = node.items;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (node.elements) {
|
|
843
|
+
for (const n of Object.keys( node.elements ))
|
|
844
|
+
traverseDef( node.elements[n], node, 'element', n, callback )
|
|
845
|
+
}
|
|
846
|
+
if (node.enum) {
|
|
847
|
+
for (const n of Object.keys( node.enum ))
|
|
848
|
+
traverseDef( node.enum[n], node, 'enum', n, callback )
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function targetAspect( art ) {
|
|
853
|
+
const { $origin } = art;
|
|
854
|
+
return art.targetAspect ||
|
|
855
|
+
$origin && typeof $origin === 'object' && !Array.isArray( $origin ) && $origin.target ||
|
|
856
|
+
art.target;
|
|
857
|
+
}
|
|
858
|
+
|
|
742
859
|
function pathId( item ) {
|
|
743
860
|
return (typeof item === 'string') ? item : item.id;
|
|
744
861
|
}
|
|
@@ -748,31 +865,43 @@ function implicitAs( ref ) {
|
|
|
748
865
|
return id.substring( id.lastIndexOf('.') + 1 );
|
|
749
866
|
}
|
|
750
867
|
|
|
868
|
+
function startCsnPath( csnPath, csn ) {
|
|
869
|
+
const head = csnPath[0];
|
|
870
|
+
if (typeof head !== 'string') {
|
|
871
|
+
const { main, parent, art } = head;
|
|
872
|
+
return { index: 1, main, parent, art };
|
|
873
|
+
}
|
|
874
|
+
if (csnPath.length < 2 || csnPath[0] !== 'definitions')
|
|
875
|
+
throw new CompilerAssertion( 'References outside definitions not supported yet');
|
|
876
|
+
const art = csn.definitions[csnPath[1]];
|
|
877
|
+
return { index: 2, main: art, parent: null, art };
|
|
878
|
+
}
|
|
879
|
+
|
|
751
880
|
/**
|
|
752
881
|
* @param {CSN.Path} csnPath
|
|
753
882
|
* @param {CSN.Model} csn
|
|
754
883
|
*/
|
|
755
884
|
function analyseCsnPath( csnPath, csn, resolve ) {
|
|
756
|
-
if (csnPath[0] !== 'definitions')
|
|
757
|
-
throw new Error( 'References outside definitions not supported yet');
|
|
758
|
-
|
|
759
885
|
/** @type {object} */
|
|
760
|
-
let obj = csn;
|
|
761
|
-
let parent = null;
|
|
762
886
|
let query = null;
|
|
763
887
|
let refCtx = null;
|
|
764
|
-
let art = null;
|
|
765
888
|
/** @type {boolean|string|number} */
|
|
766
889
|
let isName = false;
|
|
767
890
|
let baseRef = null;
|
|
768
891
|
let baseEnv = null;
|
|
769
|
-
let main = csn
|
|
892
|
+
let { index, main, parent, art } = startCsnPath( csnPath, csn );
|
|
893
|
+
let obj = art;
|
|
894
|
+
|
|
895
|
+
for (; index < csnPath.length; index++) {
|
|
896
|
+
if (!obj && !resolve)
|
|
897
|
+
// For the semantic location, use current object as best guess
|
|
898
|
+
break;
|
|
770
899
|
|
|
771
|
-
for (let index = 0; index < csnPath.length; index++) {
|
|
772
900
|
const prop = csnPath[index];
|
|
773
901
|
// array item, name/index of artifact/member, (named) argument
|
|
774
|
-
if (isName || Array.isArray( obj )) {
|
|
775
|
-
|
|
902
|
+
if (isName || Array.isArray( obj ) || prop === 'returns') {
|
|
903
|
+
// TODO: call some kind of resolve.setOrigin()
|
|
904
|
+
if (typeof isName === 'string' || prop === 'returns') {
|
|
776
905
|
parent = art;
|
|
777
906
|
art = obj[prop];
|
|
778
907
|
}
|
|
@@ -784,7 +913,8 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
784
913
|
parent = null;
|
|
785
914
|
}
|
|
786
915
|
isName = prop;
|
|
787
|
-
|
|
916
|
+
// if we want to allow auto-redirect of user-provided target with renamed keys:
|
|
917
|
+
refCtx = (refCtx === '$origin' && prop === 'keys') ? 'keys_origin' : prop;
|
|
788
918
|
}
|
|
789
919
|
else if (prop === 'items' || prop === 'returns') {
|
|
790
920
|
art = obj[prop];
|
|
@@ -800,15 +930,13 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
800
930
|
}
|
|
801
931
|
else if (prop === 'where' && refCtx === 'ref') {
|
|
802
932
|
if (resolve)
|
|
803
|
-
baseEnv = resolve.ref_where( obj, baseRef, refCtx,
|
|
804
|
-
query, parent, baseEnv );
|
|
933
|
+
baseEnv = resolve.ref_where( obj, baseRef, refCtx, main, query, parent, baseEnv );
|
|
805
934
|
refCtx = 'ref_where';
|
|
806
935
|
}
|
|
807
936
|
else if (prop === 'expand' || prop === 'inline') {
|
|
808
937
|
if (obj.ref) {
|
|
809
938
|
if (resolve)
|
|
810
|
-
baseEnv = resolve.expandInline( obj, refCtx,
|
|
811
|
-
query, parent, baseEnv );
|
|
939
|
+
baseEnv = resolve.expandInline( obj, refCtx, main, query, parent, baseEnv );
|
|
812
940
|
refCtx = prop;
|
|
813
941
|
}
|
|
814
942
|
if (prop === 'expand')
|
|
@@ -816,9 +944,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
816
944
|
}
|
|
817
945
|
else if (prop === 'on') {
|
|
818
946
|
if (refCtx === 'from')
|
|
819
|
-
refCtx = '
|
|
947
|
+
refCtx = 'on_join';
|
|
820
948
|
else if (refCtx === 'mixin')
|
|
821
|
-
refCtx = '
|
|
949
|
+
refCtx = 'on_mixin';
|
|
822
950
|
else
|
|
823
951
|
refCtx = 'on'; // will use query elements with REDIRECTED TO
|
|
824
952
|
}
|
|
@@ -832,11 +960,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
832
960
|
else if (prop !== 'xpr') {
|
|
833
961
|
refCtx = prop;
|
|
834
962
|
}
|
|
835
|
-
|
|
836
963
|
obj = obj[prop];
|
|
837
|
-
if (!obj && !resolve)
|
|
838
|
-
// For the semantic location, use current object as best guess
|
|
839
|
-
break;
|
|
840
964
|
}
|
|
841
965
|
// console.log( 'CPATH:', csnPath, refCtx, obj, parent.$location );
|
|
842
966
|
if (!resolve)
|