@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/main.d.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
//
|
|
5
5
|
// These types are improved step by step and use a lot any types at the moment.
|
|
6
6
|
|
|
7
|
+
// Author's note: All "options" interfaces should actually be types. However, due to
|
|
8
|
+
// https://github.com/TypeStrong/typedoc/issues/1519 we can't use
|
|
9
|
+
// intersection types at the moment.
|
|
10
|
+
|
|
7
11
|
export = compiler;
|
|
8
12
|
|
|
9
13
|
declare namespace compiler {
|
|
@@ -11,7 +15,7 @@ declare namespace compiler {
|
|
|
11
15
|
/**
|
|
12
16
|
* Options used by the core compiler and all backends.
|
|
13
17
|
*/
|
|
14
|
-
export
|
|
18
|
+
export interface Options {
|
|
15
19
|
[option: string]: any,
|
|
16
20
|
|
|
17
21
|
/**
|
|
@@ -57,6 +61,102 @@ declare namespace compiler {
|
|
|
57
61
|
dictionaryPrototype?: any
|
|
58
62
|
}
|
|
59
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Options used by OData backends. Includes options for the OData
|
|
66
|
+
* transformer as well as for rendering EDM and EDMX.
|
|
67
|
+
*/
|
|
68
|
+
export interface ODataOptions extends Options {
|
|
69
|
+
/**
|
|
70
|
+
* OData version for output files. Either 'v4' or 'v2'.
|
|
71
|
+
*
|
|
72
|
+
* @default 'v4'
|
|
73
|
+
*/
|
|
74
|
+
odataVersion?: string | 'v4' | 'v2'
|
|
75
|
+
/**
|
|
76
|
+
* Whether to generate OData as flat or as structured.
|
|
77
|
+
* Structured is only supported for OData v4.
|
|
78
|
+
*
|
|
79
|
+
* @default 'flat'
|
|
80
|
+
*/
|
|
81
|
+
odataFormat?: string | 'flat' | 'structured'
|
|
82
|
+
/**
|
|
83
|
+
* Naming mode used by the corresponding SQL.
|
|
84
|
+
*
|
|
85
|
+
* @default 'plain'
|
|
86
|
+
*/
|
|
87
|
+
sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
|
|
88
|
+
/**
|
|
89
|
+
* If `true`, `cds.Compositions` are rendered as `edm:NavigationProperty` with the additional
|
|
90
|
+
* attribute `ContainsTarget="true"` and all contained entities (composition targets) have no
|
|
91
|
+
* `edm.EntitySet`.
|
|
92
|
+
*
|
|
93
|
+
* @note Only available for OData v4 EDM(X) rendering.
|
|
94
|
+
* @default false
|
|
95
|
+
*/
|
|
96
|
+
odataContainment?: boolean
|
|
97
|
+
/**
|
|
98
|
+
* If `true`, render generated foreign keys for managed associations.
|
|
99
|
+
* By default foreign keys are never visible in structured OData APIs.
|
|
100
|
+
*
|
|
101
|
+
* @note Only available for structured OData v4 EDM(X) rendering.
|
|
102
|
+
* @default false
|
|
103
|
+
*/
|
|
104
|
+
odataForeignKeys?: boolean
|
|
105
|
+
/**
|
|
106
|
+
* If `true`, association targets outside of the current service are added as
|
|
107
|
+
* `edm.EntityType` that only exposes their primary keys and have no `edm.EntitySet`.
|
|
108
|
+
* If the original association target is a service member, a corresponding `edm.Schema`
|
|
109
|
+
* representing the namespace of that service is added to `edm.Services`. All association
|
|
110
|
+
* targets that are no service members are collected in an `edm.Schema` with namespace `root`.
|
|
111
|
+
*
|
|
112
|
+
* @note Only valid for structured OData v4 EDM(X) rendering.
|
|
113
|
+
* @default false
|
|
114
|
+
* @since v2.1.0
|
|
115
|
+
*/
|
|
116
|
+
odataProxies?: boolean
|
|
117
|
+
/**
|
|
118
|
+
* This option is an extension to `odataProxies`.
|
|
119
|
+
* If `true`, an `edm:Reference` instead of a proxy `edm.EntityType` is rendered for each
|
|
120
|
+
* association target that is a service member outside the current service instead of proxies.
|
|
121
|
+
*
|
|
122
|
+
* @note Only valid for structured OData v4 EDM(X) rendering.
|
|
123
|
+
* @default false
|
|
124
|
+
* @since v2.1.0
|
|
125
|
+
*/
|
|
126
|
+
odataXServiceRefs?: boolean
|
|
127
|
+
/**
|
|
128
|
+
* The OData specification requires that all primary keys of the principal must be used as
|
|
129
|
+
* referential constraints. If an association is modelled with only a partial key, no
|
|
130
|
+
* referential constraints are added. If `true`, partial constraints are rendered for
|
|
131
|
+
* backwards compatibility and mocking scenarios. A spec violation warning is raised for
|
|
132
|
+
* each incomplete constraint.
|
|
133
|
+
*
|
|
134
|
+
* @note Only valid for OData v2 CSN transformation.
|
|
135
|
+
* @default false
|
|
136
|
+
* @since v2.2.6
|
|
137
|
+
*/
|
|
138
|
+
odataV2PartialConstr?: boolean
|
|
139
|
+
/**
|
|
140
|
+
* Service name for which EDMX or EDM shall be rendered.
|
|
141
|
+
*
|
|
142
|
+
* @note Only available for `to.edmx()` and `to.edm()`. For `to.edmx.all()`
|
|
143
|
+
* and `to.edm.all()`, use `serviceNames` instead.
|
|
144
|
+
*
|
|
145
|
+
* @see serviceNames
|
|
146
|
+
*/
|
|
147
|
+
service?: string
|
|
148
|
+
/**
|
|
149
|
+
* Array of service names for which EDMX or EDM shall be rendered.
|
|
150
|
+
* If unspecified, all services are rendered.
|
|
151
|
+
*
|
|
152
|
+
* @note Only available for `to.edmx.all()` and `to.edm.all()`. For `to.edmx()`
|
|
153
|
+
* and `to.edm()`, use `service` instead.
|
|
154
|
+
*
|
|
155
|
+
* @see service
|
|
156
|
+
*/
|
|
157
|
+
serviceNames?: string[]
|
|
158
|
+
}
|
|
159
|
+
|
|
60
160
|
/**
|
|
61
161
|
* The compiler's package version.
|
|
62
162
|
* For more details on versioning and SemVer, see `doc/Versioning.md`
|
|
@@ -83,6 +183,7 @@ declare namespace compiler {
|
|
|
83
183
|
* {@link CompilationError} containing a vector of individual errors.
|
|
84
184
|
*
|
|
85
185
|
* @param filenames Array of files that should be compiled.
|
|
186
|
+
* @param dir Working directory. Relative paths in `filenames` will be resolved relatively to this directory.
|
|
86
187
|
* @param options Compiler options. If you do not set `messages`, they will be printed to console.
|
|
87
188
|
* @param fileCache A dictionary of absolute file names to the file content with values:
|
|
88
189
|
* - false: the file does not exist
|
|
@@ -138,7 +239,7 @@ declare namespace compiler {
|
|
|
138
239
|
* _Note_: Sorting is done in-place.
|
|
139
240
|
*
|
|
140
241
|
* Example of sorted messages:
|
|
141
|
-
* ```
|
|
242
|
+
* ```
|
|
142
243
|
* A.cds:1:11: Info id-3: First message text (in entity:“E”/element:“c”)
|
|
143
244
|
* A.cds:8:11: Error id-5: Another message text (in entity:“C”/element:“g”)
|
|
144
245
|
* B.cds:3:10: Debug id-7: First message text (in entity:“B”/element:“e”)
|
|
@@ -188,10 +289,11 @@ declare namespace compiler {
|
|
|
188
289
|
* form (i.e. one line)
|
|
189
290
|
*
|
|
190
291
|
* Example:
|
|
191
|
-
* ```
|
|
292
|
+
* ```
|
|
192
293
|
* <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
|
|
193
294
|
* ```
|
|
194
295
|
*
|
|
296
|
+
* @param msg Compiler message which shall be stringified.
|
|
195
297
|
* @param normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
|
|
196
298
|
* @param noMessageId If true, the message ID will _not_ be part of the string.
|
|
197
299
|
* @param noHome If true, the semantic location will _not_ be part of the string.
|
|
@@ -201,26 +303,30 @@ declare namespace compiler {
|
|
|
201
303
|
/**
|
|
202
304
|
* Returns a message string with file- and semantic location if present
|
|
203
305
|
* in multiline form.
|
|
204
|
-
* The error (+ message id)
|
|
205
|
-
* run on a TTY.
|
|
306
|
+
* The error (+ message id) can colored according to their severity.
|
|
206
307
|
*
|
|
207
308
|
* Example:
|
|
208
|
-
* ```
|
|
209
|
-
* Error[message-id]: Can't find type `nu` in this scope
|
|
309
|
+
* ```
|
|
310
|
+
* Error[message-id]: Can't find type `nu` in this scope
|
|
210
311
|
* |
|
|
211
|
-
* <source>.cds:3:11, at entity:“E”
|
|
312
|
+
* <source>.cds:3:11, at entity:“E”/element:“e”
|
|
212
313
|
* ```
|
|
213
314
|
*
|
|
214
315
|
* @param config.normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
|
|
215
316
|
* @param config.noMessageId If true, no messages id (in brackets) will be shown.
|
|
216
317
|
* @param config.hintExplanation If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
|
|
217
318
|
* @param config.withLineSpacer If true, an additional line (with `|`) will be inserted between message and location.
|
|
319
|
+
* @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
|
|
320
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
321
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
322
|
+
* unset.
|
|
218
323
|
*/
|
|
219
324
|
export function messageStringMultiline(msg: CompileMessage, config?: {
|
|
220
325
|
normalizeFilename?: boolean
|
|
221
326
|
noMessageId?: boolean
|
|
222
327
|
hintExplanation?: boolean
|
|
223
328
|
withLineSpacer?: boolean
|
|
329
|
+
color?: boolean | 'auto'
|
|
224
330
|
}): string;
|
|
225
331
|
|
|
226
332
|
/**
|
|
@@ -233,16 +339,24 @@ declare namespace compiler {
|
|
|
233
339
|
* All lines are prepended by a pipe (`|`) and show the corresponding line number.
|
|
234
340
|
*
|
|
235
341
|
* Example Output:
|
|
236
|
-
* ```
|
|
342
|
+
* ```
|
|
237
343
|
* |
|
|
238
344
|
* 13 | num * nu
|
|
239
345
|
* | ^^
|
|
240
346
|
* ```
|
|
241
347
|
*
|
|
242
|
-
* @param sourceLines
|
|
243
|
-
* @param msg
|
|
348
|
+
* @param sourceLines The source code split up into lines, e.g. by `str.split(/\r\n?|\n/);`.
|
|
349
|
+
* @param msg Message whose location is used to print the message context.
|
|
350
|
+
* @param config Configuration for the message context.
|
|
351
|
+
* @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
|
|
352
|
+
* coloring will be used. If 'auto', we will decide based on certain factors such
|
|
353
|
+
* as whether the shell is a TTY and whether the environment variable 'NO_COLOR' is
|
|
354
|
+
* unset.
|
|
355
|
+
|
|
244
356
|
*/
|
|
245
|
-
export function messageContext(sourceLines: string[], msg: CompileMessage
|
|
357
|
+
export function messageContext(sourceLines: string[], msg: CompileMessage, config?: {
|
|
358
|
+
color?: boolean | 'auto'
|
|
359
|
+
}): string;
|
|
246
360
|
|
|
247
361
|
/**
|
|
248
362
|
* Get an explanatory text for a complicated compiler message with ID
|
|
@@ -316,21 +430,26 @@ declare namespace compiler {
|
|
|
316
430
|
* @alias for
|
|
317
431
|
*/
|
|
318
432
|
export namespace For {
|
|
319
|
-
|
|
433
|
+
/**
|
|
434
|
+
* Transform the given (generic) CSN into one that is used for OData.
|
|
435
|
+
* Changes include flattening, type resolution and more, according to
|
|
436
|
+
* the provided options.
|
|
437
|
+
*/
|
|
438
|
+
function odata(csn: CSN, options: ODataOptions): any;
|
|
320
439
|
}
|
|
321
440
|
|
|
322
441
|
export namespace to {
|
|
323
442
|
function cdl(csn: CSN, options: Options): object;
|
|
324
443
|
function sql(csn: CSN, options: Options): any;
|
|
325
444
|
|
|
326
|
-
function edm(csn: CSN, options:
|
|
445
|
+
function edm(csn: CSN, options: ODataOptions): any;
|
|
327
446
|
namespace edm {
|
|
328
|
-
function all(csn: CSN, options:
|
|
447
|
+
function all(csn: CSN, options: ODataOptions): any;
|
|
329
448
|
}
|
|
330
449
|
|
|
331
|
-
function edmx(csn: CSN, options:
|
|
450
|
+
function edmx(csn: CSN, options: ODataOptions): any;
|
|
332
451
|
namespace edmx {
|
|
333
|
-
function all(csn: CSN, options:
|
|
452
|
+
function all(csn: CSN, options: ODataOptions): any;
|
|
334
453
|
}
|
|
335
454
|
|
|
336
455
|
function hdbcds(csn: CSN, options: Options): any;
|
package/lib/main.js
CHANGED
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
const backends = require('./backends');
|
|
17
17
|
const { odata, cdl, sql, hdi, hdbcds, edm, edmx } = require('./api/main');
|
|
18
18
|
const { getArtifactDatabaseNameOf, getElementDatabaseNameOf } = require('./model/csnUtils');
|
|
19
|
+
const { traverseCsn } = require('./model/api');
|
|
19
20
|
const { createMessageFunctions, sortMessages, sortMessagesSeverityAware, deduplicateMessages } = require('./base/messages');
|
|
20
21
|
|
|
21
22
|
const parseLanguage = require('./language/antlrParser');
|
|
22
23
|
const { parseX, compileX, compileSyncX, compileSourcesX, InvocationError } = require('./compiler');
|
|
24
|
+
const { fns } = require('./compiler/shared');
|
|
23
25
|
const { define } = require('./compiler/definer');
|
|
24
26
|
|
|
25
27
|
// The compiler version (taken from package.json)
|
|
@@ -42,13 +44,14 @@ const { compactModel, compactQuery, compactExpr } = require('./json/to-csn')
|
|
|
42
44
|
function parseCdl( cdl, filename, options = {} ) {
|
|
43
45
|
options = Object.assign( {}, options, { parseCdl: true } );
|
|
44
46
|
const sources = Object.create(null);
|
|
45
|
-
const model = { sources, options };
|
|
47
|
+
const model = { sources, options, $functions: {}, $volatileFunctions: {} };
|
|
46
48
|
const messageFunctions = createMessageFunctions( options, 'parse', model );
|
|
47
49
|
model.$messageFunctions = messageFunctions;
|
|
48
50
|
|
|
49
51
|
const xsn = parseLanguage( cdl, filename, Object.assign( { parseOnly: true }, options ),
|
|
50
52
|
messageFunctions );
|
|
51
53
|
sources[filename] = xsn;
|
|
54
|
+
fns( model );
|
|
52
55
|
define( model );
|
|
53
56
|
messageFunctions.throwWithError();
|
|
54
57
|
return compactModel( model );
|
|
@@ -111,6 +114,9 @@ module.exports = {
|
|
|
111
114
|
getArtifactCdsPersistenceName: getArtifactDatabaseNameOf,
|
|
112
115
|
getElementCdsPersistenceName: getElementDatabaseNameOf,
|
|
113
116
|
|
|
117
|
+
// Other API functions:
|
|
118
|
+
traverseCsn,
|
|
119
|
+
|
|
114
120
|
// INTERNAL functions for the cds-lsp package and friends - before you use
|
|
115
121
|
// it, you MUST talk with us - there can be potential incompatibilities with
|
|
116
122
|
// new releases (even having the same major version):
|
package/lib/model/api.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Miscellaneous CSN functions we put into our compiler API
|
|
2
|
+
|
|
3
|
+
// Do not change at will - they are in the compiler API!
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Dictionary of default traversal functions for function `traverseCsn`.
|
|
7
|
+
* It maps CSN property names to functions which are used by default
|
|
8
|
+
* to traverse the CSN node which is the value of the corresponding property.
|
|
9
|
+
* Users specify their own traversal function via argument `userFunctions`.
|
|
10
|
+
*
|
|
11
|
+
* Each function in `userFunctions` and `defaultFunctions` is called with:
|
|
12
|
+
* - `userFunctions`
|
|
13
|
+
* - the current CSN node, i.e. ‹parent node›.‹property name›
|
|
14
|
+
* - the the ‹parent node›
|
|
15
|
+
* - the ‹property name› (might be useful if the same function is used for several props)
|
|
16
|
+
*/
|
|
17
|
+
const defaultFunctions = {
|
|
18
|
+
'@': () => { /* do not traverse annotation assignments */ },
|
|
19
|
+
args: dictionary,
|
|
20
|
+
elements: dictionary,
|
|
21
|
+
enum: dictionary,
|
|
22
|
+
params: dictionary,
|
|
23
|
+
actions: dictionary,
|
|
24
|
+
mixin: dictionary,
|
|
25
|
+
definitions: dictionary,
|
|
26
|
+
'$': () => { /* do not traverse properties starting with '$' */},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Traverse the CSN node `csn`.
|
|
31
|
+
* If `csn` is an array, call it recursively on each array item.
|
|
32
|
+
* If `csn` is an(other) object, call a function on each property:
|
|
33
|
+
* - The property name is a used as key in argument `userFunctions` and the
|
|
34
|
+
* constant `defaultFunctions` above to get the function which is called on
|
|
35
|
+
* the property value, see `defaultFunctions` for details.
|
|
36
|
+
* - If no function is found with the property name, try to find one with the first char.
|
|
37
|
+
* - If still not found, call `traverseCsn` recursively.
|
|
38
|
+
*
|
|
39
|
+
* The functions in `userFunctions` are usually transformer functions, which
|
|
40
|
+
* change the input CSN destructively.
|
|
41
|
+
*/
|
|
42
|
+
function traverseCsn( userFunctions, csn ) {
|
|
43
|
+
if (!csn || typeof csn !== 'object')
|
|
44
|
+
return;
|
|
45
|
+
if (Array.isArray( csn )) {
|
|
46
|
+
csn.forEach( node => traverseCsn( userFunctions, node ) );
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (const prop of Object.keys( csn )) {
|
|
50
|
+
const func = userFunctions[prop] || defaultFunctions[prop] ||
|
|
51
|
+
userFunctions[prop.charAt(0)] || defaultFunctions[prop.charAt(0)] ||
|
|
52
|
+
traverseCsn;
|
|
53
|
+
func( userFunctions, csn[prop], csn, prop );
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// people might want to have their own traversal function for `elements`, etc:
|
|
58
|
+
traverseCsn.dictionary = dictionary;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Traverse the CSN dictionary node `csn`.
|
|
62
|
+
* Call `traverseCsn` on each property value in `csn`, passing down `userFunctions`.
|
|
63
|
+
*/
|
|
64
|
+
function dictionary( userFunctions, csn ) {
|
|
65
|
+
if (!csn || typeof csn !== 'object')
|
|
66
|
+
return;
|
|
67
|
+
if (Array.isArray( csn )) { // args can be both array and dictionary
|
|
68
|
+
csn.forEach( node => traverseCsn( userFunctions, node ) );
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
for (const name of Object.keys( csn ))
|
|
72
|
+
traverseCsn( userFunctions, csn[name] );
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
module.exports = {
|
|
77
|
+
traverseCsn,
|
|
78
|
+
};
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -175,6 +175,7 @@ const referenceSemantics = {
|
|
|
175
175
|
type: { lexical: false, dynamic: 'global' },
|
|
176
176
|
includes: { lexical: false, dynamic: 'global' },
|
|
177
177
|
target: { lexical: false, dynamic: 'global' },
|
|
178
|
+
targetAspect: { lexical: false, dynamic: 'global' },
|
|
178
179
|
from: { lexical: false, dynamic: 'global' },
|
|
179
180
|
keys: { lexical: false, dynamic: 'target' },
|
|
180
181
|
excluding: { lexical: false, dynamic: 'source' },
|
|
@@ -224,17 +225,19 @@ function csnRefs( csn ) {
|
|
|
224
225
|
const cachedType = getCache( art, '_effectiveType' );
|
|
225
226
|
if (cachedType !== undefined)
|
|
226
227
|
return cachedType;
|
|
227
|
-
else if (!art.type && !art.$origin ||
|
|
228
|
-
art.elements || art.target || art.targetAspect || art.enum)
|
|
229
|
-
return setCache( art, '_effectiveType', art );
|
|
230
228
|
|
|
231
229
|
const chain = [];
|
|
232
|
-
|
|
230
|
+
let origin;
|
|
231
|
+
while (getCache( art, '_effectiveType' ) === undefined &&
|
|
232
|
+
(origin = cached( art, '_origin', getOriginRaw )) &&
|
|
233
233
|
!art.elements && !art.target && !art.targetAspect && !art.enum && !art.items) {
|
|
234
234
|
chain.push( art );
|
|
235
235
|
setCache( art, '_effectiveType', 0 ); // initial setting in case of cycles
|
|
236
|
-
art =
|
|
236
|
+
art = origin;
|
|
237
237
|
}
|
|
238
|
+
if (!chain.length)
|
|
239
|
+
return setCache( art, '_effectiveType', art );
|
|
240
|
+
|
|
238
241
|
if (getCache( art, '_effectiveType' ) === 0)
|
|
239
242
|
throw new Error( 'Circular type reference');
|
|
240
243
|
const type = getCache( art, '_effectiveType' ) || art;
|
|
@@ -250,10 +253,16 @@ function csnRefs( csn ) {
|
|
|
250
253
|
// here, we do not care whether it is semantically ok to navigate into sub
|
|
251
254
|
// elements of array items (that is the task of the core compiler /
|
|
252
255
|
// semantic check)
|
|
253
|
-
while (type.items)
|
|
256
|
+
while (type.items) {
|
|
257
|
+
cached( type, '$origin', _a => setImplicitOrigin( type, origin ) );
|
|
254
258
|
type = effectiveType( type.items );
|
|
259
|
+
}
|
|
255
260
|
// cannot navigate along targetAspect!
|
|
256
|
-
|
|
261
|
+
const env = (type.target) ? csn.definitions[type.target] : type;
|
|
262
|
+
const origin = cached( env, '_origin', getOriginRaw );
|
|
263
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
264
|
+
cached( env, '$origin', _a => setImplicitOrigin( env, origin ) );
|
|
265
|
+
return env;
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
/**
|
|
@@ -278,38 +287,102 @@ function csnRefs( csn ) {
|
|
|
278
287
|
function artifactPathRef( ref ) {
|
|
279
288
|
const [ head, ...tail ] = ref.ref;
|
|
280
289
|
let art = csn.definitions[pathId( head )];
|
|
281
|
-
for (const elem of tail)
|
|
282
|
-
|
|
290
|
+
for (const elem of tail) {
|
|
291
|
+
const env = navigationEnv( art );
|
|
292
|
+
art = env.elements[pathId( elem )];
|
|
293
|
+
}
|
|
283
294
|
return art;
|
|
284
295
|
}
|
|
285
296
|
|
|
286
|
-
function getOrigin(
|
|
287
|
-
const
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
297
|
+
function getOrigin( art, alsoType ) {
|
|
298
|
+
const origin = cached( art, '_origin', getOriginRaw );
|
|
299
|
+
if (origin && origin !== BUILTIN_TYPE)
|
|
300
|
+
cached( art, '$origin', _a => setImplicitOrigin( art, origin ) );
|
|
301
|
+
return art.type && !alsoType ? undefined : origin;
|
|
291
302
|
}
|
|
292
303
|
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
304
|
+
function getOriginRaw( art ) {
|
|
305
|
+
if (art.type) // TODO: make robust against "linked" = only direct
|
|
306
|
+
return artifactRef( art.type, BUILTIN_TYPE );
|
|
307
|
+
if (!art.$origin) // implicit $origin should have been set
|
|
308
|
+
return null;
|
|
309
|
+
// art.$origin must not be a string here - shortened refs should already
|
|
310
|
+
// have been used to set the _origin cache
|
|
311
|
+
if (!Array.isArray( art.$origin )) // anonymous prototype in $origin
|
|
312
|
+
return cached( art.$origin, '_origin', getOriginRaw );
|
|
313
|
+
const [ head, ...tail ] = art.$origin;
|
|
314
|
+
let origin = csn.definitions[head];
|
|
315
|
+
// allow shorter $origin ref for actions/functions, just using a string:
|
|
316
|
+
let isAction = art.kind === 'action' || art.kind === 'function';
|
|
317
|
+
for (const elem of tail) {
|
|
318
|
+
origin = originNavigation( origin, elem, isAction );
|
|
319
|
+
isAction = false;
|
|
320
|
+
}
|
|
321
|
+
return origin;
|
|
299
322
|
}
|
|
300
323
|
|
|
301
|
-
function originNavigation( art, elem ) {
|
|
324
|
+
function originNavigation( art, elem, isAction ) {
|
|
302
325
|
if (typeof elem !== 'string') {
|
|
303
326
|
if (elem.action)
|
|
304
|
-
return art.actions[elem.action]
|
|
327
|
+
return art.actions[elem.action];
|
|
305
328
|
if (elem.param)
|
|
306
|
-
return
|
|
329
|
+
return art.params[elem.param];
|
|
330
|
+
if (elem.returns)
|
|
331
|
+
return art.returns;
|
|
307
332
|
}
|
|
308
|
-
if (
|
|
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) {
|
|
309
355
|
art = art.returns;
|
|
310
|
-
|
|
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) {
|
|
311
363
|
art = art.items;
|
|
312
|
-
|
|
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;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function setMembersImplicit( members, originMembers ) {
|
|
379
|
+
if (!members)
|
|
380
|
+
return;
|
|
381
|
+
for (const name in members) {
|
|
382
|
+
const elem = members[name];
|
|
383
|
+
if (!elem.type && typeof elem.$origin !== 'object') // undefined or string
|
|
384
|
+
setCache( elem, '_origin', originMembers[elem.$origin || name] || false );
|
|
385
|
+
}
|
|
313
386
|
}
|
|
314
387
|
|
|
315
388
|
/**
|
|
@@ -370,6 +443,9 @@ function csnRefs( csn ) {
|
|
|
370
443
|
if (semantics.dynamic === 'global' || ref.global)
|
|
371
444
|
return resolvePath( path, csn.definitions[head], 'global', refCtx === 'from' );
|
|
372
445
|
|
|
446
|
+
const origin = cached( main, '_origin', getOriginRaw )
|
|
447
|
+
if (origin)
|
|
448
|
+
cached( main, '$origin', _a => setImplicitOrigin( main, origin ) );
|
|
373
449
|
cached( main, '$queries', allQueries );
|
|
374
450
|
let qcache = query && cache.get( query.projection || query );
|
|
375
451
|
// BACKEND ISSUE: you cannot call csnRefs(), inspect some refs, change the
|
|
@@ -386,7 +462,7 @@ function csnRefs( csn ) {
|
|
|
386
462
|
while (cache) {
|
|
387
463
|
const alias = tryAlias && cache.$aliases[head];
|
|
388
464
|
if (alias)
|
|
389
|
-
return resolvePath( path, alias._select || alias, 'alias', cache.$queryNumber );
|
|
465
|
+
return resolvePath( path, alias._select || alias._ref, 'alias', cache.$queryNumber );
|
|
390
466
|
const mixin = cache._select.mixin && cache._select.mixin[head];
|
|
391
467
|
if (mixin && {}.hasOwnProperty.call( cache._select.mixin, head ))
|
|
392
468
|
return resolvePath( path, mixin, 'mixin', cache.$queryNumber );
|
|
@@ -480,7 +556,8 @@ function csnRefs( csn ) {
|
|
|
480
556
|
if (query.ref) { // ref in from
|
|
481
557
|
// console.log('SQ:',query,cache.get(query))
|
|
482
558
|
const as = query.as || implicitAs( query.ref );
|
|
483
|
-
|
|
559
|
+
const _ref = fromRef( query );
|
|
560
|
+
getCache( fromSelect, '$aliases' )[as] = { _ref, elements: _ref.elements };
|
|
484
561
|
}
|
|
485
562
|
else {
|
|
486
563
|
const qcache = getQueryCache( parentQuery );
|
|
@@ -606,7 +683,7 @@ function queryOrMain( query, main ) {
|
|
|
606
683
|
*
|
|
607
684
|
* @param {CSN.Query} query
|
|
608
685
|
* @param {CSN.QuerySelect} fromSelect
|
|
609
|
-
* @param {CSN.Query} parentQuery
|
|
686
|
+
* @param {CSN.Query} parentQuery
|
|
610
687
|
* @param {(query: CSN.Query&CSN.QueryFrom, select: CSN.QuerySelectEnriched) => void} callback
|
|
611
688
|
*/
|
|
612
689
|
function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
@@ -631,8 +708,9 @@ function traverseQuery( query, fromSelect, parentQuery, callback ) {
|
|
|
631
708
|
|
|
632
709
|
/**
|
|
633
710
|
* @param {CSN.QueryFrom} from
|
|
634
|
-
* @param {CSN.QuerySelect}
|
|
635
|
-
* @param {
|
|
711
|
+
* @param {CSN.QuerySelect} fromSelect
|
|
712
|
+
* @param {CSN.Query} parentQuery
|
|
713
|
+
* @param {(from: CSN.QueryFrom, select: CSN.QuerySelect, parentQuery: CSN.Query) => void} callback
|
|
636
714
|
*/
|
|
637
715
|
function traverseFrom( from, fromSelect, parentQuery, callback ) {
|
|
638
716
|
if (from.ref) {
|
|
@@ -688,6 +766,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
688
766
|
let isName = false;
|
|
689
767
|
let baseRef = null;
|
|
690
768
|
let baseEnv = null;
|
|
769
|
+
let main = csn.definitions[csnPath[1]];
|
|
691
770
|
|
|
692
771
|
for (let index = 0; index < csnPath.length; index++) {
|
|
693
772
|
const prop = csnPath[index];
|
|
@@ -700,6 +779,10 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
700
779
|
isName = false;
|
|
701
780
|
}
|
|
702
781
|
else if (artifactProperties.includes( String(prop) )) {
|
|
782
|
+
if (refCtx === 'target' || refCtx === 'targetAspect') { // with 'elements'
|
|
783
|
+
main = art = obj; // $self refers to the anonymous aspect
|
|
784
|
+
parent = null;
|
|
785
|
+
}
|
|
703
786
|
isName = prop;
|
|
704
787
|
refCtx = prop;
|
|
705
788
|
}
|
|
@@ -758,7 +841,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
|
|
|
758
841
|
// console.log( 'CPATH:', csnPath, refCtx, obj, parent.$location );
|
|
759
842
|
if (!resolve)
|
|
760
843
|
return { query }; // for constructSemanticLocationFromCsnPath
|
|
761
|
-
return resolve( obj, refCtx,
|
|
844
|
+
return resolve( obj, refCtx, main, query, parent, baseEnv );
|
|
762
845
|
}
|
|
763
846
|
|
|
764
847
|
module.exports = {
|