@sap/cds-compiler 4.0.2 → 4.2.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 +200 -5
- package/bin/cdsc.js +18 -15
- package/doc/CHANGELOG_BETA.md +16 -0
- package/doc/CHANGELOG_DEPRECATED.md +15 -0
- package/lib/api/main.js +33 -13
- package/lib/api/options.js +2 -2
- package/lib/api/validate.js +25 -25
- package/lib/base/location.js +6 -7
- package/lib/base/message-registry.js +123 -42
- package/lib/base/messages.js +18 -10
- package/lib/base/model.js +43 -10
- package/lib/checks/defaultValues.js +6 -6
- package/lib/checks/elements.js +11 -10
- package/lib/checks/foreignKeys.js +0 -5
- package/lib/checks/manyNavigations.js +33 -0
- package/lib/checks/onConditions.js +22 -14
- package/lib/checks/queryNoDbArtifacts.js +132 -73
- package/lib/checks/selectItems.js +4 -55
- package/lib/checks/sql-snippets.js +15 -4
- package/lib/checks/types.js +3 -3
- package/lib/checks/utils.js +4 -3
- package/lib/checks/validator.js +3 -1
- package/lib/compiler/.eslintrc.json +2 -1
- package/lib/compiler/assert-consistency.js +71 -40
- package/lib/compiler/base.js +7 -2
- package/lib/compiler/builtins.js +40 -41
- package/lib/compiler/checks.js +415 -367
- package/lib/compiler/classes.js +62 -0
- package/lib/compiler/cycle-detector.js +9 -9
- package/lib/compiler/define.js +124 -90
- package/lib/compiler/extend.js +115 -88
- package/lib/compiler/finalize-parse-cdl.js +26 -25
- package/lib/compiler/generate.js +57 -49
- package/lib/compiler/index.js +56 -56
- package/lib/compiler/kick-start.js +10 -7
- package/lib/compiler/moduleLayers.js +1 -1
- package/lib/compiler/populate.js +180 -144
- package/lib/compiler/propagator.js +10 -9
- package/lib/compiler/resolve.js +321 -246
- package/lib/compiler/shared.js +812 -433
- package/lib/compiler/tweak-assocs.js +114 -50
- package/lib/compiler/utils.js +241 -46
- package/lib/edm/.eslintrc.json +40 -1
- package/lib/edm/annotations/genericTranslation.js +721 -707
- package/lib/edm/annotations/preprocessAnnotations.js +88 -77
- package/lib/edm/csn2edm.js +389 -378
- package/lib/edm/edm.js +679 -770
- package/lib/edm/edmAnnoPreprocessor.js +132 -146
- package/lib/edm/edmInboundChecks.js +29 -27
- package/lib/edm/edmPreprocessor.js +689 -648
- package/lib/edm/edmUtils.js +279 -300
- package/lib/gen/Dictionary.json +34 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2857 -2856
- package/lib/json/from-csn.js +77 -51
- package/lib/json/to-csn.js +15 -15
- package/lib/language/antlrParser.js +2 -1
- package/lib/language/genericAntlrParser.js +52 -43
- package/lib/language/language.g4 +61 -64
- package/lib/language/multiLineStringParser.js +2 -0
- package/lib/main.d.ts +65 -0
- package/lib/model/csnRefs.js +37 -19
- package/lib/model/csnUtils.js +51 -18
- package/lib/model/revealInternalProperties.js +30 -22
- package/lib/modelCompare/compare.js +149 -41
- package/lib/modelCompare/utils/filter.js +55 -25
- package/lib/optionProcessor.js +21 -9
- package/lib/render/manageConstraints.js +20 -17
- package/lib/render/toCdl.js +63 -23
- package/lib/render/toHdbcds.js +2 -2
- package/lib/render/toRename.js +4 -9
- package/lib/render/toSql.js +82 -35
- package/lib/render/utils/common.js +11 -9
- package/lib/render/utils/unique.js +52 -0
- package/lib/transform/db/applyTransformations.js +62 -21
- package/lib/transform/db/assertUnique.js +7 -8
- package/lib/transform/db/associations.js +2 -2
- package/lib/transform/db/cdsPersistence.js +9 -9
- package/lib/transform/db/constraints.js +47 -17
- package/lib/transform/db/expansion.js +138 -68
- package/lib/transform/db/flattening.js +98 -30
- package/lib/transform/db/rewriteCalculatedElements.js +20 -14
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/transformExists.js +8 -7
- package/lib/transform/db/views.js +73 -33
- package/lib/transform/draft/db.js +11 -9
- package/lib/transform/draft/odata.js +1 -1
- package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
- package/lib/transform/forRelationalDB.js +148 -136
- package/lib/transform/localized.js +92 -54
- package/lib/transform/odata/toFinalBaseType.js +3 -3
- package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
- package/lib/transform/translateAssocsToJoins.js +14 -28
- package/lib/utils/file.js +7 -7
- package/lib/utils/moduleResolve.js +210 -121
- package/lib/utils/objectUtils.js +1 -1
- package/package.json +5 -5
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
- package/share/messages/message-explanations.json +1 -1
package/lib/json/from-csn.js
CHANGED
|
@@ -72,8 +72,9 @@
|
|
|
72
72
|
* What "kind" values are possible in a definition. The root "definitions" properties allows
|
|
73
73
|
* more kinds than e.g. definitions inside "elements".
|
|
74
74
|
*
|
|
75
|
-
* @property {string|string[]} [onlyWith]
|
|
76
|
-
* Defines that the property *must* be used with these properties.
|
|
75
|
+
* @property {string|string[]|Object} [onlyWith]
|
|
76
|
+
* Defines that the property *must* be used with one of these properties.
|
|
77
|
+
* If an object, it maps the kind value to a string or array of strings.
|
|
77
78
|
*
|
|
78
79
|
* @property {number} [minLength]
|
|
79
80
|
* Minimum number of elements that an array must have.
|
|
@@ -84,6 +85,7 @@
|
|
|
84
85
|
* @property {string[]} [xorGroups]
|
|
85
86
|
* Corresponding xor groups. It references a value of xorGroups. If set then only one property
|
|
86
87
|
* of the xorGroup may be set, e.g. if target is set, elements may not.
|
|
88
|
+
* If you are looking for a `notWith` (which should be symmetric), this is your property.
|
|
87
89
|
*
|
|
88
90
|
* @property {string} [xsnOp]
|
|
89
91
|
* Defines the operator to be used for XSN. Used for SET and SELECT. See queryTerm().
|
|
@@ -117,6 +119,7 @@
|
|
|
117
119
|
const { dictAdd } = require('../base/dictionaries');
|
|
118
120
|
const { quotedLiteralPatterns } = require('../compiler/builtins');
|
|
119
121
|
const { CompilerAssertion } = require('../base/error');
|
|
122
|
+
const { XsnSource, CsnLocation } = require('../compiler/classes');
|
|
120
123
|
|
|
121
124
|
const $location = Symbol.for('cds.$location');
|
|
122
125
|
|
|
@@ -144,20 +147,22 @@ const exprProperties = [
|
|
|
144
147
|
// Groups of properties which cannot be used together:
|
|
145
148
|
const xorGroups = {
|
|
146
149
|
// include CSN v0.1.0 properties here:
|
|
147
|
-
':type': [
|
|
150
|
+
':type': [
|
|
151
|
+
'target', 'targetAspect', 'elements', 'items', // xorException: target+targetAspect
|
|
152
|
+
'length', 'precision', 'scale', 'srid', // xorException: precision+scale
|
|
153
|
+
],
|
|
154
|
+
':enum': [ 'target', 'targetAspect', 'elements', 'enum', 'items' ],
|
|
148
155
|
':expr': [ // see also xorException property in schema
|
|
149
156
|
'ref', 'xpr', 'list', 'val', '#', 'func', 'SELECT', 'SET', 'expand',
|
|
150
157
|
'=', 'path', 'value', 'op', // '='/'path' is CSN v0.1.0 here
|
|
151
158
|
],
|
|
152
159
|
':col': [ 'expand', 'inline' ],
|
|
153
160
|
':ext': [ 'annotate', 'extend' ], // TODO: better msg for test/negative/UnexpectedProperties.csn
|
|
154
|
-
':assoc': [
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
':
|
|
159
|
-
':precision': [ 'elements', 'items', 'precision' ],
|
|
160
|
-
':scale': [ 'elements', 'items', 'scale' ],
|
|
161
|
+
':assoc': [
|
|
162
|
+
'on', 'keys',
|
|
163
|
+
'foreignKeys', 'onCond', // 'foreignKeys'/'onCond' is CSN v0.1.0
|
|
164
|
+
],
|
|
165
|
+
':on': [ 'on', 'default' ],
|
|
161
166
|
|
|
162
167
|
// TODO - improve consequential errors: assume no name given with `join` or `inline`?
|
|
163
168
|
as: [ 'as', 'join', 'inline' ],
|
|
@@ -251,12 +256,14 @@ const schema = compileSchema( {
|
|
|
251
256
|
requires: [ 'extend', 'annotate' ],
|
|
252
257
|
},
|
|
253
258
|
enum: {
|
|
259
|
+
type: enumDict,
|
|
254
260
|
dictionaryOf: definition,
|
|
255
261
|
defaultKind: 'enum',
|
|
256
262
|
validKinds: [ 'enum' ],
|
|
257
263
|
inKind: [ 'element', 'type', 'param', 'annotation', 'annotate', 'extend' ],
|
|
258
264
|
},
|
|
259
265
|
elements: {
|
|
266
|
+
type: elementsDict,
|
|
260
267
|
dictionaryOf: definition,
|
|
261
268
|
defaultKind: 'element',
|
|
262
269
|
validKinds: [ 'element' ],
|
|
@@ -272,17 +279,11 @@ const schema = compileSchema( {
|
|
|
272
279
|
'extend',
|
|
273
280
|
],
|
|
274
281
|
},
|
|
275
|
-
payload: { // keep it for a while, TODO: remove with v2 - at least warning
|
|
276
|
-
dictionaryOf: definition, // duplicate of line below only for better error message
|
|
277
|
-
type: renameTo( 'elements', dictionaryOf( definition ) ),
|
|
278
|
-
defaultKind: 'element',
|
|
279
|
-
validKinds: [],
|
|
280
|
-
inKind: [ 'event' ],
|
|
281
|
-
},
|
|
282
282
|
actions: {
|
|
283
283
|
dictionaryOf: actions,
|
|
284
284
|
defaultKind: 'action',
|
|
285
285
|
validKinds: [ 'action', 'function' ],
|
|
286
|
+
onlyWith: { aspect: 'elements' },
|
|
286
287
|
inKind: [ 'entity', 'aspect', 'annotate', 'extend' ],
|
|
287
288
|
},
|
|
288
289
|
params: {
|
|
@@ -364,6 +365,7 @@ const schema = compileSchema( {
|
|
|
364
365
|
},
|
|
365
366
|
targetAspect: {
|
|
366
367
|
type: artifactRef,
|
|
368
|
+
xorException: 'target', // see xorGroup :type
|
|
367
369
|
msgVariant: 'or-object', // for 'syntax-expecting-string',
|
|
368
370
|
requires: 'elements',
|
|
369
371
|
optional: [ 'elements' ], // 'elements' for ad-hoc aspect compositions
|
|
@@ -371,6 +373,7 @@ const schema = compileSchema( {
|
|
|
371
373
|
},
|
|
372
374
|
target: {
|
|
373
375
|
type: artifactRef,
|
|
376
|
+
xorException: 'targetAspect', // see xorGroup :type
|
|
374
377
|
msgVariant: 'or-object', // for 'syntax-expecting-string',
|
|
375
378
|
requires: 'elements',
|
|
376
379
|
optional: [ 'elements' ], // 'elements' for ad-hoc COMPOSITION OF (gensrc style CSN)
|
|
@@ -398,10 +401,12 @@ const schema = compileSchema( {
|
|
|
398
401
|
},
|
|
399
402
|
precision: {
|
|
400
403
|
type: natnum,
|
|
404
|
+
xorException: 'scale', // see xorGroup :type
|
|
401
405
|
inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
|
|
402
406
|
},
|
|
403
407
|
scale: {
|
|
404
408
|
type: scalenum,
|
|
409
|
+
xorException: 'precision', // see xorGroup :type
|
|
405
410
|
inKind: [ 'element', 'type', 'param', 'annotation', 'extend' ],
|
|
406
411
|
},
|
|
407
412
|
srid: {
|
|
@@ -655,7 +660,7 @@ const schema = compileSchema( {
|
|
|
655
660
|
'-expr': { // '-expr' and '-' must not exist top-level
|
|
656
661
|
prop: '@‹anno›',
|
|
657
662
|
type: object,
|
|
658
|
-
optional: [ '=', '#', 'xpr', 'ref', 'val', 'list', 'literal', 'func', 'args' ],
|
|
663
|
+
optional: [ '=', '#', 'xpr', 'ref', 'val', 'list', 'literal', 'func', 'args', 'param' ],
|
|
659
664
|
schema: {
|
|
660
665
|
'=': {
|
|
661
666
|
type: renameTo( '$tokenTexts', string ),
|
|
@@ -990,15 +995,12 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
990
995
|
const kind0 = (spec.validKinds.length || spec.prop === 'extensions') && kind;
|
|
991
996
|
const csnProps = Object.keys( def );
|
|
992
997
|
|
|
993
|
-
//
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
def.type === undefined && def.elements === undefined) {
|
|
1000
|
-
r.$syntax = 'enum';
|
|
1001
|
-
kind = 'enum'; // for function expected()
|
|
998
|
+
// For compatibility, extension property `elements` could actually be an `enum`:
|
|
999
|
+
if (savedInExtensions === '' && prop === 'elements' && // in extend property `elements`
|
|
1000
|
+
!Object.keys( def ).some( couldNotBeEnumProperty )) {
|
|
1001
|
+
r.$syntax = 'enum'; // could be an enum
|
|
1002
|
+
if (def.val !== undefined || def['#'] !== undefined)
|
|
1003
|
+
kind = 'enum'; // for function expected(), i.e. allow property `val`/`#`
|
|
1002
1004
|
}
|
|
1003
1005
|
|
|
1004
1006
|
if (csnProps.length) {
|
|
@@ -1018,15 +1020,6 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
1018
1020
|
r.name = { id: name, location: r.location };
|
|
1019
1021
|
if (prop === 'columns' || prop === 'keys' || prop === 'foreignKeys')
|
|
1020
1022
|
r.name.$inferred = 'as';
|
|
1021
|
-
// TODO the following 'if' (if necessary) should be part of the core compiler
|
|
1022
|
-
if (prop === 'definitions' || prop === 'vocabularies') { // as spec property
|
|
1023
|
-
r.name = {
|
|
1024
|
-
absolute: name,
|
|
1025
|
-
id: name.substring( name.lastIndexOf('.') + 1 ),
|
|
1026
|
-
path: [ { id: name, location: r.location } ],
|
|
1027
|
-
location: r.location,
|
|
1028
|
-
};
|
|
1029
|
-
}
|
|
1030
1023
|
}
|
|
1031
1024
|
if (spec.requires)
|
|
1032
1025
|
onlyWith( spec, spec.requires, def, null, xor, () => true );
|
|
@@ -1049,11 +1042,18 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
1049
1042
|
// for an 'annotate', both 'annotate' and the "host" kind must be expected
|
|
1050
1043
|
(!inExtensions || s.inKind.includes( inExtensions ) ||
|
|
1051
1044
|
// extending elements in returns can be without 'returns' in CSN
|
|
1052
|
-
// TODO:
|
|
1045
|
+
// see function elementsDict() for detail, TODO: remove finally
|
|
1053
1046
|
inExtensions === 'action' && p === 'elements');
|
|
1054
1047
|
}
|
|
1055
1048
|
}
|
|
1056
1049
|
|
|
1050
|
+
function couldNotBeEnumProperty( prop ) {
|
|
1051
|
+
// returns true for `value` (which we allow with warning when extending an enum with `elements`)
|
|
1052
|
+
const inKind = schema[prop]?.inKind; // undefined for annotations, $location, …
|
|
1053
|
+
// inKind for annotation assignments is function -> can be for enum
|
|
1054
|
+
return Array.isArray( inKind ) && inKind.includes( 'element' );
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
1057
|
function actions( def, spec, xsn, csn, name ) {
|
|
1058
1058
|
if (def.kind === 'extend' && (def.elements || def.enum)) {
|
|
1059
1059
|
// TODO: Handle this case in extend.js; already done for `returns`
|
|
@@ -1125,6 +1125,27 @@ function returnsDefinition( def, spec, xsn, csn ) {
|
|
|
1125
1125
|
return definition( def, spec, xsn, csn, '' );
|
|
1126
1126
|
}
|
|
1127
1127
|
|
|
1128
|
+
// Temporary function as long as the message below is not a hard error
|
|
1129
|
+
function elementsDict( def, spec, xsn ) {
|
|
1130
|
+
const elements = dictionaryOf( definition )( def, spec );
|
|
1131
|
+
if (inExtensions !== 'action')
|
|
1132
|
+
return elements;
|
|
1133
|
+
warning( 'syntax-expecting-returns', elements[$location],
|
|
1134
|
+
{ prop: 'elements', parentprop: 'returns' },
|
|
1135
|
+
// eslint-disable-next-line max-len
|
|
1136
|
+
'Expecting property $(PROP) to be put into an object for property $(PARENTPROP) when annotating action return structures' );
|
|
1137
|
+
xsn.returns = { kind: 'annotate', elements, location: elements[$location] };
|
|
1138
|
+
return undefined;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function enumDict( def, spec, xsn ) {
|
|
1142
|
+
const dict = dictionaryOf( definition )( def, spec );
|
|
1143
|
+
if (!inExtensions)
|
|
1144
|
+
return dict;
|
|
1145
|
+
xsn.elements = dict; // normalize to `elements` for `annotate`
|
|
1146
|
+
return undefined;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1128
1149
|
// For v1 CSNs with annotation definitions
|
|
1129
1150
|
function attachVocabInDefinitions( csn ) {
|
|
1130
1151
|
if (!csn.vocabularies) {
|
|
@@ -1472,7 +1493,7 @@ function annoValue( val, spec ) {
|
|
|
1472
1493
|
function annotation( val, spec, xsn, csn, name ) {
|
|
1473
1494
|
const absolute = (xsn ? name.substring(1) : name);
|
|
1474
1495
|
// TODO: really care about variant (qualifier parts)?
|
|
1475
|
-
const variantIndex = absolute.indexOf('#') + 1 || absolute.length;
|
|
1496
|
+
const variantIndex = absolute.indexOf('#') + 1 || absolute.length; // including '#'
|
|
1476
1497
|
const n = refSplit( absolute.substring( 0, variantIndex ), !xsn && '{}' );
|
|
1477
1498
|
if (!n)
|
|
1478
1499
|
return undefined;
|
|
@@ -1831,6 +1852,11 @@ function calculateKind( def, spec ) {
|
|
|
1831
1852
|
function onlyWith( spec, need, csn, prop, xor, expected ) {
|
|
1832
1853
|
if (!need)
|
|
1833
1854
|
return spec;
|
|
1855
|
+
if (typeof need === 'object' && !Array.isArray( need )) {
|
|
1856
|
+
need = need[csn.kind];
|
|
1857
|
+
if (!need)
|
|
1858
|
+
return spec;
|
|
1859
|
+
}
|
|
1834
1860
|
if (typeof need === 'string') {
|
|
1835
1861
|
if (need in csn) // TODO: enumerable ?
|
|
1836
1862
|
return spec;
|
|
@@ -1941,7 +1967,8 @@ function replaceZeroValue( spec, msgVariant, newValue ) {
|
|
|
1941
1967
|
*/
|
|
1942
1968
|
function location( enforceJsonPos ) {
|
|
1943
1969
|
return !enforceJsonPos && dollarLocations.length &&
|
|
1944
|
-
|
|
1970
|
+
dollarLocations[dollarLocations.length - 1] || {
|
|
1971
|
+
__proto__: CsnLocation.prototype,
|
|
1945
1972
|
file: csnFilename,
|
|
1946
1973
|
line: virtualLine,
|
|
1947
1974
|
col: 0,
|
|
@@ -1954,7 +1981,7 @@ function pushLocation( obj ) {
|
|
|
1954
1981
|
if (loc === undefined)
|
|
1955
1982
|
return;
|
|
1956
1983
|
if (loc && typeof loc === 'object' && !Array.isArray( loc )) {
|
|
1957
|
-
dollarLocations.push( loc.line ? loc : null );
|
|
1984
|
+
dollarLocations.push( loc.line ? { __proto__: CsnLocation.prototype, ...loc } : null );
|
|
1958
1985
|
return;
|
|
1959
1986
|
}
|
|
1960
1987
|
else if (!loc || typeof loc !== 'string') {
|
|
@@ -1970,10 +1997,9 @@ function pushLocation( obj ) {
|
|
|
1970
1997
|
else {
|
|
1971
1998
|
const line = Number( m[1] );
|
|
1972
1999
|
const column = m[2] && Number( m[2] ) || 0;
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
line,
|
|
1976
|
-
col: column,
|
|
2000
|
+
const file = loc.substring( 0, m.index );
|
|
2001
|
+
dollarLocations.push({
|
|
2002
|
+
__proto__: CsnLocation.prototype, file, line, col: column,
|
|
1977
2003
|
} );
|
|
1978
2004
|
}
|
|
1979
2005
|
}
|
|
@@ -2011,7 +2037,8 @@ function toXsn( csn, filename, options, messageFunctions ) {
|
|
|
2011
2037
|
inExtensions = null;
|
|
2012
2038
|
vocabInDefinitions = null;
|
|
2013
2039
|
|
|
2014
|
-
const xsn =
|
|
2040
|
+
const xsn = new XsnSource();
|
|
2041
|
+
xsn.$frontend = 'json';
|
|
2015
2042
|
|
|
2016
2043
|
// eslint-disable-next-line object-curly-newline
|
|
2017
2044
|
({ message, error, warning, info } = messageFunctions);
|
|
@@ -2050,7 +2077,7 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
|
|
|
2050
2077
|
resetHeapModuleVars();
|
|
2051
2078
|
if (!(e instanceof SyntaxError))
|
|
2052
2079
|
throw e;
|
|
2053
|
-
const xsn =
|
|
2080
|
+
const xsn = new XsnSource();
|
|
2054
2081
|
const msg = e.message;
|
|
2055
2082
|
const p = /in JSON at position ([0-9]+)/.exec( msg );
|
|
2056
2083
|
let line = 1;
|
|
@@ -2067,12 +2094,11 @@ function parse( source, filename = 'csn.json', options = {}, messageFunctions =
|
|
|
2067
2094
|
}
|
|
2068
2095
|
column = end - eol + 1;
|
|
2069
2096
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
file: filename,
|
|
2097
|
+
const loc = new CsnLocation(
|
|
2098
|
+
filename,
|
|
2073
2099
|
line,
|
|
2074
|
-
|
|
2075
|
-
|
|
2100
|
+
column
|
|
2101
|
+
);
|
|
2076
2102
|
messageFunctions.error( 'syntax-invalid-json', loc, { msg },
|
|
2077
2103
|
'Invalid JSON: $(MSG)' );
|
|
2078
2104
|
return xsn;
|
package/lib/json/to-csn.js
CHANGED
|
@@ -115,7 +115,6 @@ const transformers = {
|
|
|
115
115
|
offset: expression,
|
|
116
116
|
on: onCondition,
|
|
117
117
|
// definitions, extensions, members ----------------------------------------
|
|
118
|
-
returns, // storing the return type of actions
|
|
119
118
|
notNull: value,
|
|
120
119
|
default: expression,
|
|
121
120
|
// targetElement: ignore, // special display of foreign key, renameTo: select
|
|
@@ -123,6 +122,7 @@ const transformers = {
|
|
|
123
122
|
query,
|
|
124
123
|
elements,
|
|
125
124
|
actions, // TODO: just normal dictionary
|
|
125
|
+
returns, // storing the return type of actions
|
|
126
126
|
// special: top-level, cardinality -----------------------------------------
|
|
127
127
|
sources,
|
|
128
128
|
definitions: sortedDict,
|
|
@@ -335,7 +335,8 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
335
335
|
break;
|
|
336
336
|
}
|
|
337
337
|
set( 'definitions', csn, model );
|
|
338
|
-
|
|
338
|
+
if (Object.keys(model.vocabularies || {}).length > 0)
|
|
339
|
+
set( 'vocabularies', csn, model );
|
|
339
340
|
const exts = extensions( model.extensions || [], csn, model );
|
|
340
341
|
if (exts && exts.length)
|
|
341
342
|
csn.extensions = exts;
|
|
@@ -464,13 +465,13 @@ function attachAnnotations( annotate, prop, dict, inferred, insideReturns = fals
|
|
|
464
465
|
attachAnnotations( sub, 'actions', entry.actions, inf );
|
|
465
466
|
else if (entry.params)
|
|
466
467
|
attachAnnotations( sub, 'params', entry.params, inf );
|
|
467
|
-
const obj = entry.returns || entry;
|
|
468
|
+
const obj = entry.returns || entry;
|
|
468
469
|
const many = obj.items || obj;
|
|
469
470
|
const elems = (many.targetAspect || many).elements;
|
|
470
471
|
if (elems)
|
|
471
472
|
attachAnnotations( sub, 'elements', elems, inf, entry.returns );
|
|
472
|
-
if (many.enum)
|
|
473
|
-
attachAnnotations( sub, '
|
|
473
|
+
else if (many.enum) // make 'enum' annotations appear in 'elements' annotate
|
|
474
|
+
attachAnnotations( sub, 'elements', many.enum, inf, entry.returns );
|
|
474
475
|
}
|
|
475
476
|
if (Object.keys( sub ).length)
|
|
476
477
|
annoDict[name] = sub;
|
|
@@ -493,10 +494,12 @@ function standard( node ) {
|
|
|
493
494
|
// XSN input node, not the CSN result node. Not really an issue...
|
|
494
495
|
const keys = Object.keys( node ).sort( compareProperties );
|
|
495
496
|
for (const prop of keys) {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
497
|
+
if (node[prop] !== undefined) {
|
|
498
|
+
const transformer = transformers[prop] || transformers[prop.charAt(0)] || unexpected;
|
|
499
|
+
const sub = transformer( node[prop], csn, node, prop );
|
|
500
|
+
if (sub !== undefined)
|
|
501
|
+
csn[prop] = sub;
|
|
502
|
+
}
|
|
500
503
|
}
|
|
501
504
|
return csn;
|
|
502
505
|
}
|
|
@@ -715,14 +718,12 @@ function actions( dict, _csn, node ) {
|
|
|
715
718
|
const keys = Object.keys( dict );
|
|
716
719
|
if (strictMode && node.kind === 'annotate')
|
|
717
720
|
keys.sort(); // TODO: always sort with --test-mode ?
|
|
718
|
-
return (keys
|
|
719
|
-
? dictionary( dict, keys, 'actions' )
|
|
720
|
-
: undefined;
|
|
721
|
+
return dictionary( dict, keys, 'actions' );
|
|
721
722
|
}
|
|
722
723
|
|
|
723
724
|
function params( dict ) {
|
|
724
725
|
const keys = Object.keys( dict );
|
|
725
|
-
return (keys.length)
|
|
726
|
+
return (keys.length) // TODO: still?
|
|
726
727
|
? insertOrderDict( dict )
|
|
727
728
|
: undefined;
|
|
728
729
|
}
|
|
@@ -1212,8 +1213,7 @@ function value( node ) {
|
|
|
1212
1213
|
function enumValueOrCalc( v, csn, node ) {
|
|
1213
1214
|
if (v.$inferred && (universalCsn || gensrcFlavor))
|
|
1214
1215
|
return undefined;
|
|
1215
|
-
// Enums
|
|
1216
|
-
// In v4, we don't check `node.$syntax === 'enum'` anymore.
|
|
1216
|
+
// Enums values in CSN are without outer `value: { … }`:
|
|
1217
1217
|
if (node.kind === 'enum') {
|
|
1218
1218
|
Object.assign( csn, expression( v ) );
|
|
1219
1219
|
}
|
|
@@ -12,6 +12,7 @@ const antlr4 = require('antlr4');
|
|
|
12
12
|
|
|
13
13
|
const { CompileMessage } = require('../base/messages');
|
|
14
14
|
const errorStrategy = require('./errorStrategy');
|
|
15
|
+
const { XsnSource } = require('../compiler/classes');
|
|
15
16
|
|
|
16
17
|
const Parser = require('../gen/languageParser').default;
|
|
17
18
|
const Lexer = require('../gen/languageLexer').default;
|
|
@@ -167,7 +168,7 @@ function parse( source, filename = '<undefined>.cds',
|
|
|
167
168
|
throw e;
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
|
-
const ast = tree && tree[rulespec.returns] ||
|
|
171
|
+
const ast = tree && tree[rulespec.returns] || new XsnSource();
|
|
171
172
|
ast.options = options;
|
|
172
173
|
if (rulespec.$frontend)
|
|
173
174
|
ast.$frontend = rulespec.$frontend;
|
|
@@ -18,6 +18,9 @@ const {
|
|
|
18
18
|
quotedLiteralPatterns,
|
|
19
19
|
} = require('../compiler/builtins');
|
|
20
20
|
const { pathName } = require('../compiler/utils');
|
|
21
|
+
const {
|
|
22
|
+
XsnArtifact, XsnName, CsnLocation, XsnSource,
|
|
23
|
+
} = require('../compiler/classes');
|
|
21
24
|
const { isBetaEnabled } = require('../base/model');
|
|
22
25
|
const { weakLocation } = require('../base/messages');
|
|
23
26
|
const { normalizeNewLine } = require('./textUtils');
|
|
@@ -110,7 +113,6 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
110
113
|
addDef,
|
|
111
114
|
addItem,
|
|
112
115
|
addExtension,
|
|
113
|
-
aspectWithoutElements,
|
|
114
116
|
createSource,
|
|
115
117
|
createDict,
|
|
116
118
|
createArray,
|
|
@@ -139,6 +141,8 @@ Object.assign(GenericAntlrParser.prototype, {
|
|
|
139
141
|
prepareGenericKeywords,
|
|
140
142
|
reportErrorForGenericKeyword,
|
|
141
143
|
parseMultiLineStringLiteral,
|
|
144
|
+
XsnArtifact,
|
|
145
|
+
XsnName,
|
|
142
146
|
});
|
|
143
147
|
|
|
144
148
|
// Use the following function for language constructs which we (currently)
|
|
@@ -160,7 +164,13 @@ function csnParseOnly( msgId, tokens, textArgs ) {
|
|
|
160
164
|
this.error( msgId, loc, textArgs );
|
|
161
165
|
}
|
|
162
166
|
|
|
163
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* Do not propose a `;` or closing brace `}` at this position.
|
|
169
|
+
*
|
|
170
|
+
* Attention: May conflict with excludeExpected()!
|
|
171
|
+
*
|
|
172
|
+
* @this {object}
|
|
173
|
+
* */
|
|
164
174
|
function noSemicolonHere() {
|
|
165
175
|
const handler = this._errHandler;
|
|
166
176
|
const t = this.getCurrentToken();
|
|
@@ -173,10 +183,21 @@ function noSemicolonHere() {
|
|
|
173
183
|
handler.reportIgnoredWith( this, t );
|
|
174
184
|
}
|
|
175
185
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Using this function "during ATN decision making" has no effect
|
|
188
|
+
* In front of an ATN decision, you might specify dedicated excludes
|
|
189
|
+
* for non-LA1 tokens via a sub-array in excludes[0].
|
|
190
|
+
* TODO: consider $nextTokens…, see commented use in rule `elementProperties`
|
|
191
|
+
*
|
|
192
|
+
* Usage Note:
|
|
193
|
+
* Must be used at all positions where sync() is called in the generated coding.
|
|
194
|
+
* ```antlr4
|
|
195
|
+
* { this.excludeExpected(['ACTIONS']); }
|
|
196
|
+
* ( WITH { this.excludeExpected(['ACTIONS']); } )?
|
|
197
|
+
* annotationAssignment_ll1[ $art ]* { this.excludeExpected(['ACTIONS']); }
|
|
198
|
+
* ACTIONS
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
180
201
|
function excludeExpected( excludes ) {
|
|
181
202
|
if (excludes) {
|
|
182
203
|
// @ts-ignore
|
|
@@ -414,6 +435,12 @@ function checkExtensionDict( dict ) {
|
|
|
414
435
|
def[prop] = dup[prop]; // continuation semantics: last wins
|
|
415
436
|
}
|
|
416
437
|
}
|
|
438
|
+
if (dup.$annotations) { // update deprecated $annotations for cds-lsp / annotation modeler
|
|
439
|
+
if (def.$annotations)
|
|
440
|
+
def.$annotations.push( ...dup.$annotations );
|
|
441
|
+
else
|
|
442
|
+
def.$annotations = dup.$annotations;
|
|
443
|
+
}
|
|
417
444
|
}
|
|
418
445
|
def.$duplicates = null;
|
|
419
446
|
}
|
|
@@ -445,14 +472,14 @@ function handleDuplicateExtension( ext, name, numDefines ) {
|
|
|
445
472
|
* Return start location of `token`, or the first token matched by the current
|
|
446
473
|
* rule if `token` is undefined
|
|
447
474
|
*
|
|
448
|
-
* @returns {
|
|
475
|
+
* @returns {CsnLocation}
|
|
449
476
|
*/
|
|
450
477
|
function startLocation( token = this._ctx.start ) {
|
|
451
|
-
return
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
478
|
+
return new CsnLocation(
|
|
479
|
+
this.filename,
|
|
480
|
+
token.line,
|
|
481
|
+
token.column + 1
|
|
482
|
+
);
|
|
456
483
|
}
|
|
457
484
|
|
|
458
485
|
/**
|
|
@@ -461,7 +488,7 @@ function startLocation( token = this._ctx.start ) {
|
|
|
461
488
|
*
|
|
462
489
|
* @param {object} token
|
|
463
490
|
* @param {object} endToken
|
|
464
|
-
* @return {
|
|
491
|
+
* @return {CsnLocation}
|
|
465
492
|
*/
|
|
466
493
|
function tokenLocation( token, endToken = null ) {
|
|
467
494
|
if (!token)
|
|
@@ -469,16 +496,11 @@ function tokenLocation( token, endToken = null ) {
|
|
|
469
496
|
if (!endToken) // including null
|
|
470
497
|
endToken = token;
|
|
471
498
|
|
|
472
|
-
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// Default for single line tokens
|
|
478
|
-
endLine: endToken.line,
|
|
479
|
-
// after the last char (special for EOF?)
|
|
480
|
-
endCol: endToken.stop - endToken.start + endToken.column + 2,
|
|
481
|
-
};
|
|
499
|
+
// Default for single line tokens
|
|
500
|
+
const endLine = endToken.line;
|
|
501
|
+
// after the last char (special for EOF?)
|
|
502
|
+
const endCol = endToken.stop - endToken.start + endToken.column + 2;
|
|
503
|
+
const loc = new CsnLocation( this.filename, token.line, token.column + 1, endLine, endCol );
|
|
482
504
|
|
|
483
505
|
// This check is done for performance reason. No need to access a token's
|
|
484
506
|
// data if we know that it spans only one single line.
|
|
@@ -579,7 +601,7 @@ function secureParens( expr ) {
|
|
|
579
601
|
return {
|
|
580
602
|
op: { val: 'xpr', location: this.startLocation() },
|
|
581
603
|
args: [ expr ],
|
|
582
|
-
location: { ...expr.location },
|
|
604
|
+
location: { __proto__: CsnLocation.prototype, ...expr.location },
|
|
583
605
|
$parens,
|
|
584
606
|
};
|
|
585
607
|
}
|
|
@@ -733,7 +755,11 @@ function argsExpression( args, nary, location ) {
|
|
|
733
755
|
val, // there is no n-ary in rule conditionTerm
|
|
734
756
|
location: this.startLocation(),
|
|
735
757
|
};
|
|
736
|
-
return this.attachLocation( {
|
|
758
|
+
return this.attachLocation( {
|
|
759
|
+
op,
|
|
760
|
+
args,
|
|
761
|
+
location: location && { __proto__: CsnLocation.prototype, ...location },
|
|
762
|
+
} );
|
|
737
763
|
}
|
|
738
764
|
|
|
739
765
|
function pushXprToken( args ) {
|
|
@@ -1097,16 +1123,6 @@ function addExtension( ext, parent, kind, artName, elemPath ) {
|
|
|
1097
1123
|
}
|
|
1098
1124
|
}
|
|
1099
1125
|
|
|
1100
|
-
function aspectWithoutElements( art ) {
|
|
1101
|
-
// Empty dictionary to allow element extensions. NO, please NO empty dict.
|
|
1102
|
-
// TODO: Checking it here does not prevent aspect in CSN input having no elements!
|
|
1103
|
-
art.elements = this.createDict();
|
|
1104
|
-
if (!isBetaEnabled( this.options, 'aspectWithoutElements' )) {
|
|
1105
|
-
this.error( null, [ art.name.location, null ], {},
|
|
1106
|
-
'Aspects without elements are not supported, yet' );
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
1126
|
// must be in action directly after having parsed '{', '(`, or a keyword before
|
|
1111
1127
|
function createDict() {
|
|
1112
1128
|
const dict = Object.create(null);
|
|
@@ -1132,14 +1148,7 @@ function finalizeDictOrArray( dict ) {
|
|
|
1132
1148
|
}
|
|
1133
1149
|
|
|
1134
1150
|
function createSource() {
|
|
1135
|
-
return
|
|
1136
|
-
kind: 'source',
|
|
1137
|
-
usings: [],
|
|
1138
|
-
dependencies: [],
|
|
1139
|
-
artifacts: Object.create(null),
|
|
1140
|
-
// vocabularies: Object.create(null),
|
|
1141
|
-
extensions: [],
|
|
1142
|
-
};
|
|
1151
|
+
return new XsnSource();
|
|
1143
1152
|
}
|
|
1144
1153
|
|
|
1145
1154
|
// Create AST node for prefix operator `op` and arguments `args`
|