@sap/cds-compiler 2.7.0 → 2.10.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 +103 -0
- package/lib/api/main.js +8 -10
- package/lib/api/options.js +13 -9
- package/lib/api/validate.js +11 -8
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +16 -0
- package/lib/base/messages.js +2 -0
- package/lib/base/model.js +1 -0
- package/lib/checks/onConditions.js +5 -0
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +38 -0
- package/lib/checks/validator.js +7 -2
- package/lib/compiler/assert-consistency.js +11 -5
- package/lib/compiler/builtins.js +2 -0
- package/lib/compiler/checks.js +3 -1
- package/lib/compiler/definer.js +87 -29
- package/lib/compiler/resolver.js +75 -16
- package/lib/compiler/shared.js +29 -9
- package/lib/edm/annotations/genericTranslation.js +182 -186
- package/lib/edm/csn2edm.js +93 -98
- package/lib/edm/edm.js +16 -20
- package/lib/edm/edmPreprocessor.js +274 -83
- package/lib/edm/edmUtils.js +29 -10
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +12 -1
- package/lib/gen/language.tokens +57 -53
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +770 -744
- package/lib/gen/languageLexer.tokens +49 -46
- package/lib/gen/languageParser.js +4727 -4323
- package/lib/json/from-csn.js +52 -23
- package/lib/json/to-csn.js +185 -71
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +9 -0
- package/lib/language/language.g4 +90 -31
- package/lib/main.js +4 -0
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +7 -1
- package/lib/model/csnUtils.js +5 -4
- package/lib/optionProcessor.js +7 -1
- package/lib/render/.eslintrc.json +3 -1
- package/lib/render/toCdl.js +45 -9
- package/lib/render/toHdbcds.js +100 -34
- package/lib/render/toSql.js +12 -4
- package/lib/render/utils/common.js +5 -9
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/draft.js +6 -4
- package/lib/transform/db/expansion.js +14 -4
- package/lib/transform/db/flattening.js +13 -5
- package/lib/transform/db/transformExists.js +252 -58
- package/lib/transform/forHanaNew.js +7 -1
- package/lib/transform/forOdataNew.js +12 -8
- 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 +76 -0
- package/lib/transform/odata/structureFlattener.js +13 -10
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +33 -1
- package/lib/transform/translateAssocsToJoins.js +6 -4
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/package.json +1 -1
package/lib/json/from-csn.js
CHANGED
|
@@ -145,7 +145,7 @@ const schemaClasses = {
|
|
|
145
145
|
type: natnumOrStar,
|
|
146
146
|
msgId: 'syntax-csn-expected-cardinality',
|
|
147
147
|
},
|
|
148
|
-
|
|
148
|
+
columns: {
|
|
149
149
|
arrayOf: selectItem,
|
|
150
150
|
msgId: 'syntax-csn-expected-column',
|
|
151
151
|
defaultKind: '$column',
|
|
@@ -238,15 +238,15 @@ const schema = compileSchema( {
|
|
|
238
238
|
validKinds: [],
|
|
239
239
|
},
|
|
240
240
|
columns: {
|
|
241
|
-
class: '
|
|
241
|
+
class: 'columns',
|
|
242
242
|
inKind: [ 'extend' ], // only valid in extend and SELECT/projection
|
|
243
243
|
},
|
|
244
244
|
expand: {
|
|
245
|
-
class: '
|
|
245
|
+
class: 'columns',
|
|
246
246
|
inKind: [ '$column' ], // only valid in $column
|
|
247
247
|
},
|
|
248
248
|
inline: {
|
|
249
|
-
class: '
|
|
249
|
+
class: 'columns',
|
|
250
250
|
inKind: [ '$column' ], // only valid in $column
|
|
251
251
|
},
|
|
252
252
|
keys: {
|
|
@@ -272,7 +272,7 @@ const schema = compileSchema( {
|
|
|
272
272
|
},
|
|
273
273
|
annotate: {
|
|
274
274
|
type: kindAndName,
|
|
275
|
-
inKind:
|
|
275
|
+
inKind: [ 'annotate' ],
|
|
276
276
|
},
|
|
277
277
|
extend: {
|
|
278
278
|
type: kindAndName,
|
|
@@ -326,7 +326,7 @@ const schema = compileSchema( {
|
|
|
326
326
|
inKind: [ 'element', 'type', 'param', 'annotation' ],
|
|
327
327
|
},
|
|
328
328
|
scale: {
|
|
329
|
-
type:
|
|
329
|
+
type: scalenum,
|
|
330
330
|
inKind: [ 'element', 'type', 'param', 'annotation' ],
|
|
331
331
|
},
|
|
332
332
|
srid: {
|
|
@@ -609,10 +609,10 @@ const schema = compileSchema( {
|
|
|
609
609
|
inKind: [ 'entity', 'type', 'aspect', 'event', 'extend' ],
|
|
610
610
|
},
|
|
611
611
|
returns: {
|
|
612
|
-
type:
|
|
612
|
+
type: returnsDefinition,
|
|
613
613
|
defaultKind: 'param',
|
|
614
614
|
validKinds: [ 'param' ],
|
|
615
|
-
inKind: [ 'action', 'function' ],
|
|
615
|
+
inKind: [ 'action', 'function', 'annotate' ],
|
|
616
616
|
},
|
|
617
617
|
technicalConfig: { // treat it like external_property
|
|
618
618
|
type: extra,
|
|
@@ -739,14 +739,15 @@ function compileSchema( specs, proto = null) {
|
|
|
739
739
|
throw new Error( `Missing type specification for property "${ p }"` );
|
|
740
740
|
}
|
|
741
741
|
}
|
|
742
|
-
|
|
743
|
-
return r;
|
|
742
|
+
// Set property 'xorGroup' in main and sub schema:
|
|
744
743
|
for (const group in xorGroups) {
|
|
745
744
|
for (const prop of xorGroups[group]) {
|
|
746
745
|
if (r[prop].xorGroup === undefined)
|
|
747
746
|
r[prop].xorGroup = group;
|
|
748
747
|
}
|
|
749
748
|
}
|
|
749
|
+
if (proto)
|
|
750
|
+
return r;
|
|
750
751
|
for (const prop of exprProperties) {
|
|
751
752
|
if (r[prop].inValue === undefined)
|
|
752
753
|
r[prop].inValue = true;
|
|
@@ -944,7 +945,10 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
944
945
|
return s.inKind && s.inKind( kind, spec );
|
|
945
946
|
return s.inKind.includes( kind ) &&
|
|
946
947
|
// for an 'annotate', both 'annotate' and the "host" kind must be expected
|
|
947
|
-
(!inExtensions || s.inKind.includes( inExtensions )
|
|
948
|
+
(!inExtensions || s.inKind.includes( inExtensions ) ||
|
|
949
|
+
// extending elements in returns can be without 'returns' in CSN
|
|
950
|
+
// TODO: with warning/info?
|
|
951
|
+
inExtensions === 'action' && p === 'elements');
|
|
948
952
|
}
|
|
949
953
|
}
|
|
950
954
|
|
|
@@ -992,12 +996,23 @@ function keys( array, spec, xsn ) {
|
|
|
992
996
|
}
|
|
993
997
|
|
|
994
998
|
function selectItem( def, spec, xsn, csn ) {
|
|
995
|
-
if (def === '*')
|
|
999
|
+
if (def === '*') // compile() will complain about repeated '*'s
|
|
996
1000
|
return { val: '*', location: location() };
|
|
997
1001
|
|
|
998
1002
|
return definition( def, spec, xsn, csn, null ); // definer sets name
|
|
999
1003
|
}
|
|
1000
1004
|
|
|
1005
|
+
function returnsDefinition( def, spec, xsn, csn, name ) {
|
|
1006
|
+
// TODO: be stricter in what is allowed inside returns
|
|
1007
|
+
if (!inExtensions)
|
|
1008
|
+
return definition( def, spec, xsn, csn, name );
|
|
1009
|
+
// for the moment, flatten elements in returns in an annotate
|
|
1010
|
+
// TODO: bigger Core Compiler changes would have to be done otherwise
|
|
1011
|
+
xsn.elements = definition( def, spec, xsn, csn, name ).elements;
|
|
1012
|
+
xsn.$syntax = 'returns';
|
|
1013
|
+
return undefined;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1001
1016
|
// For v1 CSNs with annotation definitions
|
|
1002
1017
|
function attachVocabInDefinitions( csn ) {
|
|
1003
1018
|
if (!csn.vocabularies) {
|
|
@@ -1124,6 +1139,12 @@ function stringValOrNull( val, spec ) {
|
|
|
1124
1139
|
return stringVal(val, spec);
|
|
1125
1140
|
}
|
|
1126
1141
|
|
|
1142
|
+
function scalenum( val, spec ) {
|
|
1143
|
+
if ([ 'floating', 'variable' ].includes(val))
|
|
1144
|
+
return { val, literal: 'string', location: location() };
|
|
1145
|
+
return natnum(val, spec );
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1127
1148
|
function natnum( val, spec ) {
|
|
1128
1149
|
if (typeof val === 'number' && val >= 0)
|
|
1129
1150
|
// XSN TODO: do not require literal
|
|
@@ -1248,9 +1269,14 @@ function func( val, spec, xsn ) {
|
|
|
1248
1269
|
return { path: [ { id: val, location: location() } ], location: location() };
|
|
1249
1270
|
}
|
|
1250
1271
|
|
|
1251
|
-
function xpr( exprs, spec, xsn ) {
|
|
1252
|
-
|
|
1253
|
-
|
|
1272
|
+
function xpr( exprs, spec, xsn, csn ) {
|
|
1273
|
+
if (csn.func) {
|
|
1274
|
+
xsn.suffix = exprArgs( exprs, spec );
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
xsn.op = { val: 'xpr', location: location() };
|
|
1278
|
+
xsn.args = exprArgs( exprs, spec, xsn );
|
|
1279
|
+
}
|
|
1254
1280
|
}
|
|
1255
1281
|
|
|
1256
1282
|
function list( exprs, spec, xsn ) {
|
|
@@ -1258,15 +1284,15 @@ function list( exprs, spec, xsn ) {
|
|
|
1258
1284
|
xsn.args = arrayOf( exprOrString )( exprs, spec, xsn );
|
|
1259
1285
|
}
|
|
1260
1286
|
|
|
1261
|
-
function xprInValue( exprs, spec, xsn ) {
|
|
1287
|
+
function xprInValue( exprs, spec, xsn, csn ) {
|
|
1262
1288
|
// if the top-level xpr is just for a cast:
|
|
1263
1289
|
if (exprs.length === 1 && exprs[0].cast) {
|
|
1264
1290
|
const x = {};
|
|
1265
|
-
xpr( exprs, spec, x );
|
|
1291
|
+
xpr( exprs, spec, x, csn );
|
|
1266
1292
|
Object.assign( xsn, x.args[0] );
|
|
1267
1293
|
}
|
|
1268
1294
|
else {
|
|
1269
|
-
xpr( exprs, spec, xsn );
|
|
1295
|
+
xpr( exprs, spec, xsn, csn );
|
|
1270
1296
|
}
|
|
1271
1297
|
}
|
|
1272
1298
|
|
|
@@ -1315,9 +1341,9 @@ function exprOrString( e, spec ) {
|
|
|
1315
1341
|
}
|
|
1316
1342
|
|
|
1317
1343
|
// mark path argument of 'exits' predicate with $expected:'exists'
|
|
1318
|
-
function
|
|
1319
|
-
const rxsn = arrayOf( exprOrString )(cond, spec, xsn, csn);
|
|
1320
|
-
if (Array.isArray(rxsn) && rxsn.some(x => x === 'exists')) {
|
|
1344
|
+
function exprArgs( cond, spec, xsn, csn ) {
|
|
1345
|
+
const rxsn = arrayOf( exprOrString )( cond, spec, xsn, csn );
|
|
1346
|
+
if (Array.isArray( rxsn ) && rxsn.some( x => x === 'exists' )) {
|
|
1321
1347
|
for (let i = 0; i < rxsn.length - 1; i++) {
|
|
1322
1348
|
if (rxsn[i] === 'exists' && rxsn[i + 1].path)
|
|
1323
1349
|
rxsn[++i].$expected = 'exists';
|
|
@@ -1330,7 +1356,7 @@ function condition( cond, spec ) {
|
|
|
1330
1356
|
const loc = location();
|
|
1331
1357
|
const x = {
|
|
1332
1358
|
op: { val: 'xpr', location: loc },
|
|
1333
|
-
args:
|
|
1359
|
+
args: exprArgs( cond, spec ),
|
|
1334
1360
|
location: loc,
|
|
1335
1361
|
};
|
|
1336
1362
|
return x;
|
|
@@ -1500,7 +1526,7 @@ function calculateKind( def, spec ) {
|
|
|
1500
1526
|
return 'annotate';
|
|
1501
1527
|
}
|
|
1502
1528
|
if (spec.prop === 'extensions') {
|
|
1503
|
-
inExtensions = (def.extend) ? '' : '
|
|
1529
|
+
inExtensions = (def.extend) ? '' : 'annotate';
|
|
1504
1530
|
return (def.extend) ? 'extend' : 'annotate';
|
|
1505
1531
|
}
|
|
1506
1532
|
const kind = (def.kind === 'view') ? 'entity' : def.kind; // 'view' is CSN v0.1.0
|
|
@@ -1556,6 +1582,9 @@ function checkAndSetXorGroup( group, prop, xor ) {
|
|
|
1556
1582
|
xor[group] = prop;
|
|
1557
1583
|
return true;
|
|
1558
1584
|
}
|
|
1585
|
+
if (prop === 'func' && xor[group] === 'xpr' ||
|
|
1586
|
+
prop === 'xpr' && xor[group] === 'func')
|
|
1587
|
+
return true; // hack for window function: both func and xpr is allowed
|
|
1559
1588
|
error( 'syntax-csn-excluded-property', location(true),
|
|
1560
1589
|
{ prop, otherprop: xor[group] },
|
|
1561
1590
|
'CSN property $(PROP) can only be used alternatively to $(OTHERPROP)');
|
package/lib/json/to-csn.js
CHANGED
|
@@ -80,6 +80,7 @@ const transformers = {
|
|
|
80
80
|
where: condition, // also pathItem after 'cardinality' before 'args'
|
|
81
81
|
having: condition,
|
|
82
82
|
args, // also pathItem after 'where', before 'on'/'orderBy'
|
|
83
|
+
suffix: node => [].concat( ...node.suffix.map( xprArg ) ),
|
|
83
84
|
orderBy: arrayOf( orderBy ), // TODO XSN: make `sort` and `nulls` sibling properties
|
|
84
85
|
sort: value,
|
|
85
86
|
nulls: value,
|
|
@@ -95,7 +96,7 @@ const transformers = {
|
|
|
95
96
|
value: enumValue, // do not list for select items as elements
|
|
96
97
|
query,
|
|
97
98
|
elements,
|
|
98
|
-
actions
|
|
99
|
+
actions, // TODO: just normal dictionary
|
|
99
100
|
// special: top-level, cardinality -----------------------------------------
|
|
100
101
|
sources,
|
|
101
102
|
definitions: sortedDict,
|
|
@@ -191,6 +192,13 @@ const operators = {
|
|
|
191
192
|
notLike: ternary( [ 'not', 'like' ], [ 'escape' ] ),
|
|
192
193
|
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
193
194
|
case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
|
|
195
|
+
over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
|
|
196
|
+
orderBy: exprs => [
|
|
197
|
+
'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
198
|
+
],
|
|
199
|
+
partitionBy: exprs => [
|
|
200
|
+
'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
201
|
+
],
|
|
194
202
|
// xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
|
|
195
203
|
};
|
|
196
204
|
|
|
@@ -232,22 +240,36 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
232
240
|
r[n] = sortCsn(val, cloneOptions);
|
|
233
241
|
}
|
|
234
242
|
if (cloneOptions && typeof csn === 'object') {
|
|
235
|
-
if (csn
|
|
236
|
-
setHidden(r, '$sources', csn.$sources);
|
|
237
|
-
if (csn
|
|
238
|
-
setHidden(r, '$location', csn.$location);
|
|
239
|
-
if (csn
|
|
240
|
-
setHidden(r, '$path', csn.$path);
|
|
241
|
-
if (csn
|
|
242
|
-
setHidden(r, '$paths', csn.$paths);
|
|
243
|
-
if (csn
|
|
244
|
-
setHidden(r, 'elements', csnDictionary( csn.elements, false, cloneOptions ) );
|
|
245
|
-
if (csn
|
|
246
|
-
setHidden(r, '$tableConstraints', csn.$tableConstraints);
|
|
243
|
+
if ({}.hasOwnProperty.call( csn, '$sources' ) && !r.$sources)
|
|
244
|
+
setHidden( r, '$sources', csn.$sources );
|
|
245
|
+
if ({}.hasOwnProperty.call( csn, '$location' ) && !r.$location)
|
|
246
|
+
setHidden( r, '$location', csn.$location );
|
|
247
|
+
if ({}.hasOwnProperty.call( csn, '$path' )) // used in generic reference flattener
|
|
248
|
+
setHidden( r, '$path', csn.$path );
|
|
249
|
+
if ({}.hasOwnProperty.call( csn, '$paths' )) // used in generic reference flattener
|
|
250
|
+
setHidden( r, '$paths', csn.$paths );
|
|
251
|
+
if (hasNonEnumerable( csn, 'elements' ) && !r.elements) // non-enumerable 'elements'
|
|
252
|
+
setHidden( r, 'elements', csnDictionary( csn.elements, false, cloneOptions ) );
|
|
253
|
+
if (hasNonEnumerable( csn, '$tableConstraints' ) && !r.$tableConstraints)
|
|
254
|
+
setHidden( r, '$tableConstraints', csn.$tableConstraints );
|
|
247
255
|
}
|
|
248
256
|
return r;
|
|
249
257
|
}
|
|
250
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Check wether the given object has non enumerable property.
|
|
261
|
+
* Ensure that we don't take it from the prototype, only "directly" - we accidentally
|
|
262
|
+
* cloned elements with a cds.linked input otherwise.
|
|
263
|
+
*
|
|
264
|
+
* @param {object} object
|
|
265
|
+
* @param {string} property
|
|
266
|
+
* @returns
|
|
267
|
+
*/
|
|
268
|
+
function hasNonEnumerable(object, property) {
|
|
269
|
+
return {}.hasOwnProperty.call( object, property ) &&
|
|
270
|
+
!{}.propertyIsEnumerable.call( object, property );
|
|
271
|
+
}
|
|
272
|
+
|
|
251
273
|
/**
|
|
252
274
|
* @param {object} csn
|
|
253
275
|
* @param {boolean} sort
|
|
@@ -359,10 +381,12 @@ function usings( srcDict ) {
|
|
|
359
381
|
* @param {object} csn
|
|
360
382
|
* @param {object} model
|
|
361
383
|
*/
|
|
384
|
+
|
|
385
|
+
|
|
362
386
|
function extensions( node, csn, model ) {
|
|
363
387
|
if (model.kind && model.kind !== 'source')
|
|
364
388
|
return undefined;
|
|
365
|
-
const exts = node.map(
|
|
389
|
+
const exts = node.map( definition );
|
|
366
390
|
|
|
367
391
|
// builtins are non-enumerable for smaller display
|
|
368
392
|
for (const name of Object.getOwnPropertyNames( model.definitions || {} ).sort()) {
|
|
@@ -377,17 +401,21 @@ function extensions( node, csn, model ) {
|
|
|
377
401
|
}
|
|
378
402
|
else if (gensrcFlavor) {
|
|
379
403
|
// From definitions (without redefinitions) with potential inferred elements:
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (
|
|
387
|
-
annotate.
|
|
388
|
-
|
|
389
|
-
|
|
404
|
+
const annotate = { annotate: name };
|
|
405
|
+
if (art.$inferred)
|
|
406
|
+
Object.assign( annotate, annotationsAndDocComment( art, true ) );
|
|
407
|
+
if (art.$expand === 'annotate') {
|
|
408
|
+
if (art.actions)
|
|
409
|
+
attachAnnotations( annotate, 'actions', art.actions, art.$inferred );
|
|
410
|
+
else if (art.params)
|
|
411
|
+
attachAnnotations( annotate, 'params', art.params, art.$inferred );
|
|
412
|
+
const obj = art.returns || art;
|
|
413
|
+
const elems = (obj.items || obj).elements; // no targetAspect here
|
|
414
|
+
if (elems)
|
|
415
|
+
attachAnnotations( annotate, 'elements', elems, art.$inferred, art.returns );
|
|
390
416
|
}
|
|
417
|
+
if (Object.keys( annotate ).length > 1)
|
|
418
|
+
exts.push( annotate );
|
|
391
419
|
}
|
|
392
420
|
}
|
|
393
421
|
|
|
@@ -395,6 +423,58 @@ function extensions( node, csn, model ) {
|
|
|
395
423
|
(a, b) => (a.annotate || a.extend).localeCompare( b.annotate || b.extend )
|
|
396
424
|
);
|
|
397
425
|
|
|
426
|
+
/*
|
|
427
|
+
function attachElementAnnos( annotate, art ) {
|
|
428
|
+
while (art.items)
|
|
429
|
+
art = art.items;
|
|
430
|
+
if (art.elements) {
|
|
431
|
+
const elems = inferred( art.elements, art.$inferred );
|
|
432
|
+
if (Object.keys( elems ).length)
|
|
433
|
+
annotate.elements = elems;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function attachParamAnnos( annotate, art ) {
|
|
438
|
+
const inferredParent = art.$inferred;
|
|
439
|
+
if (art.params) {
|
|
440
|
+
const ext = Object.create( dictionaryPrototype );
|
|
441
|
+
for (const name in art.params) {
|
|
442
|
+
const par = art.params[name];
|
|
443
|
+
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
444
|
+
continue;
|
|
445
|
+
const render = annotationsAndDocComment( par, true );
|
|
446
|
+
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
447
|
+
if (subElems) {
|
|
448
|
+
const sub = inferred( subElems, par.$inferred );
|
|
449
|
+
if (Object.keys( sub ).length)
|
|
450
|
+
render.elements = sub;
|
|
451
|
+
}
|
|
452
|
+
if (Object.keys(render).length)
|
|
453
|
+
ext[name] = render;
|
|
454
|
+
}
|
|
455
|
+
if (obj.keys( ext ))
|
|
456
|
+
annotate.params = ext;
|
|
457
|
+
}
|
|
458
|
+
if (art.returns) {
|
|
459
|
+
const par = art.returns;
|
|
460
|
+
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
461
|
+
return;
|
|
462
|
+
const render = annotationsAndDocComment( par, true );
|
|
463
|
+
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
464
|
+
if (subElems) {
|
|
465
|
+
const sub = inferred( subElems, par.$inferred );
|
|
466
|
+
if (Object.keys( sub ).length)
|
|
467
|
+
render.elements = sub;
|
|
468
|
+
}
|
|
469
|
+
if (Object.keys(render).length)
|
|
470
|
+
const sub = inferred( subElems, par.$inferred );
|
|
471
|
+
if (Object.keys( sub ).length)
|
|
472
|
+
render.elements = sub;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return ext;
|
|
476
|
+
*/
|
|
477
|
+
|
|
398
478
|
// extract namespace/builtin annotations
|
|
399
479
|
function extractAnnotationsToExtension( art ) {
|
|
400
480
|
const name = art.name.absolute;
|
|
@@ -456,17 +536,29 @@ function sources( srcDict, csn ) {
|
|
|
456
536
|
}
|
|
457
537
|
}
|
|
458
538
|
|
|
459
|
-
function
|
|
460
|
-
const
|
|
461
|
-
for (const name in
|
|
462
|
-
const elem =
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
539
|
+
function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
|
|
540
|
+
const annoDict = Object.create( dictionaryPrototype );
|
|
541
|
+
for (const name in dict) {
|
|
542
|
+
const elem = dict[name];
|
|
543
|
+
const inf = inferred || elem.$inferred; // is probably always inferred if parent was
|
|
544
|
+
const sub = (inf) ? annotationsAndDocComment( elem, true ) : {};
|
|
545
|
+
if (elem.$expand === 'annotate') {
|
|
546
|
+
if (elem.params)
|
|
547
|
+
attachAnnotations( sub, 'params', elem.params, inf );
|
|
548
|
+
const obj = elem.returns || elem;
|
|
549
|
+
const elems = (obj.items || obj.targetAspect || obj).elements;
|
|
550
|
+
if (elems)
|
|
551
|
+
attachAnnotations( sub, 'elements', elems, inf, elem.returns );
|
|
552
|
+
}
|
|
553
|
+
if (Object.keys( sub ).length)
|
|
554
|
+
annoDict[name] = sub;
|
|
555
|
+
}
|
|
556
|
+
if (Object.keys( annoDict ).length) {
|
|
557
|
+
if (returns)
|
|
558
|
+
annotate.returns = { elements: annoDict };
|
|
559
|
+
else
|
|
560
|
+
annotate[prop] = annoDict;
|
|
468
561
|
}
|
|
469
|
-
return ext;
|
|
470
562
|
}
|
|
471
563
|
|
|
472
564
|
function standard( node ) {
|
|
@@ -505,10 +597,13 @@ function set( prop, csn, node ) {
|
|
|
505
597
|
}
|
|
506
598
|
|
|
507
599
|
function targetAspect( val, csn, node ) {
|
|
600
|
+
const ta = (val.elements)
|
|
601
|
+
? addLocation( val.location, standard( val ) )
|
|
602
|
+
: artifactRef( val, true );
|
|
508
603
|
if (!gensrcFlavor || node.target && !node.target.$inferred)
|
|
509
|
-
return
|
|
604
|
+
return ta;
|
|
510
605
|
// For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
|
|
511
|
-
csn.target =
|
|
606
|
+
csn.target = ta;
|
|
512
607
|
return undefined;
|
|
513
608
|
}
|
|
514
609
|
|
|
@@ -528,10 +623,8 @@ function target( val, _csn, node ) {
|
|
|
528
623
|
|
|
529
624
|
function items( obj, csn, node ) {
|
|
530
625
|
if (!keepElements( node ))
|
|
531
|
-
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
532
|
-
// hidden 'elements' will be set in query()
|
|
533
626
|
return undefined;
|
|
534
|
-
return standard( obj );
|
|
627
|
+
return standard( obj ); // no 'elements' with inferred elements with gensrc
|
|
535
628
|
}
|
|
536
629
|
|
|
537
630
|
function elements( dict, csn, node ) {
|
|
@@ -553,21 +646,24 @@ function enumerableQueryElements( select ) {
|
|
|
553
646
|
return alias.query && (alias.query._leadingQuery || alias.query) === select;
|
|
554
647
|
}
|
|
555
648
|
|
|
556
|
-
//
|
|
557
|
-
// way from the original structure type definition to the current usage
|
|
649
|
+
// Should we render the elements? (and items?)
|
|
558
650
|
function keepElements( node ) {
|
|
559
651
|
if (universalCsn)
|
|
560
|
-
//
|
|
561
|
-
//
|
|
652
|
+
// $expand = null/undefined: not elements not via expansion
|
|
653
|
+
// $expand = 'target'/'annotate': with redirections / individual annotations
|
|
562
654
|
return node.$expand !== 'origin';
|
|
563
655
|
if (!node.type || node.kind === 'type')
|
|
564
656
|
return true;
|
|
657
|
+
// even if expanded elements have no new target or direct annotation,
|
|
658
|
+
// they might have got one via propagation – any new target/annos during their
|
|
659
|
+
// way from the original structure type definition to the current usage
|
|
565
660
|
while (node) {
|
|
566
661
|
if (node.$expand !== 'origin')
|
|
567
662
|
return true;
|
|
568
663
|
node = node._origin;
|
|
569
664
|
}
|
|
570
|
-
|
|
665
|
+
// all in _origin chain only have expanded elements with 'origin':
|
|
666
|
+
return false; // no need to render elements
|
|
571
667
|
}
|
|
572
668
|
|
|
573
669
|
// for gensrcFlavor and namespace/builtin annotation extraction:
|
|
@@ -653,17 +749,17 @@ function sortedDict( dict ) {
|
|
|
653
749
|
return dictionary( dict, keys );
|
|
654
750
|
}
|
|
655
751
|
|
|
656
|
-
function
|
|
752
|
+
function actions( dict ) {
|
|
657
753
|
const keys = Object.keys( dict );
|
|
658
754
|
return (keys.length)
|
|
659
|
-
? dictionary( dict, keys )
|
|
755
|
+
? dictionary( dict, keys, 'actions' )
|
|
660
756
|
: undefined;
|
|
661
757
|
}
|
|
662
758
|
|
|
663
|
-
function dictionary( dict, keys ) {
|
|
759
|
+
function dictionary( dict, keys, prop ) {
|
|
664
760
|
const csn = Object.create( dictionaryPrototype );
|
|
665
761
|
for (const name of keys) {
|
|
666
|
-
const def = definition( dict[name] );
|
|
762
|
+
const def = definition( dict[name], null, null, prop );
|
|
667
763
|
if (def !== undefined)
|
|
668
764
|
csn[name] = def;
|
|
669
765
|
}
|
|
@@ -686,7 +782,7 @@ function foreignKeys( dict, csn, node ) {
|
|
|
686
782
|
csn.keys = keys;
|
|
687
783
|
}
|
|
688
784
|
|
|
689
|
-
function definition( art ) {
|
|
785
|
+
function definition( art, _csn, _node, prop ) {
|
|
690
786
|
if (!art || typeof art !== 'object')
|
|
691
787
|
return undefined; // TODO: complain with strict
|
|
692
788
|
// Do not include namespace definitions or inferred construct (in gensrc):
|
|
@@ -698,7 +794,14 @@ function definition( art ) {
|
|
|
698
794
|
addLocation( art.targetElement.location, key );
|
|
699
795
|
return extra( key, art );
|
|
700
796
|
}
|
|
701
|
-
|
|
797
|
+
const c = standard( art );
|
|
798
|
+
// The XSN of actions in extensions do not contain a returns yet - TODO?
|
|
799
|
+
const elems = c.elements;
|
|
800
|
+
if (elems && (prop === 'actions' || art.$syntax === 'returns')) {
|
|
801
|
+
delete c.elements;
|
|
802
|
+
c.returns = { elements: elems };
|
|
803
|
+
}
|
|
804
|
+
return c;
|
|
702
805
|
}
|
|
703
806
|
|
|
704
807
|
function addOrigin( csn, xsn ) {
|
|
@@ -865,10 +968,12 @@ function args( node ) {
|
|
|
865
968
|
return dict;
|
|
866
969
|
}
|
|
867
970
|
|
|
868
|
-
// "Short" value form, e.g. for annotation assignments
|
|
869
971
|
function value( node ) {
|
|
972
|
+
// "Short" value form, e.g. for annotation assignments
|
|
870
973
|
if (!node)
|
|
871
974
|
return true; // `@aBool` short for `@aBool: true`
|
|
975
|
+
if (universalCsn && node.$inferred === 'prop') // via propagator.js
|
|
976
|
+
return undefined;
|
|
872
977
|
if (node.$inferred && gensrcFlavor)
|
|
873
978
|
return undefined;
|
|
874
979
|
if (node.path) {
|
|
@@ -911,7 +1016,7 @@ function onCondition( cond, csn, node ) {
|
|
|
911
1016
|
function condition( node ) {
|
|
912
1017
|
const expr = expression( node );
|
|
913
1018
|
// we do not set a hidden $parens on array - we could still do it if requested
|
|
914
|
-
return !expr.cast && expr.xpr || [ expr ];
|
|
1019
|
+
return !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
915
1020
|
}
|
|
916
1021
|
|
|
917
1022
|
function expression( node, dollarExtra ) {
|
|
@@ -951,10 +1056,12 @@ function expression( node, dollarExtra ) {
|
|
|
951
1056
|
arg0.xpr.unshift( quantifier.val );
|
|
952
1057
|
}
|
|
953
1058
|
}
|
|
1059
|
+
if (node.suffix)
|
|
1060
|
+
call.xpr = [].concat( ...node.suffix.map( xprArg ) );
|
|
954
1061
|
return extra( call, dollarExtraNode );
|
|
955
1062
|
}
|
|
956
1063
|
if (node.query)
|
|
957
|
-
return query( node.query, null, null, 1 );
|
|
1064
|
+
return query( node.query, null, null, null, 1 );
|
|
958
1065
|
if (!node.op) // parse error
|
|
959
1066
|
return { xpr: [] };
|
|
960
1067
|
else if (node.op.val === 'xpr')
|
|
@@ -964,7 +1071,7 @@ function expression( node, dollarExtra ) {
|
|
|
964
1071
|
return cast( expression( node.args[0] ), dollarExtraNode );
|
|
965
1072
|
// from here on: CDL input (no $extra possible - but $parens)
|
|
966
1073
|
else if (node.op.val !== ',')
|
|
967
|
-
return extra( { xpr: xpr( node ) }, dollarExtraNode, 1 );
|
|
1074
|
+
return extra( { xpr: xpr( node ) }, dollarExtraNode, (dollarExtra === 'sub-xpr' ? 1 : 0) );
|
|
968
1075
|
return (parensAsStrings)
|
|
969
1076
|
? { xpr: [ '(', ...xpr( node ), ')' ] }
|
|
970
1077
|
// the inner parens belong to the tuple construct, i.e. won't count as parens
|
|
@@ -974,15 +1081,7 @@ function expression( node, dollarExtra ) {
|
|
|
974
1081
|
function xpr( node ) {
|
|
975
1082
|
// if (!node.op) console.log(node)
|
|
976
1083
|
const op = operators[node.op.val] || node.op.val.split(' ');
|
|
977
|
-
const exprs = node.args.map(
|
|
978
|
-
const expr = expression( sub );
|
|
979
|
-
// return !sub.$parens && !expr.cast && expr.xpr || [ expr ]; if parensAsStrings is gone
|
|
980
|
-
if (expr.cast || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
981
|
-
return [ expr ];
|
|
982
|
-
else if (sub.$parens && sub.op.val !== ',')
|
|
983
|
-
return [ '(', ...expr.xpr, ')' ];
|
|
984
|
-
return expr.xpr;
|
|
985
|
-
} );
|
|
1084
|
+
const exprs = node.args.map( xprArg );
|
|
986
1085
|
if (op instanceof Function)
|
|
987
1086
|
return op( exprs );
|
|
988
1087
|
if (node.quantifier)
|
|
@@ -992,6 +1091,27 @@ function xpr( node ) {
|
|
|
992
1091
|
return exprs[0].concat( ...exprs.slice(1).map( a => [ ...op, ...a ] ) );
|
|
993
1092
|
}
|
|
994
1093
|
|
|
1094
|
+
function xprArg( sub ) {
|
|
1095
|
+
const realXpr = sub.op && sub.op.val === 'xpr';
|
|
1096
|
+
const expr = expression( sub, 'sub-xpr' );
|
|
1097
|
+
// `sort`/`nulls` will be attached to arguments of orderBy
|
|
1098
|
+
// which might be either `path`s or `xpr`s
|
|
1099
|
+
const sortAndNulls = [];
|
|
1100
|
+
if (sub.sort)
|
|
1101
|
+
sortAndNulls.push( sub.sort.val );
|
|
1102
|
+
if (sub.nulls)
|
|
1103
|
+
sortAndNulls.push( ...[ 'nulls', sub.nulls.val ] );
|
|
1104
|
+
// return !sub.$parens && !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
1105
|
+
// if parensAsStrings is gone
|
|
1106
|
+
if (realXpr || expr.cast || expr.func || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
1107
|
+
return [ expr, ...sortAndNulls ];
|
|
1108
|
+
else if (sub.$parens && sub.op.val !== ',')
|
|
1109
|
+
return [ '(', ...expr.xpr, ')' ];
|
|
1110
|
+
|
|
1111
|
+
expr.xpr.push( ...sortAndNulls );
|
|
1112
|
+
return expr.xpr;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
995
1115
|
function ternary( op1, op2 ) {
|
|
996
1116
|
return function ternaryOp( exprs ) {
|
|
997
1117
|
return (exprs[2])
|
|
@@ -1015,7 +1135,7 @@ function binaryRightParen( op ) {
|
|
|
1015
1135
|
};
|
|
1016
1136
|
}
|
|
1017
1137
|
|
|
1018
|
-
function query( node, csn, xsn, expectedParens = 0 ) {
|
|
1138
|
+
function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
1019
1139
|
if (node.op.val === 'SELECT') {
|
|
1020
1140
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
1021
1141
|
node.from && node.from.path && !projectionAsQuery) {
|
|
@@ -1087,7 +1207,7 @@ function from( node ) {
|
|
|
1087
1207
|
return extra( join, node );
|
|
1088
1208
|
}
|
|
1089
1209
|
else if (node.query) {
|
|
1090
|
-
return addExplicitAs( query( node.query, null, null, 1 ), node.name );
|
|
1210
|
+
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1091
1211
|
}
|
|
1092
1212
|
else if (!node._artifact || node._artifact._main) { // CQL or follow assoc
|
|
1093
1213
|
return extra( addExplicitAs( artifactRef( node, false ), node.name ), node );
|
|
@@ -1132,12 +1252,6 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1132
1252
|
finally {
|
|
1133
1253
|
gensrcFlavor = gensrcSaved;
|
|
1134
1254
|
}
|
|
1135
|
-
// FIXME: Currently toHana requires that an '_ignore' property on the elem is
|
|
1136
|
-
// also visible on the column. Don't ignore virtual columns, let the
|
|
1137
|
-
// renderer decide how to render that column.
|
|
1138
|
-
if (!elem.virtual && elem._ignore)
|
|
1139
|
-
col._ignore = true;
|
|
1140
|
-
|
|
1141
1255
|
if (elem.value && !elem.$inferred) {
|
|
1142
1256
|
const parens = elem.value.$parens;
|
|
1143
1257
|
if (parens)
|
|
@@ -77,6 +77,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
77
77
|
noAssignmentInSameLine,
|
|
78
78
|
noSemicolonHere,
|
|
79
79
|
setLocalToken,
|
|
80
|
+
setLocalTokenIfBefore,
|
|
80
81
|
excludeExpected,
|
|
81
82
|
isStraightBefore,
|
|
82
83
|
meltKeywordToIdentifier,
|
|
@@ -183,6 +184,14 @@ function setLocalToken( string, tokenName, notBefore, inSameLine ) {
|
|
|
183
184
|
ll1.type = this.constructor[tokenName];
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
|
|
188
|
+
const ll1 = this.getCurrentToken();
|
|
189
|
+
if (ll1.text.toUpperCase() === string &&
|
|
190
|
+
(!inSameLine || this._input.LT(-1).line === ll1.line) &&
|
|
191
|
+
(!before || before && before.test( this._input.LT(2).text )))
|
|
192
|
+
ll1.type = this.constructor[tokenName];
|
|
193
|
+
}
|
|
194
|
+
|
|
186
195
|
// // Special function for rule `requiredSemi` before return $ctx
|
|
187
196
|
// function braceForSemi() {
|
|
188
197
|
// if (RBRACE == null)
|