@sap/cds-compiler 2.11.4 → 2.13.8
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 +159 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +22 -23
- package/bin/cdsse.js +2 -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 +30 -63
- package/lib/api/options.js +5 -5
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +52 -2
- package/lib/base/messages.js +16 -26
- 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/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/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +10 -6
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +8 -6
- 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 +33 -14
- 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 +76 -38
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +204 -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 +143 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/genericAntlrParser.js +144 -54
- package/lib/language/language.g4 +424 -203
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +472 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +321 -204
- package/lib/model/csnUtils.js +224 -263
- package/lib/model/enrichCsn.js +97 -40
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +7 -6
- 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 +201 -115
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +149 -75
- 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 +35 -12
- 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 +552 -105
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +94 -28
- package/lib/transform/db/views.js +5 -4
- 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 +94 -801
- 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 +10 -27
- 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 +2 -1
- 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 -2340
- package/lib/compiler/resolver.js +0 -2988
- 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,132 +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
|
-
// console.log(art)
|
|
347
|
-
return (art.elements || art.enum || targetAspect( art ).elements)[elem];
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function targetAspect( art ) {
|
|
351
|
-
const { $origin } = art;
|
|
352
|
-
return art.targetAspect ||
|
|
353
|
-
$origin && typeof $origin === 'object' && !Array.isArray( $origin ) && $origin.target ||
|
|
354
|
-
art.target;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// From the current CSN object, set implicit origin for the next navigation step
|
|
358
|
-
function setImplicitOrigin( art, origin ) {
|
|
359
|
-
setMembersImplicit( art.actions, origin.actions );
|
|
360
|
-
setMembersImplicit( art.params, origin.params );
|
|
361
|
-
if (art.returns) {
|
|
362
|
-
art = art.returns;
|
|
363
|
-
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
364
|
-
return true; // not implicit or shortened
|
|
365
|
-
origin = effectiveType( origin.returns );
|
|
366
|
-
setCache( art, '_origin', origin );
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
while (art.items) {
|
|
370
|
-
art = art.items;
|
|
371
|
-
if (art.type || typeof art.$origin === 'object') // null, […], {…}
|
|
372
|
-
return true; // not implicit or shortened
|
|
373
|
-
origin = effectiveType( origin.items );
|
|
374
|
-
setCache( art, '_origin', origin );
|
|
375
|
-
}
|
|
376
|
-
setMembersImplicit( art.elements, origin.elements );
|
|
377
|
-
// The enum base type is _not_ where we find the origins of the enum symbols.
|
|
378
|
-
// A derived type of an enum type with individual annotations on a symbol
|
|
379
|
-
// has both 'type' and '$origin'.
|
|
380
|
-
if (!art.type || art.$origin)
|
|
381
|
-
setMembersImplicit( art.enum, origin.enum );
|
|
382
|
-
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 );
|
|
383
406
|
}
|
|
384
407
|
|
|
385
|
-
function
|
|
386
|
-
if (!
|
|
387
|
-
return;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
+
}
|
|
392
450
|
}
|
|
451
|
+
return main;
|
|
393
452
|
}
|
|
394
453
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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 }) );
|
|
403
466
|
}
|
|
404
467
|
|
|
405
468
|
/**
|
|
@@ -440,111 +503,108 @@ function csnRefs( csn ) {
|
|
|
440
503
|
function resolveRef( ref, refCtx, main, query, parent, baseEnv ) {
|
|
441
504
|
const path = (typeof ref === 'string') ? [ ref ] : ref.ref;
|
|
442
505
|
if (!Array.isArray( path ))
|
|
443
|
-
throw new
|
|
506
|
+
throw new ModelError( 'References must look like {ref:[...]}' );
|
|
444
507
|
|
|
445
508
|
const head = pathId( path[0] );
|
|
509
|
+
if (main) // TODO: improve, for csnpath starting with art
|
|
510
|
+
initDefinition( main );
|
|
446
511
|
if (ref.param)
|
|
447
|
-
return resolvePath( path, main.params[head], 'param' );
|
|
512
|
+
return resolvePath( path, main.params[head], main, 'param' );
|
|
448
513
|
|
|
449
514
|
const semantics = referenceSemantics[refCtx] || {};
|
|
450
515
|
if (semantics.dynamic === 'global' || ref.global)
|
|
451
|
-
return resolvePath( path, csn.definitions[head], 'global',
|
|
516
|
+
return resolvePath( path, csn.definitions[head], null, 'global', semantics.assoc );
|
|
517
|
+
|
|
452
518
|
|
|
453
|
-
const origin = cached( main, '_origin', getOriginRaw )
|
|
454
|
-
if (origin)
|
|
455
|
-
cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
|
|
456
|
-
cached( main, '$queries', allQueries );
|
|
457
519
|
let qcache = query && cache.get( query.projection || query );
|
|
458
|
-
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
459
|
-
// CSN and again inspect some refs without calling csnRefs() before!
|
|
460
|
-
// WORKAROUND: if no cached query, a backend has changed the CSN - re-eval cache
|
|
461
|
-
if (query && !qcache) {
|
|
462
|
-
setCache( main, '$queries', allQueries( main ) );
|
|
463
|
-
qcache = cache.get( query.projection || query );
|
|
464
|
-
}
|
|
465
520
|
// first the lexical scopes (due to query hierarchy) and $magic: ---------
|
|
466
521
|
if (semantics.lexical !== false) {
|
|
467
522
|
const tryAlias = path.length > 1 || ref.expand || ref.inline;
|
|
468
|
-
let
|
|
469
|
-
while (
|
|
470
|
-
const alias = tryAlias &&
|
|
523
|
+
let ncache = qcache && (semantics.lexical ? semantics.lexical( qcache ) : qcache);
|
|
524
|
+
while (ncache) {
|
|
525
|
+
const alias = tryAlias && ncache.$aliases[head];
|
|
471
526
|
if (alias)
|
|
472
|
-
return resolvePath( path, alias._select || alias._ref,
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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;
|
|
477
535
|
}
|
|
478
536
|
if (head.charAt(0) === '$') {
|
|
479
537
|
if (head !== '$self' && head !== '$projection')
|
|
480
538
|
return { scope: '$magic' };
|
|
481
539
|
const self = qcache && qcache.$queryNumber > 1 ? qcache._select : main;
|
|
482
|
-
return resolvePath( path, self, '$self' );
|
|
540
|
+
return resolvePath( path, self, null, '$self' );
|
|
483
541
|
}
|
|
484
542
|
}
|
|
485
543
|
// now the dynamic environment: ------------------------------------------
|
|
486
544
|
if (semantics.dynamic === 'target') { // ref in keys
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
// target
|
|
490
|
-
const target = csn.definitions[parent.target || parent.$origin && parent.$origin.target || parent.cast.target];
|
|
491
|
-
return resolvePath( path, target.elements[head], 'target' );
|
|
545
|
+
const target = assocTarget( parent, refCtx );
|
|
546
|
+
return resolvePath( path, target.elements[head], target, 'target' );
|
|
492
547
|
}
|
|
493
548
|
if (baseEnv) // ref-target (filter condition), expand, inline
|
|
494
|
-
return resolvePath( path, baseEnv.elements[head], semantics.dynamic );
|
|
549
|
+
return resolvePath( path, baseEnv.elements[head], baseEnv, semantics.dynamic );
|
|
495
550
|
if (!query) // outside queries - TODO: items?
|
|
496
|
-
return resolvePath( path, parent.elements[head], 'parent' );
|
|
551
|
+
return resolvePath( path, parent.elements[head], parent, 'parent' );
|
|
497
552
|
|
|
498
553
|
if (semantics.dynamic === 'query')
|
|
499
554
|
// TODO: for ON condition in expand, would need to use cached _element
|
|
500
|
-
return resolvePath( path, qcache.elements[head], 'query' );
|
|
555
|
+
return resolvePath( path, qcache.elements[head], null, 'query' );
|
|
501
556
|
for (const name in qcache.$aliases) {
|
|
502
|
-
const
|
|
557
|
+
const alias = qcache.$aliases[name];
|
|
558
|
+
const found = alias.elements[head];
|
|
503
559
|
if (found)
|
|
504
|
-
return resolvePath( path, found, 'source', name )
|
|
560
|
+
return resolvePath( path, found, alias._ref, 'source', name )
|
|
505
561
|
}
|
|
506
562
|
// console.log(query.SELECT,qcache,qcache.$next,main)
|
|
507
|
-
throw new
|
|
563
|
+
throw new ModelError ( `Path item ${ 0 }=${ head } refers to nothing, refCtx: ${ refCtx }` );
|
|
508
564
|
}
|
|
509
565
|
|
|
510
566
|
/**
|
|
511
567
|
* @param {CSN.Path} path
|
|
512
568
|
* @param {CSN.Artifact} art
|
|
513
569
|
* @param {string} [scope]
|
|
570
|
+
* @param [extraInfo]
|
|
514
571
|
*/
|
|
515
|
-
function resolvePath( path, art, scope, extraInfo ) {
|
|
572
|
+
function resolvePath( path, art, parent, scope, extraInfo ) {
|
|
573
|
+
const staticAssoc = extraInfo === 'static' && scope === 'global';
|
|
516
574
|
/** @type {{idx, art?, env?}[]} */
|
|
517
575
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
518
576
|
// TODO: backends should be changed to enable uncommenting:
|
|
519
577
|
// if (!art) // does not work with test3/Associations/KeylessManagedAssociation/
|
|
520
|
-
// throw new
|
|
578
|
+
// throw new ModelError ( `Path item ${ 0 }=${ pathId( path[0] ) } refers to nothing, scope: ${ scope }`);
|
|
521
579
|
links[0].art = art;
|
|
522
580
|
for (let i = 1; i < links.length; ++i) { // yes, starting at 1, links[0] is set above
|
|
523
|
-
|
|
524
|
-
links[i - 1].env =
|
|
581
|
+
parent = navigationEnv( art, staticAssoc );
|
|
582
|
+
links[i - 1].env = parent;
|
|
525
583
|
if (typeof path[i - 1] !== 'string')
|
|
526
|
-
setCache( path[i - 1], '_env',
|
|
527
|
-
art =
|
|
584
|
+
setCache( path[i - 1], '_env', parent );
|
|
585
|
+
art = parent.elements[pathId( path[i] )];
|
|
528
586
|
if (!art) {
|
|
529
587
|
const env = links[i - 1].env;
|
|
530
588
|
const loc = env.name && env.name.$location || env.$location;
|
|
531
|
-
throw new
|
|
589
|
+
throw new ModelError ( `Path item ${ i }=${ pathId( path[i] ) } on ${ locationString( loc ) } refers to nothing` );
|
|
532
590
|
}
|
|
533
591
|
links[i].art = art;
|
|
534
592
|
}
|
|
535
593
|
const last = path[path.length - 1];
|
|
536
|
-
const fromRef = scope === 'global' && extraInfo;
|
|
594
|
+
const fromRef = scope === 'global' && extraInfo === 'target';
|
|
537
595
|
if (fromRef || typeof last !== 'string') {
|
|
538
596
|
const env = navigationEnv( art );
|
|
539
597
|
links[links.length - 1].env = env;
|
|
540
|
-
if (fromRef)
|
|
598
|
+
if (fromRef) {
|
|
541
599
|
art = env;
|
|
600
|
+
parent = null;
|
|
601
|
+
}
|
|
542
602
|
if (typeof last !== 'string')
|
|
543
603
|
setCache( last, '_env', env )
|
|
544
604
|
}
|
|
545
|
-
return (extraInfo &&
|
|
546
|
-
? { links, art, scope, $env: extraInfo }
|
|
547
|
-
: { links, art, scope };
|
|
605
|
+
return (extraInfo && scope !== 'global')
|
|
606
|
+
? { links, art, parent, scope, $env: extraInfo }
|
|
607
|
+
: { links, art, parent, scope };
|
|
548
608
|
}
|
|
549
609
|
|
|
550
610
|
/**
|
|
@@ -563,8 +623,8 @@ function csnRefs( csn ) {
|
|
|
563
623
|
if (query.ref) { // ref in from
|
|
564
624
|
// console.log('SQ:',query,cache.get(query))
|
|
565
625
|
const as = query.as || implicitAs( query.ref );
|
|
566
|
-
const _ref =
|
|
567
|
-
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 };
|
|
568
628
|
}
|
|
569
629
|
else {
|
|
570
630
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -577,15 +637,18 @@ function csnRefs( csn ) {
|
|
|
577
637
|
if (select) {
|
|
578
638
|
cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
|
|
579
639
|
qcache._select = select;
|
|
640
|
+
qcache._parent = main;
|
|
580
641
|
all.push( qcache );
|
|
581
642
|
}
|
|
582
643
|
}
|
|
583
644
|
} );
|
|
584
645
|
all.forEach( function initElements( qcache, index ) {
|
|
646
|
+
qcache._parent = main;
|
|
585
647
|
qcache.$queryNumber = index + 1;
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
648
|
+
const { elements } = (index ? qcache._select : main);
|
|
649
|
+
qcache.elements = elements;
|
|
650
|
+
const { columns } = qcache._select;
|
|
651
|
+
if (elements && columns)
|
|
589
652
|
columns.map( c => initColumnElement( c, qcache ) );
|
|
590
653
|
} );
|
|
591
654
|
return all;
|
|
@@ -624,7 +687,8 @@ function csnRefs( csn ) {
|
|
|
624
687
|
while (type.items)
|
|
625
688
|
type = type.items;
|
|
626
689
|
const elem = setCache( col, '_element', type.elements[as] );
|
|
627
|
-
//
|
|
690
|
+
if (elem) // TODO to.sql: something is strange if this is not set
|
|
691
|
+
setCache( elem, '_column', col );
|
|
628
692
|
if (col.expand)
|
|
629
693
|
col.expand.map( c => initColumnElement( c, elem ) );
|
|
630
694
|
}
|
|
@@ -666,8 +730,8 @@ function csnRefs( csn ) {
|
|
|
666
730
|
// i.e. a value with an `elements` property.
|
|
667
731
|
// TODO: only used in forHanaNew - move somewhere else
|
|
668
732
|
/**
|
|
669
|
-
* @param {
|
|
670
|
-
* @param {
|
|
733
|
+
* @param {object} query node (object with SET or SELECT property)
|
|
734
|
+
* @param {object} main definition
|
|
671
735
|
*/
|
|
672
736
|
function queryOrMain( query, main ) {
|
|
673
737
|
while (query.SET)
|
|
@@ -682,7 +746,7 @@ function queryOrMain( query, main ) {
|
|
|
682
746
|
// 'projection'), but `leading` can be its `query` property:
|
|
683
747
|
if ((leading === query || leading === query.query) && main.elements)
|
|
684
748
|
return main;
|
|
685
|
-
throw new
|
|
749
|
+
throw new ModelError( `Query elements not available: ${ Object.keys( query ).join('+') }`);
|
|
686
750
|
}
|
|
687
751
|
|
|
688
752
|
/**
|
|
@@ -691,7 +755,7 @@ function queryOrMain( query, main ) {
|
|
|
691
755
|
* @param {CSN.Query} query
|
|
692
756
|
* @param {CSN.QuerySelect} fromSelect
|
|
693
757
|
* @param {CSN.Query} parentQuery
|
|
694
|
-
* @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
|
|
695
759
|
*/
|
|
696
760
|
function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
697
761
|
const select = query.SELECT || query.projection;
|
|
@@ -746,6 +810,52 @@ function traverseExpr( expr, parentQuery, callback ) {
|
|
|
746
810
|
}
|
|
747
811
|
}
|
|
748
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
|
+
|
|
749
859
|
function pathId( item ) {
|
|
750
860
|
return (typeof item === 'string') ? item : item.id;
|
|
751
861
|
}
|
|
@@ -755,31 +865,43 @@ function implicitAs( ref ) {
|
|
|
755
865
|
return id.substring( id.lastIndexOf('.') + 1 );
|
|
756
866
|
}
|
|
757
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
|
+
|
|
758
880
|
/**
|
|
759
881
|
* @param {CSN.Path} csnPath
|
|
760
882
|
* @param {CSN.Model} csn
|
|
761
883
|
*/
|
|
762
884
|
function analyseCsnPath( csnPath, csn, resolve ) {
|
|
763
|
-
if (csnPath[0] !== 'definitions')
|
|
764
|
-
throw new Error( 'References outside definitions not supported yet');
|
|
765
|
-
|
|
766
885
|
/** @type {object} */
|
|
767
|
-
let obj = csn;
|
|
768
|
-
let parent = null;
|
|
769
886
|
let query = null;
|
|
770
887
|
let refCtx = null;
|
|
771
|
-
let art = null;
|
|
772
888
|
/** @type {boolean|string|number} */
|
|
773
889
|
let isName = false;
|
|
774
890
|
let baseRef = null;
|
|
775
891
|
let baseEnv = null;
|
|
776
|
-
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;
|
|
777
899
|
|
|
778
|
-
for (let index = 0; index < csnPath.length; index++) {
|
|
779
900
|
const prop = csnPath[index];
|
|
780
901
|
// array item, name/index of artifact/member, (named) argument
|
|
781
|
-
if (isName || Array.isArray( obj )) {
|
|
782
|
-
|
|
902
|
+
if (isName || Array.isArray( obj ) || prop === 'returns') {
|
|
903
|
+
// TODO: call some kind of resolve.setOrigin()
|
|
904
|
+
if (typeof isName === 'string' || prop === 'returns') {
|
|
783
905
|
parent = art;
|
|
784
906
|
art = obj[prop];
|
|
785
907
|
}
|
|
@@ -791,7 +913,8 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
791
913
|
parent = null;
|
|
792
914
|
}
|
|
793
915
|
isName = prop;
|
|
794
|
-
|
|
916
|
+
// if we want to allow auto-redirect of user-provided target with renamed keys:
|
|
917
|
+
refCtx = (refCtx === '$origin' && prop === 'keys') ? 'keys_origin' : prop;
|
|
795
918
|
}
|
|
796
919
|
else if (prop === 'items' || prop === 'returns') {
|
|
797
920
|
art = obj[prop];
|
|
@@ -807,15 +930,13 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
807
930
|
}
|
|
808
931
|
else if (prop === 'where' && refCtx === 'ref') {
|
|
809
932
|
if (resolve)
|
|
810
|
-
baseEnv = resolve.ref_where( obj, baseRef, refCtx,
|
|
811
|
-
query, parent, baseEnv );
|
|
933
|
+
baseEnv = resolve.ref_where( obj, baseRef, refCtx, main, query, parent, baseEnv );
|
|
812
934
|
refCtx = 'ref_where';
|
|
813
935
|
}
|
|
814
936
|
else if (prop === 'expand' || prop === 'inline') {
|
|
815
937
|
if (obj.ref) {
|
|
816
938
|
if (resolve)
|
|
817
|
-
baseEnv = resolve.expandInline( obj, refCtx,
|
|
818
|
-
query, parent, baseEnv );
|
|
939
|
+
baseEnv = resolve.expandInline( obj, refCtx, main, query, parent, baseEnv );
|
|
819
940
|
refCtx = prop;
|
|
820
941
|
}
|
|
821
942
|
if (prop === 'expand')
|
|
@@ -823,9 +944,9 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
823
944
|
}
|
|
824
945
|
else if (prop === 'on') {
|
|
825
946
|
if (refCtx === 'from')
|
|
826
|
-
refCtx = '
|
|
947
|
+
refCtx = 'on_join';
|
|
827
948
|
else if (refCtx === 'mixin')
|
|
828
|
-
refCtx = '
|
|
949
|
+
refCtx = 'on_mixin';
|
|
829
950
|
else
|
|
830
951
|
refCtx = 'on'; // will use query elements with REDIRECTED TO
|
|
831
952
|
}
|
|
@@ -839,11 +960,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
839
960
|
else if (prop !== 'xpr') {
|
|
840
961
|
refCtx = prop;
|
|
841
962
|
}
|
|
842
|
-
|
|
843
963
|
obj = obj[prop];
|
|
844
|
-
if (!obj && !resolve)
|
|
845
|
-
// For the semantic location, use current object as best guess
|
|
846
|
-
break;
|
|
847
964
|
}
|
|
848
965
|
// console.log( 'CPATH:', csnPath, refCtx, obj, parent.$location );
|
|
849
966
|
if (!resolve)
|