@sap/cds-compiler 3.4.2 → 3.5.0
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 +80 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +15 -16
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +2 -2
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +61 -59
- package/lib/api/options.js +4 -2
- package/lib/api/validate.js +2 -2
- package/lib/base/cleanSymbols.js +2 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +6 -6
- package/lib/base/location.js +11 -12
- package/lib/base/message-registry.js +177 -58
- package/lib/base/messages.js +252 -180
- package/lib/base/model.js +14 -11
- package/lib/base/node-helpers.js +9 -10
- package/lib/base/optionProcessorHelper.js +138 -129
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +5 -5
- package/lib/checks/annotationsOData.js +4 -4
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +3 -3
- package/lib/checks/defaultValues.js +3 -3
- package/lib/checks/elements.js +7 -7
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +4 -4
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/managedWithoutKeys.js +1 -1
- package/lib/checks/nonexpandableStructured.js +5 -3
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +5 -6
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -2
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +4 -4
- package/lib/checks/types.js +7 -7
- package/lib/checks/utils.js +4 -4
- package/lib/checks/validator.js +16 -13
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/builtins.js +14 -14
- package/lib/compiler/checks.js +123 -48
- package/lib/compiler/define.js +12 -13
- package/lib/compiler/extend.js +266 -60
- package/lib/compiler/finalize-parse-cdl.js +10 -5
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +14 -6
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +27 -16
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +20 -0
- package/lib/edm/annotations/genericTranslation.js +604 -358
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +275 -222
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +6 -6
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +107 -77
- package/lib/edm/edmUtils.js +44 -5
- package/lib/gen/Dictionary.json +210 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +67 -63
- package/lib/gen/language.tokens +81 -81
- package/lib/gen/languageLexer.interp +4 -10
- package/lib/gen/languageLexer.js +854 -869
- package/lib/gen/languageLexer.tokens +79 -81
- package/lib/gen/languageParser.js +14309 -13832
- package/lib/inspect/inspectModelStatistics.js +2 -2
- package/lib/inspect/inspectPropagation.js +6 -6
- package/lib/inspect/inspectUtils.js +2 -2
- package/lib/json/from-csn.js +102 -55
- package/lib/json/to-csn.js +119 -198
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +113 -133
- package/lib/language/language.g4 +1550 -1506
- package/lib/language/multiLineStringParser.js +3 -3
- package/lib/language/textUtils.js +2 -2
- package/lib/main.js +3 -3
- package/lib/model/csnRefs.js +5 -0
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +100 -0
- package/lib/optionProcessor.js +5 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +311 -276
- package/lib/render/toHdbcds.js +97 -94
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +127 -223
- package/lib/render/utils/common.js +141 -108
- package/lib/render/utils/delta.js +227 -0
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/assertUnique.js +13 -12
- package/lib/transform/db/associations.js +5 -5
- package/lib/transform/db/cdsPersistence.js +10 -8
- package/lib/transform/db/constraints.js +14 -14
- package/lib/transform/db/expansion.js +20 -22
- package/lib/transform/db/flattening.js +24 -42
- package/lib/transform/db/groupByOrderBy.js +3 -3
- package/lib/transform/db/temporal.js +6 -6
- package/lib/transform/db/transformExists.js +23 -23
- package/lib/transform/db/views.js +16 -16
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/draft/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +8 -29
- package/lib/transform/forRelationalDB.js +16 -6
- package/lib/transform/localized.js +11 -10
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +113 -47
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +17 -10
- package/lib/transform/translateAssocsToJoins.js +24 -19
- package/lib/transform/universalCsn/coreComputed.js +10 -10
- package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
- package/lib/transform/universalCsn/utils.js +3 -3
- package/lib/utils/file.js +5 -5
- package/lib/utils/moduleResolve.js +13 -13
- package/lib/utils/objectUtils.js +6 -6
- package/lib/utils/term.js +5 -2
- package/lib/utils/timetrace.js +51 -24
- package/package.json +5 -8
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/redirected-to-complex.md +4 -4
- package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
- package/lib/modelCompare/filter.js +0 -83
package/lib/json/to-csn.js
CHANGED
|
@@ -23,15 +23,16 @@ const normalizedKind = {
|
|
|
23
23
|
param: 'param',
|
|
24
24
|
action: 'action',
|
|
25
25
|
function: 'action',
|
|
26
|
+
enum: 'enum',
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
/** @type {boolean|string} */
|
|
29
30
|
let gensrcFlavor = true; // good enough here...
|
|
30
31
|
let universalCsn = false;
|
|
31
32
|
let strictMode = false; // whether to dump with unknown properties (in standard)
|
|
32
|
-
let parensAsStrings = false;
|
|
33
33
|
let projectionAsQuery = false;
|
|
34
34
|
let withLocations = false;
|
|
35
|
+
let structXpr = false;
|
|
35
36
|
let dictionaryPrototype = null;
|
|
36
37
|
|
|
37
38
|
// Properties for dictionaries, set in compileX() and TODO: parseX(), must be
|
|
@@ -92,7 +93,7 @@ const transformers = {
|
|
|
92
93
|
where: condition, // also pathItem after 'cardinality' before 'args'
|
|
93
94
|
having: condition,
|
|
94
95
|
args, // also pathItem after 'where', before 'on'/'orderBy'
|
|
95
|
-
suffix:
|
|
96
|
+
suffix: ignore, // handled in exprInternal()
|
|
96
97
|
orderBy: arrayOf( orderBy ), // TODO XSN: make `sort` and `nulls` sibling properties
|
|
97
98
|
sort: value,
|
|
98
99
|
nulls: value,
|
|
@@ -190,41 +191,6 @@ const typeProperties = [
|
|
|
190
191
|
'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
|
|
191
192
|
];
|
|
192
193
|
|
|
193
|
-
const operators = {
|
|
194
|
-
// standard is: binary infix (and corresponding n-ary), unary prefix
|
|
195
|
-
isNot: [ 'is', 'not' ], // TODO XSN: 'is not'
|
|
196
|
-
isNull: postfix( [ 'is', 'null' ] ),
|
|
197
|
-
isNotNull: postfix( [ 'is', 'not', 'null' ] ),
|
|
198
|
-
in: binaryRightParen( [ 'in' ] ),
|
|
199
|
-
notIn: binaryRightParen( [ 'not', 'in' ] ),
|
|
200
|
-
between: ternary( [ 'between' ], [ 'and' ] ),
|
|
201
|
-
notBetween: ternary( [ 'not', 'between' ], [ 'and' ] ),
|
|
202
|
-
like: ternary( [ 'like' ], [ 'escape' ] ),
|
|
203
|
-
notLike: ternary( [ 'not', 'like' ], [ 'escape' ] ),
|
|
204
|
-
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
205
|
-
case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
|
|
206
|
-
over: exprs => [ 'over', { xpr: [].concat( ...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
|
|
211
|
-
'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
212
|
-
],
|
|
213
|
-
partitionBy: exprs => [
|
|
214
|
-
'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
215
|
-
],
|
|
216
|
-
rows: exprs => [
|
|
217
|
-
'rows', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
218
|
-
],
|
|
219
|
-
preceding: postfix( [ 'preceding' ] ),
|
|
220
|
-
unboundedPreceding: [ 'unbounded', 'preceding' ],
|
|
221
|
-
currentRow: [ 'current', 'row' ],
|
|
222
|
-
unboundedFollowing: [ 'unbounded', 'following' ],
|
|
223
|
-
following: postfix( [ 'following' ] ),
|
|
224
|
-
frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
|
|
225
|
-
ixpr: exprs => [].concat( ...exprs ), // xpr extra, due to extra parentheses
|
|
226
|
-
};
|
|
227
|
-
|
|
228
194
|
const csnDictionaries = [
|
|
229
195
|
'args', 'params', 'enum', 'mixin', 'elements', 'actions', 'definitions',
|
|
230
196
|
];
|
|
@@ -280,7 +246,7 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
280
246
|
return r;
|
|
281
247
|
}
|
|
282
248
|
|
|
283
|
-
function cloneAnnotationValue(val) {
|
|
249
|
+
function cloneAnnotationValue( val ) {
|
|
284
250
|
if (typeof val !== 'object') // scalar
|
|
285
251
|
return val;
|
|
286
252
|
return JSON.parse( JSON.stringify( val ) );
|
|
@@ -295,7 +261,7 @@ function cloneAnnotationValue(val) {
|
|
|
295
261
|
* @param {string} property
|
|
296
262
|
* @returns
|
|
297
263
|
*/
|
|
298
|
-
function hasNonEnumerable(object, property) {
|
|
264
|
+
function hasNonEnumerable( object, property ) {
|
|
299
265
|
return {}.hasOwnProperty.call( object, property ) &&
|
|
300
266
|
!{}.propertyIsEnumerable.call( object, property );
|
|
301
267
|
}
|
|
@@ -345,7 +311,7 @@ function compactModel( model, options = model.options || {} ) {
|
|
|
345
311
|
for (const first in srcDict) {
|
|
346
312
|
const { namespace } = srcDict[first];
|
|
347
313
|
if (namespace && namespace.path)
|
|
348
|
-
csn.namespace = namespace.path
|
|
314
|
+
csn.namespace = pathName( namespace.path );
|
|
349
315
|
break;
|
|
350
316
|
}
|
|
351
317
|
set( 'definitions', csn, model );
|
|
@@ -429,21 +395,10 @@ function extensions( node, csn, model ) {
|
|
|
429
395
|
}
|
|
430
396
|
else if (gensrcFlavor) {
|
|
431
397
|
// From definitions (without redefinitions) with potential inferred elements:
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (art.actions)
|
|
437
|
-
attachAnnotations( annotate, 'actions', art.actions, art.$inferred );
|
|
438
|
-
else if (art.params)
|
|
439
|
-
attachAnnotations( annotate, 'params', art.params, art.$inferred );
|
|
440
|
-
const obj = art.returns || art;
|
|
441
|
-
const elems = (obj.items || obj).elements; // no targetAspect here
|
|
442
|
-
if (elems)
|
|
443
|
-
attachAnnotations( annotate, 'elements', elems, art.$inferred, art.returns );
|
|
444
|
-
}
|
|
445
|
-
if (Object.keys( annotate ).length > 1)
|
|
446
|
-
exts.push( annotate );
|
|
398
|
+
const result = { annotate: Object.create(null) };
|
|
399
|
+
attachAnnotations(result, 'annotate', { [name]: art }, art.$inferred );
|
|
400
|
+
if (result.annotate[name])
|
|
401
|
+
exts.push({ annotate: name, ...result.annotate[name] } );
|
|
447
402
|
}
|
|
448
403
|
}
|
|
449
404
|
|
|
@@ -470,7 +425,7 @@ function extensions( node, csn, model ) {
|
|
|
470
425
|
const par = art.params[name];
|
|
471
426
|
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
472
427
|
continue;
|
|
473
|
-
const render = annotationsAndDocComment( par
|
|
428
|
+
const render = annotationsAndDocComment( par );
|
|
474
429
|
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
475
430
|
if (subElems) {
|
|
476
431
|
const sub = inferred( subElems, par.$inferred );
|
|
@@ -487,7 +442,7 @@ function extensions( node, csn, model ) {
|
|
|
487
442
|
const par = art.returns;
|
|
488
443
|
if (!inferredParent && !par.$inferred && par.$expand !== 'annotate')
|
|
489
444
|
return;
|
|
490
|
-
const render = annotationsAndDocComment( par
|
|
445
|
+
const render = annotationsAndDocComment( par );
|
|
491
446
|
const subElems = par.$expand !== 'origin' && (par.items || par).elements;
|
|
492
447
|
if (subElems) {
|
|
493
448
|
const sub = inferred( subElems, par.$inferred );
|
|
@@ -508,7 +463,7 @@ function extensions( node, csn, model ) {
|
|
|
508
463
|
const name = art.name.absolute;
|
|
509
464
|
// 'true' because annotations on namespaces and builtins can only
|
|
510
465
|
// happen through extensions.
|
|
511
|
-
const annos = annotationsAndDocComment( art
|
|
466
|
+
const annos = annotationsAndDocComment( art );
|
|
512
467
|
const annotate = Object.assign( { annotate: name }, annos );
|
|
513
468
|
if (Object.keys( annotate ).length > 1) {
|
|
514
469
|
const loc = locationForAnnotationExtension();
|
|
@@ -567,16 +522,21 @@ function sources( srcDict, csn ) {
|
|
|
567
522
|
function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
|
|
568
523
|
const annoDict = Object.create( dictionaryPrototype );
|
|
569
524
|
for (const name in dict) {
|
|
570
|
-
const
|
|
571
|
-
const inf = inferred ||
|
|
572
|
-
const sub = (inf) ? annotationsAndDocComment(
|
|
573
|
-
if (
|
|
574
|
-
if (
|
|
575
|
-
attachAnnotations( sub, '
|
|
576
|
-
|
|
577
|
-
|
|
525
|
+
const entry = dict[name];
|
|
526
|
+
const inf = inferred || entry.$inferred; // is probably always inferred if parent was
|
|
527
|
+
const sub = (inf) ? annotationsAndDocComment( entry ) : {};
|
|
528
|
+
if (entry.$expand === 'annotate') {
|
|
529
|
+
if (entry.actions)
|
|
530
|
+
attachAnnotations( sub, 'actions', entry.actions, inf );
|
|
531
|
+
else if (entry.params)
|
|
532
|
+
attachAnnotations( sub, 'params', entry.params, inf );
|
|
533
|
+
const obj = entry.returns || entry;
|
|
534
|
+
const many = obj.items || obj;
|
|
535
|
+
const elems = (many.targetAspect || many).elements;
|
|
578
536
|
if (elems)
|
|
579
|
-
attachAnnotations( sub, 'elements', elems, inf,
|
|
537
|
+
attachAnnotations( sub, 'elements', elems, inf, entry.returns );
|
|
538
|
+
if (many.enum)
|
|
539
|
+
attachAnnotations( sub, 'enum', many.enum, inf );
|
|
580
540
|
}
|
|
581
541
|
if (Object.keys( sub ).length)
|
|
582
542
|
annoDict[name] = sub;
|
|
@@ -723,10 +683,14 @@ function keepElements( node, line ) {
|
|
|
723
683
|
return false; // no need to render elements
|
|
724
684
|
}
|
|
725
685
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
686
|
+
/**
|
|
687
|
+
* For gensrcFlavor and namespace/builtin annotation extraction:
|
|
688
|
+
* return annotations from definition and annotations.
|
|
689
|
+
* The call side should check that node.$inferred is truthy.
|
|
690
|
+
*
|
|
691
|
+
* @param {object} node
|
|
692
|
+
*/
|
|
693
|
+
function annotationsAndDocComment( node ) {
|
|
730
694
|
const csn = {};
|
|
731
695
|
const transformer = transformers['@'];
|
|
732
696
|
const keys = Object.keys( node ).filter( a => a.charAt(0) === '@' ).sort();
|
|
@@ -734,18 +698,18 @@ function annotationsAndDocComment( node, annotated ) {
|
|
|
734
698
|
const val = node[prop];
|
|
735
699
|
// val.$priority isn't set for computed annotations like @Core.Computed
|
|
736
700
|
// and @odata.containment.ignore
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
701
|
+
// transformer (= value) takes care to exclude $inferred annotation assignments
|
|
702
|
+
const sub = transformer( val );
|
|
703
|
+
// As value() just has one value, so we do not provide ( val, csn, node, prop )
|
|
704
|
+
// which would be more robust, but makes some JS checks unhappy
|
|
705
|
+
if (sub !== undefined)
|
|
706
|
+
csn[prop] = sub;
|
|
707
|
+
}
|
|
708
|
+
if (node.doc) {
|
|
709
|
+
const doc = transformers.doc(node.doc);
|
|
710
|
+
if (doc !== undefined)
|
|
711
|
+
csn.doc = doc;
|
|
746
712
|
}
|
|
747
|
-
if (node.doc)
|
|
748
|
-
csn.doc = transformers.doc(node.doc);
|
|
749
713
|
return csn;
|
|
750
714
|
}
|
|
751
715
|
|
|
@@ -833,21 +797,18 @@ function dictionary( dict, keys, prop ) {
|
|
|
833
797
|
}
|
|
834
798
|
|
|
835
799
|
function foreignKeys( dict, csn, node ) {
|
|
836
|
-
if (universalCsn)
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
for (const n in dict) {
|
|
844
|
-
const d = definition( dict[n] );
|
|
845
|
-
if (d !== undefined)
|
|
846
|
-
keys.push( d );
|
|
847
|
-
else
|
|
800
|
+
if (universalCsn && dict[$inferred] === 'keys' || !target( node.target, csn, node ) )
|
|
801
|
+
return;
|
|
802
|
+
|
|
803
|
+
if (gensrcFlavor) {
|
|
804
|
+
if (node._origin?.$inferred === 'REDIRECTED')
|
|
805
|
+
dict = node._origin.foreignKeys;
|
|
806
|
+
if (dict[$inferred])
|
|
848
807
|
return;
|
|
849
808
|
}
|
|
850
|
-
csn.keys =
|
|
809
|
+
csn.keys = [];
|
|
810
|
+
for (const n in dict)
|
|
811
|
+
csn.keys.push( definition( dict[n] ) );
|
|
851
812
|
}
|
|
852
813
|
|
|
853
814
|
function returns( art, csn, _node, prop ) {
|
|
@@ -1235,7 +1196,7 @@ function renderArtifactPath( node, path, terse, scope ) {
|
|
|
1235
1196
|
const name = item._artifact && item._artifact.name;
|
|
1236
1197
|
// In localization views, the _artifact link of `item` is important
|
|
1237
1198
|
const id = name && name.absolute ||
|
|
1238
|
-
path.slice( 0, scope )
|
|
1199
|
+
pathName( path.slice( 0, scope ) );
|
|
1239
1200
|
path = [ Object.assign( {}, item, { id } ), ...path.slice( scope ) ];
|
|
1240
1201
|
}
|
|
1241
1202
|
const ref = path.map( pathItem );
|
|
@@ -1259,7 +1220,7 @@ function args( node ) {
|
|
|
1259
1220
|
return node.map( expression );
|
|
1260
1221
|
const dict = Object.create( dictionaryPrototype );
|
|
1261
1222
|
for (const param in node)
|
|
1262
|
-
dict[param] = expression( node[param]
|
|
1223
|
+
dict[param] = expression( node[param] );
|
|
1263
1224
|
return dict;
|
|
1264
1225
|
}
|
|
1265
1226
|
|
|
@@ -1302,7 +1263,7 @@ function enumValue( v, csn, node ) {
|
|
|
1302
1263
|
return;
|
|
1303
1264
|
// (with gensrc, the symbol itself would not make it into the CSN)
|
|
1304
1265
|
if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
|
|
1305
|
-
Object.assign( csn, expression( v
|
|
1266
|
+
Object.assign( csn, expression( v ) );
|
|
1306
1267
|
}
|
|
1307
1268
|
|
|
1308
1269
|
|
|
@@ -1317,128 +1278,89 @@ function onCondition( cond, csn, node ) {
|
|
|
1317
1278
|
}
|
|
1318
1279
|
|
|
1319
1280
|
function condition( node ) {
|
|
1320
|
-
const expr =
|
|
1321
|
-
|
|
1322
|
-
|
|
1281
|
+
const expr = exprInternal( node, 'no' );
|
|
1282
|
+
return (Array.isArray( expr ))
|
|
1283
|
+
? flattenenInternalXpr( expr )
|
|
1284
|
+
: !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
function expression( node ) {
|
|
1288
|
+
const expr = exprInternal( node, 'no' );
|
|
1289
|
+
return (Array.isArray( expr ))
|
|
1290
|
+
? { xpr: flattenenInternalXpr( expr ) }
|
|
1291
|
+
: expr;
|
|
1323
1292
|
}
|
|
1324
1293
|
|
|
1325
|
-
function
|
|
1326
|
-
const dollarExtraNode = dollarExtra !== 'ignoreExtra' && node;
|
|
1294
|
+
function exprInternal( node, xprParens ) {
|
|
1327
1295
|
if (typeof node === 'string')
|
|
1328
1296
|
return node;
|
|
1329
1297
|
if (!node) // make to-csn robst
|
|
1330
1298
|
return {};
|
|
1331
1299
|
if (node.scope === 'param') {
|
|
1332
1300
|
if (node.path)
|
|
1333
|
-
return extra( { ref: node.path.map( pathItem ), param: true },
|
|
1301
|
+
return extra( { ref: node.path.map( pathItem ), param: true }, node );
|
|
1334
1302
|
return { ref: [ node.param.val ], param: true }; // CDL rule for runtimes
|
|
1335
1303
|
}
|
|
1336
1304
|
if (node.path) {
|
|
1337
1305
|
const ref = node.path.map( pathItem );
|
|
1338
|
-
if (node.path.$prefix)
|
|
1306
|
+
if (node.path.$prefix) // auto-corrected ORDER BY refs without table alias
|
|
1339
1307
|
ref.unshift( node.path.$prefix );
|
|
1340
1308
|
// we would need to consider node.global here if we introduce that
|
|
1341
|
-
return extra( { ref },
|
|
1309
|
+
return extra( { ref }, node );
|
|
1342
1310
|
}
|
|
1343
1311
|
if (node.literal) {
|
|
1344
1312
|
if (typeof node.val === node.literal || node.val === null)
|
|
1345
|
-
return extra( { val: node.val },
|
|
1313
|
+
return extra( { val: node.val }, node );
|
|
1346
1314
|
else if (node.literal === 'enum')
|
|
1347
|
-
return extra( { '#': node.sym.id },
|
|
1315
|
+
return extra( { '#': node.sym.id }, node );
|
|
1348
1316
|
else if (node.literal === 'token')
|
|
1349
1317
|
return node.val; // * in COUNT(*)
|
|
1350
|
-
return extra( { val: node.val, literal: node.literal },
|
|
1318
|
+
return extra( { val: node.val, literal: node.literal }, node );
|
|
1351
1319
|
}
|
|
1352
1320
|
if (node.func) { // TODO XSN: remove op: 'call', func is no path
|
|
1353
1321
|
const call = { func: node.func.path[0].id };
|
|
1354
|
-
if (node.args)
|
|
1322
|
+
if (node.args) // no args from CSN input for CURRENT_DATE etc
|
|
1355
1323
|
call.args = args( node.args );
|
|
1356
|
-
const arg0 = call.args[0];
|
|
1357
|
-
const { quantifier } = node.func.path[0];
|
|
1358
|
-
if (arg0 && quantifier) {
|
|
1359
|
-
if (typeof arg0 !== 'object' || !arg0.xpr)
|
|
1360
|
-
call.args[0] = { xpr: [ quantifier.val, arg0 ] };
|
|
1361
|
-
else
|
|
1362
|
-
arg0.xpr.unshift( quantifier.val );
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
1324
|
if (node.suffix)
|
|
1366
|
-
call.xpr =
|
|
1367
|
-
|
|
1325
|
+
call.xpr = condition( { op: { val: 'ixpr' }, args: node.suffix } );
|
|
1326
|
+
// remark: node.suffix.map( expression ) would add $parens: 1 for xpr after "over"
|
|
1327
|
+
return extra( call, node );
|
|
1368
1328
|
}
|
|
1369
1329
|
if (node.query)
|
|
1370
1330
|
return query( node.query, null, null, null, 1 );
|
|
1371
1331
|
if (!node.op) // parse error
|
|
1372
1332
|
return { xpr: [] };
|
|
1373
|
-
else if (node.op.val === 'xpr')
|
|
1374
|
-
// do not use xpr() for xpr, as it would flatten inner xpr's
|
|
1375
|
-
return extra({ xpr: node.args.map( expression ) }, dollarExtraNode, 1 );
|
|
1376
|
-
else if (node.op.val === 'cast')
|
|
1377
|
-
return cast( expression( node.args[0] ), dollarExtraNode );
|
|
1378
|
-
// from here on: CDL input (no $extra possible - but $parens)
|
|
1379
|
-
else if (node.op.val !== ',')
|
|
1380
|
-
return extra( { xpr: xpr( node ) }, dollarExtraNode, (dollarExtra === 'sub-xpr' ? 1 : 0) );
|
|
1381
|
-
return (parensAsStrings)
|
|
1382
|
-
? { xpr: [ '(', ...xpr( node ), ')' ] }
|
|
1383
|
-
// the inner parens belong to the tuple construct, i.e. won't count as parens
|
|
1384
|
-
: extra( { list: node.args.map( expression ) }, dollarExtraNode, 0 );
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
function xpr( node ) {
|
|
1388
|
-
// if (!node.op) console.log(node)
|
|
1389
|
-
const op = operators[node.op.val] || node.op.val.split(' ');
|
|
1390
|
-
const exprs = node.args.map( xprArg );
|
|
1391
|
-
if (op instanceof Function)
|
|
1392
|
-
return op( exprs );
|
|
1393
|
-
if (node.quantifier)
|
|
1394
|
-
op.push( node.quantifier.val );
|
|
1395
|
-
if (exprs.length < 2)
|
|
1396
|
-
return [ ...op, ...exprs[0] || [] ];
|
|
1397
|
-
return exprs[0].concat( ...exprs.slice(1).map( a => [ ...op, ...a ] ) );
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
function xprArg( sub ) {
|
|
1401
|
-
const realXpr = sub.op && sub.op.val === 'xpr';
|
|
1402
|
-
const expr = expression( sub, 'sub-xpr' );
|
|
1403
|
-
// `sort`/`nulls` will be attached to arguments of orderBy
|
|
1404
|
-
// which might be either `path`s or `xpr`s
|
|
1405
|
-
const sortAndNulls = [];
|
|
1406
|
-
if (sub.sort)
|
|
1407
|
-
sortAndNulls.push( sub.sort.val );
|
|
1408
|
-
if (sub.nulls)
|
|
1409
|
-
sortAndNulls.push( ...[ 'nulls', sub.nulls.val ] );
|
|
1410
|
-
// return !sub.$parens && !expr.cast && !expr.func && expr.xpr || [ expr ];
|
|
1411
|
-
// if parensAsStrings is gone
|
|
1412
|
-
if (realXpr || expr.cast || expr.func || !expr.xpr || sub.$parens && !parensAsStrings)
|
|
1413
|
-
return [ expr, ...sortAndNulls ];
|
|
1414
|
-
else if (sub.$parens && sub.op.val !== ',')
|
|
1415
|
-
return [ '(', ...expr.xpr, ')' ];
|
|
1416
|
-
|
|
1417
|
-
expr.xpr.push( ...sortAndNulls );
|
|
1418
|
-
return expr.xpr;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
function ternary( op1, op2 ) {
|
|
1422
|
-
return function ternaryOp( exprs ) {
|
|
1423
|
-
return (exprs[2])
|
|
1424
|
-
? [ ...exprs[0], ...op1, ...exprs[1], ...op2, ...exprs[2] ]
|
|
1425
|
-
: [ ...exprs[0], ...op1, ...exprs[1] ];
|
|
1426
|
-
};
|
|
1427
|
-
}
|
|
1428
1333
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1334
|
+
const { val } = node.op;
|
|
1335
|
+
switch (val) {
|
|
1336
|
+
case 'ixpr':
|
|
1337
|
+
case 'xpr':
|
|
1338
|
+
break;
|
|
1339
|
+
case 'cast':
|
|
1340
|
+
return cast( expression( node.args[0] ), node );
|
|
1341
|
+
case 'list':
|
|
1342
|
+
return extra( { list: node.args.map( expression ) }, node, 0 );
|
|
1343
|
+
default: { // '=', 'and', CSN v0 input: binary (n-ary) and unary prefix
|
|
1344
|
+
if (!node.args.length)
|
|
1345
|
+
return { xpr: [] };
|
|
1346
|
+
const nary = [];
|
|
1347
|
+
for (const item of node.args)
|
|
1348
|
+
nary.push( { val, literal: 'token' }, item );
|
|
1349
|
+
node = {
|
|
1350
|
+
op: { val: 'ixpr' },
|
|
1351
|
+
args: (nary.length > 2 ? nary.slice(1) : nary),
|
|
1352
|
+
$parens: node.$parens,
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
const rargs = node.args.map( exprInternal );
|
|
1357
|
+
if (val === 'xpr' || node.$parens)
|
|
1358
|
+
return extra( { xpr: flattenenInternalXpr( rargs ) }, node, (xprParens === 'no' ? 0 : 1) );
|
|
1359
|
+
return rargs.length === 1 ? rargs[0] : rargs;
|
|
1433
1360
|
}
|
|
1434
1361
|
|
|
1435
|
-
function
|
|
1436
|
-
return (
|
|
1437
|
-
const right = exprs[1].length === 1 ? exprs[1][0] : {};
|
|
1438
|
-
return (right.xpr || right.list || !right.$parens)
|
|
1439
|
-
? [ ...exprs[0], ...op, ...exprs[1] ]
|
|
1440
|
-
: [ ...exprs[0], ...op, { xpr: exprs[1] } ];
|
|
1441
|
-
};
|
|
1362
|
+
function flattenenInternalXpr( array ) {
|
|
1363
|
+
return (structXpr) ? array : array.flat( Infinity );
|
|
1442
1364
|
}
|
|
1443
1365
|
|
|
1444
1366
|
function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
@@ -1534,7 +1456,7 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1534
1456
|
if (elem.$inferred === '*')
|
|
1535
1457
|
return;
|
|
1536
1458
|
// only list annotations here which are provided directly with definition
|
|
1537
|
-
const col = (gensrcFlavor) ? annotationsAndDocComment( elem
|
|
1459
|
+
const col = (gensrcFlavor) ? annotationsAndDocComment( elem ) : {};
|
|
1538
1460
|
// with `client` flavor, assignments are available at the element
|
|
1539
1461
|
const gensrcSaved = gensrcFlavor;
|
|
1540
1462
|
|
|
@@ -1542,7 +1464,7 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1542
1464
|
gensrcFlavor = gensrcFlavor || 'column';
|
|
1543
1465
|
set( 'virtual', col, elem );
|
|
1544
1466
|
set( 'key', col, elem );
|
|
1545
|
-
const expr = expression( elem.value
|
|
1467
|
+
const expr = expression( elem.value );
|
|
1546
1468
|
Object.assign( col, (expr.cast ? { xpr: [ expr ] } : expr) );
|
|
1547
1469
|
gensrcFlavor = gensrcSaved; // for not having annotations in inline etc
|
|
1548
1470
|
if (elem.expand)
|
|
@@ -1578,7 +1500,7 @@ function orderBy( node ) {
|
|
|
1578
1500
|
expr.sort = node.sort.val;
|
|
1579
1501
|
if (node.nulls)
|
|
1580
1502
|
expr.nulls = node.nulls.val;
|
|
1581
|
-
return
|
|
1503
|
+
return expr; // extra properties are before sort/nulls - who cares?
|
|
1582
1504
|
}
|
|
1583
1505
|
|
|
1584
1506
|
function extra( csn, node, expectedParens = 0 ) {
|
|
@@ -1643,19 +1565,18 @@ function compactQuery( q ) { // TODO: options
|
|
|
1643
1565
|
|
|
1644
1566
|
function compactExpr( e ) { // TODO: options
|
|
1645
1567
|
initModuleVars();
|
|
1646
|
-
return e && expression( e
|
|
1568
|
+
return e && expression( e );
|
|
1647
1569
|
}
|
|
1648
1570
|
|
|
1649
1571
|
/**
|
|
1650
1572
|
* @param {CSN.Options} options
|
|
1651
1573
|
*/
|
|
1652
1574
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
universalCsn =
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
!options.parseCdl;
|
|
1575
|
+
const flavor = options.csnFlavor || options.toCsn?.flavor;
|
|
1576
|
+
gensrcFlavor = options.parseCdl || flavor === 'gensrc';
|
|
1577
|
+
universalCsn = flavor === 'universal' &&
|
|
1578
|
+
isBetaEnabled( options, 'enableUniversalCsn' ) &&
|
|
1579
|
+
!options.parseCdl;
|
|
1659
1580
|
strictMode = options.testMode;
|
|
1660
1581
|
const proto = options.dictionaryPrototype;
|
|
1661
1582
|
// eslint-disable-next-line no-nested-ternary
|
|
@@ -1663,7 +1584,7 @@ function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
|
1663
1584
|
? proto
|
|
1664
1585
|
: (proto) ? Object.prototype : null;
|
|
1665
1586
|
withLocations = options.withLocations;
|
|
1666
|
-
|
|
1587
|
+
structXpr = options.structXpr;
|
|
1667
1588
|
projectionAsQuery = isDeprecatedEnabled( options, '_projectionAsQuery' );
|
|
1668
1589
|
}
|
|
1669
1590
|
|
|
@@ -164,8 +164,11 @@ function parse( source, filename = '<undefined>.cds',
|
|
|
164
164
|
if (options.docComment !== false) {
|
|
165
165
|
for (const token of tokenStream.tokens) {
|
|
166
166
|
if (token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
// TODO: think of 'syntax-unexpected-doc-comment'
|
|
168
|
+
messageFunctions.info( 'syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
|
|
169
|
+
'Ignoring doc comment as it is not written at a defined position' );
|
|
170
|
+
// this is also for position inside some artifact definition, i.e. previous text
|
|
171
|
+
// "does not belong to any artifact" might be confusing
|
|
169
172
|
}
|
|
170
173
|
}
|
|
171
174
|
}
|
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
* @returns {string|null} Parsed contents or if the comment has an invalid format or
|
|
17
17
|
* does not have any content, null is returned.
|
|
18
18
|
*/
|
|
19
|
-
function parseDocComment(comment) {
|
|
19
|
+
function parseDocComment( comment ) {
|
|
20
20
|
// Also return "null" for empty doc comments so that doc comment propagation
|
|
21
21
|
// can be stopped.
|
|
22
22
|
if (comment.length <= 5) // at least "/***/"
|
|
@@ -83,7 +83,7 @@ function parseDocComment(comment) {
|
|
|
83
83
|
* @param {string[]} lines String split into lines.
|
|
84
84
|
* @param {boolean} ignoreFirstLine Whether to ignore the first line for indentation counting.
|
|
85
85
|
*/
|
|
86
|
-
function stripCommentIndentation(lines, ignoreFirstLine) {
|
|
86
|
+
function stripCommentIndentation( lines, ignoreFirstLine ) {
|
|
87
87
|
const n = lines.length;
|
|
88
88
|
|
|
89
89
|
const minIndent = lines.reduce((min, line, index) => {
|
|
@@ -114,7 +114,7 @@ function stripCommentIndentation(lines, ignoreFirstLine) {
|
|
|
114
114
|
* @param {string} line
|
|
115
115
|
* @returns {string} line without fence
|
|
116
116
|
*/
|
|
117
|
-
function removeFence(line) {
|
|
117
|
+
function removeFence( line ) {
|
|
118
118
|
return line.replace(/^\s*[*]\s?/, '');
|
|
119
119
|
}
|
|
120
120
|
|
|
@@ -125,7 +125,7 @@ function removeFence(line) {
|
|
|
125
125
|
* @param {string} line
|
|
126
126
|
* @returns {string} Header without fence.
|
|
127
127
|
*/
|
|
128
|
-
function removeHeaderFence(line) {
|
|
128
|
+
function removeHeaderFence( line ) {
|
|
129
129
|
return line.replace(/^\/[*]{2,}\s?/, '');
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -138,7 +138,7 @@ function removeHeaderFence(line) {
|
|
|
138
138
|
* @param {string} line
|
|
139
139
|
* @returns {string} header without fence
|
|
140
140
|
*/
|
|
141
|
-
function removeFooterFence(line) {
|
|
141
|
+
function removeFooterFence( line ) {
|
|
142
142
|
return line.replace(/\s*[*]+\/$/, '');
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -148,7 +148,7 @@ function removeFooterFence(line) {
|
|
|
148
148
|
*
|
|
149
149
|
* @param {string[]} lines
|
|
150
150
|
*/
|
|
151
|
-
function isFencedComment(lines) {
|
|
151
|
+
function isFencedComment( lines ) {
|
|
152
152
|
const index = lines.findIndex((line, i) => {
|
|
153
153
|
const exclude = (i === 0 || i === lines.length - 1);
|
|
154
154
|
return !exclude && !(/^\s*[*]/.test(line));
|