@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/json/from-csn.js
CHANGED
|
@@ -89,6 +89,8 @@
|
|
|
89
89
|
|
|
90
90
|
const { dictAdd } = require('../base/dictionaries');
|
|
91
91
|
|
|
92
|
+
const $location = Symbol.for('cds.$location');
|
|
93
|
+
|
|
92
94
|
let inExtensions = null;
|
|
93
95
|
|
|
94
96
|
let vocabInDefinitions = null; // must be reset!
|
|
@@ -661,6 +663,9 @@ const schema = compileSchema( {
|
|
|
661
663
|
ignore: true, type: ignore,
|
|
662
664
|
},
|
|
663
665
|
// TODO: should we keep $parens ?
|
|
666
|
+
$generated: {
|
|
667
|
+
type: string,
|
|
668
|
+
},
|
|
664
669
|
$: { type: ignore, ignore: true }, // including $origin
|
|
665
670
|
_: { type: ignore, ignore: true },
|
|
666
671
|
} );
|
|
@@ -705,7 +710,7 @@ let csnFilename = '';
|
|
|
705
710
|
let virtualLine = 1;
|
|
706
711
|
/** @type {CSN.Location[]} */
|
|
707
712
|
let dollarLocations = [];
|
|
708
|
-
let
|
|
713
|
+
let arrayLevelCount = 0;
|
|
709
714
|
|
|
710
715
|
/**
|
|
711
716
|
* @param {Object.<string, SchemaSpec>} specs
|
|
@@ -931,8 +936,10 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
931
936
|
popLocation( def );
|
|
932
937
|
if (kind !== 'annotation' || prop === 'vocabularies')
|
|
933
938
|
return r;
|
|
934
|
-
if (!vocabInDefinitions)
|
|
939
|
+
if (!vocabInDefinitions) {
|
|
935
940
|
vocabInDefinitions = Object.create(null);
|
|
941
|
+
vocabInDefinitions[$location] = location();
|
|
942
|
+
}
|
|
936
943
|
vocabInDefinitions[name] = r; // deprecated: anno def in 'definitions'
|
|
937
944
|
return undefined;
|
|
938
945
|
|
|
@@ -957,6 +964,7 @@ function dictionaryOf( elementFct ) {
|
|
|
957
964
|
return ignore( dict );
|
|
958
965
|
}
|
|
959
966
|
const r = Object.create(null);
|
|
967
|
+
r[$location] = location();
|
|
960
968
|
const allNames = Object.keys( dict );
|
|
961
969
|
if (!allNames.length)
|
|
962
970
|
return r; // {} in one JSON line
|
|
@@ -980,6 +988,7 @@ function keys( array, spec, xsn ) {
|
|
|
980
988
|
if (!isArray( array, spec ))
|
|
981
989
|
return;
|
|
982
990
|
const r = Object.create(null);
|
|
991
|
+
r[$location] = location();
|
|
983
992
|
++virtualLine;
|
|
984
993
|
for (const def of array) {
|
|
985
994
|
const id = def.as || implicitName( def.ref );
|
|
@@ -1164,6 +1173,12 @@ function symbol( id, spec, xsn ) { // for CSN property '#'
|
|
|
1164
1173
|
xsn.sym = { id, location: location() };
|
|
1165
1174
|
}
|
|
1166
1175
|
|
|
1176
|
+
// returns: false = no "...", true = "..." without UP TO, 'upTo' = "..." with UP TO
|
|
1177
|
+
function isEllipsis( val ) {
|
|
1178
|
+
return val && typeof val === 'object' && '...' in val && Object.keys(val).length === 1 &&
|
|
1179
|
+
(val['...'] === true || 'upTo');
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1167
1182
|
function annoValue( val, spec ) {
|
|
1168
1183
|
if (val == null) // TODO: reject undefined
|
|
1169
1184
|
return { val, literal: 'null', location: location() };
|
|
@@ -1171,22 +1186,40 @@ function annoValue( val, spec ) {
|
|
|
1171
1186
|
if (lit !== 'object')
|
|
1172
1187
|
return { val, literal: lit, location: location() };
|
|
1173
1188
|
if (Array.isArray( val )) {
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1189
|
+
/** @type {string|boolean} */
|
|
1190
|
+
let seenEllipsis = false;
|
|
1191
|
+
if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
|
|
1192
|
+
if (val.some( isEllipsis )) {
|
|
1193
|
+
error( 'syntax-csn-unexpected-ellipsis', location(true), { code: '...' },
|
|
1194
|
+
'Unexpected $(CODE) in nested array' );
|
|
1195
|
+
}
|
|
1178
1196
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1197
|
+
else {
|
|
1198
|
+
for (const item of val) {
|
|
1199
|
+
if (seenEllipsis !== true) {
|
|
1200
|
+
seenEllipsis = isEllipsis( item ) || seenEllipsis;
|
|
1201
|
+
}
|
|
1202
|
+
else if (isEllipsis( item )) { // with or without UP TO
|
|
1203
|
+
// error position at the beginning of the array, but that is fine
|
|
1204
|
+
error( 'syntax-csn-duplicate-ellipsis', location(true), { code: '...' },
|
|
1205
|
+
'Expected no more than one $(CODE)' );
|
|
1206
|
+
break;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1182
1209
|
}
|
|
1183
|
-
|
|
1210
|
+
arrayLevelCount++;
|
|
1184
1211
|
const retval = {
|
|
1185
1212
|
location: location(),
|
|
1186
1213
|
val: arrayOf( annoValue )( val, spec ),
|
|
1187
1214
|
literal: 'array',
|
|
1188
1215
|
};
|
|
1189
|
-
|
|
1216
|
+
arrayLevelCount--;
|
|
1217
|
+
if (seenEllipsis === 'upTo') {
|
|
1218
|
+
error( 'syntax-csn-expecting-ellipsis', location(true), // at closing bracket
|
|
1219
|
+
{ code: '... up to', newcode: '...' },
|
|
1220
|
+
// TODO: should we be more CSN specific in the message?
|
|
1221
|
+
'Expecting an array item $(NEWCODE) after an item with $(CODE)' );
|
|
1222
|
+
}
|
|
1190
1223
|
return retval;
|
|
1191
1224
|
}
|
|
1192
1225
|
if (typeof val['#'] === 'string') {
|
|
@@ -1205,12 +1238,19 @@ function annoValue( val, spec ) {
|
|
|
1205
1238
|
return refSplit( val['='], '=' );
|
|
1206
1239
|
}
|
|
1207
1240
|
}
|
|
1208
|
-
else if (val['...'] && Object.keys(val).length === 1) {
|
|
1209
|
-
|
|
1241
|
+
else if (val['...'] && Object.keys( val ).length === 1) {
|
|
1242
|
+
// TODO: only if not nested - see error above
|
|
1243
|
+
++virtualLine;
|
|
1244
|
+
const ell = val['...'];
|
|
1245
|
+
const r = {
|
|
1210
1246
|
val: '...',
|
|
1211
1247
|
literal: 'token',
|
|
1212
1248
|
location: location(),
|
|
1213
1249
|
};
|
|
1250
|
+
if (ell !== true)
|
|
1251
|
+
r.upTo = annoValue( ell, schema['@'] );
|
|
1252
|
+
++virtualLine;
|
|
1253
|
+
return r;
|
|
1214
1254
|
}
|
|
1215
1255
|
const struct = Object.create(null);
|
|
1216
1256
|
++virtualLine;
|
|
@@ -1354,12 +1394,11 @@ function exprArgs( cond, spec, xsn, csn ) {
|
|
|
1354
1394
|
|
|
1355
1395
|
function condition( cond, spec ) {
|
|
1356
1396
|
const loc = location();
|
|
1357
|
-
|
|
1397
|
+
return {
|
|
1358
1398
|
op: { val: 'xpr', location: loc },
|
|
1359
1399
|
args: exprArgs( cond, spec ),
|
|
1360
1400
|
location: loc,
|
|
1361
1401
|
};
|
|
1362
|
-
return x;
|
|
1363
1402
|
}
|
|
1364
1403
|
|
|
1365
1404
|
function vZeroValue( obj, spec, xsn ) {
|
|
@@ -1406,6 +1445,7 @@ function excluding( array, spec, xsn ) {
|
|
|
1406
1445
|
if (!isArray( array, spec ))
|
|
1407
1446
|
return;
|
|
1408
1447
|
const r = Object.create(null);
|
|
1448
|
+
r[$location] = location();
|
|
1409
1449
|
++virtualLine;
|
|
1410
1450
|
for (const ex of array) {
|
|
1411
1451
|
const id = string( ex, spec ) || '';
|
|
@@ -1666,8 +1706,8 @@ function pushLocation( obj ) {
|
|
|
1666
1706
|
error( 'syntax-csn-expected-object', location(true), { prop: '$location' } );
|
|
1667
1707
|
}
|
|
1668
1708
|
// hidden feature: string $location
|
|
1669
|
-
const m = /:(\d+)(?::(\d+)
|
|
1670
|
-
if (!m) {
|
|
1709
|
+
const m = /:(\d+)(?::(\d+))?$/.exec( loc ); // extra '^'s at end deliberately left out
|
|
1710
|
+
if (!m) { // without location or with '^'s: do not use
|
|
1671
1711
|
dollarLocations.push( null );
|
|
1672
1712
|
}
|
|
1673
1713
|
else {
|
|
@@ -1710,8 +1750,10 @@ function toXsn( csn, filename, options, messageFunctions ) {
|
|
|
1710
1750
|
csnFilename = filename;
|
|
1711
1751
|
virtualLine = 1;
|
|
1712
1752
|
dollarLocations = [];
|
|
1753
|
+
arrayLevelCount = 0;
|
|
1713
1754
|
inExtensions = null;
|
|
1714
1755
|
vocabInDefinitions = null;
|
|
1756
|
+
|
|
1715
1757
|
const xsn = { $frontend: 'json' };
|
|
1716
1758
|
|
|
1717
1759
|
// eslint-disable-next-line object-curly-newline
|
package/lib/json/to-csn.js
CHANGED
|
@@ -57,7 +57,7 @@ const transformers = {
|
|
|
57
57
|
key: value,
|
|
58
58
|
unique: value,
|
|
59
59
|
masked: value,
|
|
60
|
-
params
|
|
60
|
+
params,
|
|
61
61
|
// early expression / query properties -------------------------------------
|
|
62
62
|
op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
|
|
63
63
|
from, // before elements!
|
|
@@ -100,7 +100,7 @@ const transformers = {
|
|
|
100
100
|
offset: expression,
|
|
101
101
|
on: onCondition,
|
|
102
102
|
// definitions, extensions, members ----------------------------------------
|
|
103
|
-
returns
|
|
103
|
+
returns, // storing the return type of actions
|
|
104
104
|
notNull: value,
|
|
105
105
|
default: expression,
|
|
106
106
|
// targetElement: ignore, // special display of foreign key, renameTo: select
|
|
@@ -185,7 +185,7 @@ const propertyOrder = (function orderPositions() {
|
|
|
185
185
|
|
|
186
186
|
// sync with definition in from-csn.js:
|
|
187
187
|
const typeProperties = [
|
|
188
|
-
'target', 'elements', 'enum', 'items',
|
|
188
|
+
'target', 'elements', 'enum', 'items', // TODO: notNull?
|
|
189
189
|
'type', 'length', 'precision', 'scale', 'srid', 'localized',
|
|
190
190
|
'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
|
|
191
191
|
];
|
|
@@ -204,7 +204,10 @@ const operators = {
|
|
|
204
204
|
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
205
205
|
case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
|
|
206
206
|
over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
|
|
207
|
-
orderBy: exprs => [
|
|
207
|
+
orderBy: exprs => [ // ORDER BY in generic functions
|
|
208
|
+
...exprs[0], ...operators.overOrderBy(exprs.slice(1)),
|
|
209
|
+
],
|
|
210
|
+
overOrderBy: exprs => [ // ORDER BY in OVER() clause
|
|
208
211
|
'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
209
212
|
],
|
|
210
213
|
partitionBy: exprs => [
|
|
@@ -277,7 +280,7 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
277
280
|
}
|
|
278
281
|
|
|
279
282
|
/**
|
|
280
|
-
* Check
|
|
283
|
+
* Check whether the given object has non enumerable property.
|
|
281
284
|
* Ensure that we don't take it from the prototype, only "directly" - we accidentally
|
|
282
285
|
* cloned elements with a cds.linked input otherwise.
|
|
283
286
|
*
|
|
@@ -556,7 +559,7 @@ function sources( srcDict, csn ) {
|
|
|
556
559
|
}
|
|
557
560
|
}
|
|
558
561
|
|
|
559
|
-
function attachAnnotations( annotate, prop, dict, inferred,
|
|
562
|
+
function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
|
|
560
563
|
const annoDict = Object.create( dictionaryPrototype );
|
|
561
564
|
for (const name in dict) {
|
|
562
565
|
const elem = dict[name];
|
|
@@ -574,7 +577,7 @@ function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
|
|
|
574
577
|
annoDict[name] = sub;
|
|
575
578
|
}
|
|
576
579
|
if (Object.keys( annoDict ).length) {
|
|
577
|
-
if (
|
|
580
|
+
if (insideReturns)
|
|
578
581
|
annotate.returns = { elements: annoDict };
|
|
579
582
|
else
|
|
580
583
|
annotate[prop] = annoDict;
|
|
@@ -621,10 +624,7 @@ function targetAspect( val, csn, node ) {
|
|
|
621
624
|
if (val.$inferred)
|
|
622
625
|
return undefined;
|
|
623
626
|
if (node.target) {
|
|
624
|
-
csn.$origin = {
|
|
625
|
-
if (node.cardinality)
|
|
626
|
-
csn.$origin.cardinality = standard( node.cardinality );
|
|
627
|
-
csn.$origin.target = (val.elements) ? standard( val ) : artifactRef( val, true );
|
|
627
|
+
csn.$origin = { target: (val.elements) ? standard( val ) : artifactRef( val, true ) };
|
|
628
628
|
return undefined;
|
|
629
629
|
}
|
|
630
630
|
}
|
|
@@ -643,7 +643,7 @@ function target( val, _csn, node ) {
|
|
|
643
643
|
val = node._origin.target;
|
|
644
644
|
if (val.elements)
|
|
645
645
|
return standard( val ); // elements in target (parse-cdl)
|
|
646
|
-
if (!universalCsn || node.on)
|
|
646
|
+
if (!universalCsn || gensrcFlavor || node.on)
|
|
647
647
|
return artifactRef( val, true );
|
|
648
648
|
const tref = artifactRef( val, true );
|
|
649
649
|
const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
|
|
@@ -674,7 +674,8 @@ function enumDict( dict, csn, node ) {
|
|
|
674
674
|
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
675
675
|
// hidden or visible 'elements' will be set in query()
|
|
676
676
|
return undefined;
|
|
677
|
-
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate'
|
|
677
|
+
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate' &&
|
|
678
|
+
node.type._artifact && !node.type._artifact.builtin)
|
|
678
679
|
// derived type of enum type with individual annotations: also set $origin
|
|
679
680
|
csn.$origin = originRef( node.type._artifact );
|
|
680
681
|
return insertOrderDict( dict );
|
|
@@ -778,7 +779,7 @@ function addLocation( loc, csn ) {
|
|
|
778
779
|
// Remove endLine/endCol:
|
|
779
780
|
// Reasoning: $location is mostly attached to definitions/members but the name
|
|
780
781
|
// is often not the reason for an error or warning. So we gain little benefit for
|
|
781
|
-
// two more properties.
|
|
782
|
+
// two more properties. It is also an indication that the location is not exact.
|
|
782
783
|
const val = { file: loc.file, line: loc.line, col: loc.col };
|
|
783
784
|
Object.defineProperty( csn, '$location', {
|
|
784
785
|
value: val, configurable: true, writable: true, enumerable: withLocations,
|
|
@@ -806,6 +807,13 @@ function actions( dict ) {
|
|
|
806
807
|
: undefined;
|
|
807
808
|
}
|
|
808
809
|
|
|
810
|
+
function params( dict ) {
|
|
811
|
+
const keys = Object.keys( dict );
|
|
812
|
+
return (keys.length)
|
|
813
|
+
? insertOrderDict( dict )
|
|
814
|
+
: undefined;
|
|
815
|
+
}
|
|
816
|
+
|
|
809
817
|
function dictionary( dict, keys, prop ) {
|
|
810
818
|
const csn = Object.create( dictionaryPrototype );
|
|
811
819
|
for (const name of keys) {
|
|
@@ -834,6 +842,14 @@ function foreignKeys( dict, csn, node ) {
|
|
|
834
842
|
csn.keys = keys;
|
|
835
843
|
}
|
|
836
844
|
|
|
845
|
+
function returns( art, csn, _node, prop ) {
|
|
846
|
+
// TODO: currently, the `returns` structure might just have been created by the propagator
|
|
847
|
+
// if that is the case, there should be no reason to store it in universal CSN
|
|
848
|
+
if (universalCsn && art.$inferred === 'proxy')
|
|
849
|
+
return undefined;
|
|
850
|
+
return definition( art, csn, _node, prop );
|
|
851
|
+
}
|
|
852
|
+
|
|
837
853
|
function definition( art, _csn, _node, prop ) {
|
|
838
854
|
if (!art || typeof art !== 'object')
|
|
839
855
|
return undefined; // TODO: complain with strict
|
|
@@ -854,7 +870,7 @@ function definition( art, _csn, _node, prop ) {
|
|
|
854
870
|
c.returns = { elements: elems };
|
|
855
871
|
}
|
|
856
872
|
// precondition already fulfilled: art.kind !== 'key'
|
|
857
|
-
addOrigin( c, art, art
|
|
873
|
+
addOrigin( c, art, art );
|
|
858
874
|
return c;
|
|
859
875
|
}
|
|
860
876
|
|
|
@@ -867,7 +883,8 @@ function includesOrigin( includes, art ) {
|
|
|
867
883
|
for (const incl of includes.slice(1)) {
|
|
868
884
|
const aspect = incl._artifact;
|
|
869
885
|
for (const prop in aspect) {
|
|
870
|
-
if (prop.charAt(0) === '@'
|
|
886
|
+
if ((prop.charAt(0) === '@' || prop === 'doc') &&
|
|
887
|
+
(!art[prop] || art[prop].$inferred)) {
|
|
871
888
|
const anno = aspect[prop];
|
|
872
889
|
if (anno.val !== null)
|
|
873
890
|
// matererialize non-null annos (whether direct or inherited)
|
|
@@ -878,9 +895,23 @@ function includesOrigin( includes, art ) {
|
|
|
878
895
|
return (Object.keys( result ).length === 1) ? $origin : result;
|
|
879
896
|
}
|
|
880
897
|
|
|
881
|
-
function addOrigin( csn, xsn,
|
|
882
|
-
if (!universalCsn
|
|
898
|
+
function addOrigin( csn, xsn, node ) {
|
|
899
|
+
if (!universalCsn)
|
|
900
|
+
return;
|
|
901
|
+
if (hasExplicitProp( xsn.type, 'cast' )) {
|
|
902
|
+
const main = xsn._main || xsn;
|
|
903
|
+
let count = 0;
|
|
904
|
+
let source = xsn;
|
|
905
|
+
while (source && source._main === main) {
|
|
906
|
+
source = source.value && source.value._artifact;
|
|
907
|
+
++count;
|
|
908
|
+
}
|
|
909
|
+
if (count > 0 && source && source.kind !== 'builtin')
|
|
910
|
+
csn.$source = originRef( source, xsn );
|
|
911
|
+
else if (count > 1)
|
|
912
|
+
csn.$source = null;
|
|
883
913
|
return;
|
|
914
|
+
}
|
|
884
915
|
if (xsn._from) {
|
|
885
916
|
const source = xsn._from[0]._origin;
|
|
886
917
|
csn.$origin = originRef( source );
|
|
@@ -894,23 +925,22 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
894
925
|
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
895
926
|
return;
|
|
896
927
|
}
|
|
897
|
-
|
|
928
|
+
let origin = getOrigin( node );
|
|
929
|
+
if (xsn.$inferred === 'composition-entity') {
|
|
930
|
+
csn.$origin = originRef( origin, xsn );
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
else if (!isMember( xsn ) || xsn.kind === 'select') {
|
|
898
934
|
return;
|
|
899
935
|
}
|
|
936
|
+
// from here on: member:
|
|
900
937
|
const parent = getParent( xsn );
|
|
901
938
|
const parentOrigin = getOrigin( parent );
|
|
902
|
-
if (!
|
|
903
|
-
if (parentOrigin && (
|
|
939
|
+
if (!origin) {
|
|
940
|
+
if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
|
|
904
941
|
csn.$origin = null;
|
|
905
942
|
return;
|
|
906
943
|
}
|
|
907
|
-
// Skip all proxies which do not make it into the CSN, as there are no
|
|
908
|
-
// individual annotations or redirection targets on it:
|
|
909
|
-
while (origin._parent && origin._parent.$expand === 'origin')
|
|
910
|
-
origin = origin._origin || origin.type._artifact;
|
|
911
|
-
// The while loop is not only for the else case below: when setting implicit
|
|
912
|
-
// prototypes, it is important that we do not have to follow the prototypes of
|
|
913
|
-
// other object; we would need to ensure a right order to avoid issues otherwise.
|
|
914
944
|
if (parentOrigin === getParent( origin )) {
|
|
915
945
|
// implicit prototype or shortened reference
|
|
916
946
|
const { id } = origin.name || {};
|
|
@@ -919,16 +949,21 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
919
949
|
return;
|
|
920
950
|
}
|
|
921
951
|
if (origin.kind === 'mixin') {
|
|
922
|
-
|
|
923
|
-
csn
|
|
952
|
+
set( 'type', csn, origin );
|
|
953
|
+
set( 'cardinality', csn, origin );
|
|
954
|
+
// currently, target and on are always set - nothing to do here
|
|
924
955
|
return;
|
|
925
956
|
}
|
|
957
|
+
// Skip all proxies which do not make it into the CSN, as there are no
|
|
958
|
+
// individual annotations or redirection targets on it:
|
|
959
|
+
while (origin._parent && origin._parent.$expand === 'origin')
|
|
960
|
+
origin = origin._origin || origin.type._artifact;
|
|
926
961
|
const ref = originRef( origin, xsn );
|
|
927
962
|
if (ref) {
|
|
928
963
|
csn.$origin = ref;
|
|
929
964
|
return;
|
|
930
965
|
}
|
|
931
|
-
// An element of a query with a query in FROM:
|
|
966
|
+
// An element of a query with a query in FROM: -----------------------------
|
|
932
967
|
const anon = definition( origin ); // use $origin: {...} if necessary
|
|
933
968
|
// as there are no implicit $origin prototypes on sub query elements (yet),
|
|
934
969
|
// we do not have to care about $origin not being set
|
|
@@ -936,15 +971,26 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
936
971
|
if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
|
|
937
972
|
// repeated anon: flatten
|
|
938
973
|
csn.$origin = Object.assign( $origin, anon );
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
// Annotations and 'doc' must keep the distinction between direct or inherited,
|
|
977
|
+
// other properties can as well be set as direct element properties
|
|
978
|
+
const annos = {};
|
|
979
|
+
for (const prop of Object.keys( anon )) {
|
|
980
|
+
if (prop.charAt(0) === '@' || prop === 'doc')
|
|
981
|
+
annos[prop] = anon[prop];
|
|
982
|
+
else if (prop === '$source')
|
|
983
|
+
csn[prop] = anon[prop]; // overwrite from inner
|
|
984
|
+
else if (prop !== '$location' && prop !== '$origin' && !(prop in csn))
|
|
985
|
+
csn[prop] = anon[prop];
|
|
939
986
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
addOrigin( csn, xsn, origin._origin );
|
|
987
|
+
if (Object.keys( annos ).length) {
|
|
988
|
+
if (!csn.type && $origin)
|
|
989
|
+
annos.$origin = $origin;
|
|
990
|
+
csn.$origin = annos;
|
|
945
991
|
}
|
|
946
|
-
else {
|
|
947
|
-
csn
|
|
992
|
+
else if (!csn.type) {
|
|
993
|
+
addOrigin( csn, xsn, origin );
|
|
948
994
|
}
|
|
949
995
|
}
|
|
950
996
|
|
|
@@ -954,10 +1000,27 @@ function getParent( art ) {
|
|
|
954
1000
|
return (main && parent === main._leadingQuery) ? main : parent;
|
|
955
1001
|
}
|
|
956
1002
|
|
|
1003
|
+
function isMember( art ) {
|
|
1004
|
+
// TODO: introduce art.kind = '$aspect' for anonymous aspect (is a member) ?
|
|
1005
|
+
return !!(art._main || art._outer);
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// function getDefinition( art ) {
|
|
1009
|
+
// let main = art._main || art;
|
|
1010
|
+
// while (main._outer) // anonymous aspect
|
|
1011
|
+
// main = main._outer._main;
|
|
1012
|
+
// return main;
|
|
1013
|
+
// }
|
|
1014
|
+
|
|
1015
|
+
// XSN `_origin` is (currently?) not the same as _origin in Universal CSN...
|
|
1016
|
+
// TODO: at least with expand, set it correctly (alias: keep, assoc: to entity, $builtin: no)
|
|
957
1017
|
function getOrigin( art ) {
|
|
958
|
-
if (art
|
|
959
|
-
return
|
|
960
|
-
|
|
1018
|
+
if (art.$noOrigin)
|
|
1019
|
+
return undefined;
|
|
1020
|
+
const { _origin } = art;
|
|
1021
|
+
if (_origin)
|
|
1022
|
+
return (_origin.kind === 'builtin') ? undefined : _origin; // not $dollarVariable
|
|
1023
|
+
if (hasExplicitProp( art.type, 'cast' ))
|
|
961
1024
|
return art.type._artifact;
|
|
962
1025
|
if (art.includes)
|
|
963
1026
|
return art.includes[0]._artifact;
|
|
@@ -966,31 +1029,31 @@ function getOrigin( art ) {
|
|
|
966
1029
|
return undefined;
|
|
967
1030
|
}
|
|
968
1031
|
|
|
969
|
-
function hasExplicitProp( ref ) {
|
|
970
|
-
return ref && !ref.$inferred;
|
|
1032
|
+
function hasExplicitProp( ref, alsoLikeExplicit ) {
|
|
1033
|
+
return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
|
|
971
1034
|
}
|
|
972
1035
|
|
|
973
1036
|
function originRef( art, user ) {
|
|
974
1037
|
const r = [];
|
|
975
|
-
// do not use name.element, as we allow `.`s in name
|
|
1038
|
+
// do not use name.element, as we might allow `.`s in name
|
|
976
1039
|
let parent = art;
|
|
977
|
-
|
|
1040
|
+
if (parent._outer && parent.kind === 'aspect')
|
|
1041
|
+
r.push( { target: true } );
|
|
1042
|
+
while (isMember( parent ) && parent.kind !== 'select') {
|
|
978
1043
|
const nkind = normalizedKind[parent.kind];
|
|
979
|
-
|
|
1044
|
+
const name = parent.name || parent._outer.name;
|
|
1045
|
+
if (name.id || !r.length)
|
|
980
1046
|
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
981
1047
|
// eslint-disable-next-line no-nested-ternary, max-len
|
|
982
|
-
r.push( !nkind ?
|
|
1048
|
+
r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
|
|
983
1049
|
parent = parent._parent;
|
|
984
1050
|
}
|
|
985
1051
|
if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
|
|
986
1052
|
// well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
|
|
987
|
-
return
|
|
1053
|
+
return false; // do not write, probably use $origin: {...}
|
|
988
1054
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
989
1055
|
|
|
990
|
-
|
|
991
|
-
if (r.length === 1 && normalizedKind[art.kind] === 'action')
|
|
992
|
-
return [ art.name.absolute, art.name.id ];
|
|
993
|
-
r.push( art.name.absolute );
|
|
1056
|
+
r.push( parent.name.absolute );
|
|
994
1057
|
r.reverse();
|
|
995
1058
|
return r;
|
|
996
1059
|
}
|
|
@@ -1009,6 +1072,9 @@ function kind( k, csn, node ) {
|
|
|
1009
1072
|
else if (k === 'action' && node._main && universalCsn && node.$inferred) {
|
|
1010
1073
|
// Universal CSN: do not mention kind: 'action' on expanded action
|
|
1011
1074
|
}
|
|
1075
|
+
else if (k === 'aspect' && (node._outer || node.$inferred)) {
|
|
1076
|
+
return; // do not show kind for anonymous aspect
|
|
1077
|
+
}
|
|
1012
1078
|
else if (![
|
|
1013
1079
|
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
1014
1080
|
'$tableAlias', 'annotation', 'mixin',
|
|
@@ -1023,26 +1089,20 @@ function kind( k, csn, node ) {
|
|
|
1023
1089
|
function type( node, csn, xsn ) {
|
|
1024
1090
|
if (!universalCsn)
|
|
1025
1091
|
return artifactRef( node, !node.$extra );
|
|
1026
|
-
if (node.$inferred)
|
|
1092
|
+
if (node.$inferred && node.$inferred !== 'cast')
|
|
1027
1093
|
return undefined;
|
|
1028
1094
|
if (xsn._origin) {
|
|
1029
1095
|
if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
|
|
1030
1096
|
csn.$origin = definition( xsn._origin );
|
|
1031
|
-
return undefined;
|
|
1032
1097
|
}
|
|
1033
1098
|
}
|
|
1034
|
-
else if ( xsn.targetAspect && xsn.target ) {
|
|
1035
|
-
// type moved to $origin: { type: … }
|
|
1036
|
-
return undefined;
|
|
1037
|
-
}
|
|
1038
1099
|
return artifactRef( node, !node.$extra );
|
|
1039
1100
|
}
|
|
1040
1101
|
|
|
1041
|
-
function cardinality( node
|
|
1102
|
+
function cardinality( node ) {
|
|
1042
1103
|
if (!universalCsn)
|
|
1043
1104
|
return standard( node );
|
|
1044
|
-
|
|
1045
|
-
if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
|
|
1105
|
+
if (node.$inferred)
|
|
1046
1106
|
return undefined;
|
|
1047
1107
|
return standard( node );
|
|
1048
1108
|
}
|
|
@@ -1150,8 +1210,12 @@ function value( node ) {
|
|
|
1150
1210
|
// "Short" value form, e.g. for annotation assignments
|
|
1151
1211
|
if (!node)
|
|
1152
1212
|
return true; // `@aBool` short for `@aBool: true`
|
|
1153
|
-
if (universalCsn && node.$inferred
|
|
1154
|
-
|
|
1213
|
+
if (universalCsn && node.$inferred) {
|
|
1214
|
+
if (node.$inferred === 'prop' || node.$inferred === '$generated') // via propagator.js
|
|
1215
|
+
return undefined;
|
|
1216
|
+
else if (node.$inferred === 'NULL')
|
|
1217
|
+
return null;
|
|
1218
|
+
}
|
|
1155
1219
|
if (node.$inferred && gensrcFlavor)
|
|
1156
1220
|
return undefined;
|
|
1157
1221
|
if (node.path) {
|
|
@@ -1163,7 +1227,7 @@ function value( node ) {
|
|
|
1163
1227
|
if (node.literal === 'array')
|
|
1164
1228
|
return node.val.map( value );
|
|
1165
1229
|
if (node.literal === 'token' && node.val === '...')
|
|
1166
|
-
return extra( { '...':
|
|
1230
|
+
return extra( { '...': !node.upTo || value( node.upTo ) } );
|
|
1167
1231
|
if (node.literal !== 'struct')
|
|
1168
1232
|
// no val (undefined) as true only for annotation values (and struct elem values)
|
|
1169
1233
|
return node.name && !('val' in node) || node.val;
|
|
@@ -1320,7 +1384,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
|
1320
1384
|
if (node.op.val === 'SELECT') {
|
|
1321
1385
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
1322
1386
|
node.from && node.from.path && !projectionAsQuery) {
|
|
1323
|
-
csn.projection = standard( node );
|
|
1387
|
+
csn.projection = addLocation( node.location, standard( node ) );
|
|
1324
1388
|
return undefined;
|
|
1325
1389
|
}
|
|
1326
1390
|
const select = { SELECT: extra( standard( node ), node, expectedParens ) };
|
|
@@ -1342,6 +1406,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
|
1342
1406
|
gensrcFlavor = gensrcSaved;
|
|
1343
1407
|
}
|
|
1344
1408
|
}
|
|
1409
|
+
// the $location is better put inside the SELECT value, not as sibling (but
|
|
1410
|
+
// we keep it as sibling also for compatibility):
|
|
1411
|
+
addLocation( node.location, select.SELECT );
|
|
1345
1412
|
return addLocation( node.location, select );
|
|
1346
1413
|
}
|
|
1347
1414
|
const union = {};
|
|
@@ -1390,11 +1457,13 @@ function from( node ) {
|
|
|
1390
1457
|
else if (node.query) {
|
|
1391
1458
|
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1392
1459
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1460
|
+
|
|
1461
|
+
const ref = artifactRef( node, false );
|
|
1462
|
+
return extra( addExplicitAs( ref, node.name, (id) => {
|
|
1463
|
+
let name = ref.ref ? ref.ref[ref.ref.length - 1] : ref;
|
|
1464
|
+
name = name && name.id || name;
|
|
1465
|
+
if (!name)
|
|
1466
|
+
return false;
|
|
1398
1467
|
const dot = name.lastIndexOf('.');
|
|
1399
1468
|
return name.substring( dot + 1 ) !== id;
|
|
1400
1469
|
}), node );
|
|
@@ -1443,7 +1512,7 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1443
1512
|
}
|
|
1444
1513
|
|
|
1445
1514
|
function orderBy( node ) {
|
|
1446
|
-
const expr = expression( node
|
|
1515
|
+
const expr = expression( node );
|
|
1447
1516
|
if (node.sort)
|
|
1448
1517
|
expr.sort = node.sort.val;
|
|
1449
1518
|
if (node.nulls)
|
|
@@ -1516,6 +1585,9 @@ function compactExpr( e ) { // TODO: options
|
|
|
1516
1585
|
return e && expression( e, true );
|
|
1517
1586
|
}
|
|
1518
1587
|
|
|
1588
|
+
/**
|
|
1589
|
+
* @param {CSN.Options} options
|
|
1590
|
+
*/
|
|
1519
1591
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1520
1592
|
gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
|
|
1521
1593
|
options.toCsn && options.toCsn.flavor === 'gensrc';
|
|
@@ -112,7 +112,7 @@ const rules = {
|
|
|
112
112
|
expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions, rule = 'cdl' ) {
|
|
115
|
+
function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions = null, rule = 'cdl' ) {
|
|
116
116
|
const lexer = new Lexer( new antlr4.InputStream(source) );
|
|
117
117
|
const tokenStream = new RewriteTypeTokenStream(lexer);
|
|
118
118
|
/** @type {object} */
|
|
@@ -166,8 +166,8 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
|
|
|
166
166
|
// Do not warn if docComments are explicitly disabled.
|
|
167
167
|
if (options.docComment !== false) {
|
|
168
168
|
for (const token of tokenStream.tokens) {
|
|
169
|
-
if (token.
|
|
170
|
-
messageFunctions.info('syntax-ignoring-doc-comment', parser.
|
|
169
|
+
if (token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
170
|
+
messageFunctions.info('syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
|
|
171
171
|
"Ignoring doc-comment as it does not belong to any artifact");
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -69,7 +69,7 @@ function parseDocComment(comment) {
|
|
|
69
69
|
* @param {string} content
|
|
70
70
|
*/
|
|
71
71
|
function isWhiteSpaceOnly(content) {
|
|
72
|
-
return content
|
|
72
|
+
return /^\s*$/.test(content);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -127,8 +127,8 @@ function removeFooterFence(line) {
|
|
|
127
127
|
* @param {string[]} lines
|
|
128
128
|
*/
|
|
129
129
|
function isFencedComment(lines) {
|
|
130
|
-
const index = lines.findIndex((line,
|
|
131
|
-
const exclude = (
|
|
130
|
+
const index = lines.findIndex((line, i) => {
|
|
131
|
+
const exclude = (i === 0 || i === lines.length - 1);
|
|
132
132
|
return !exclude && !(/^\s*[*]/.test(line));
|
|
133
133
|
});
|
|
134
134
|
return index === -1 && lines.length > 2;
|