@sap/cds-compiler 4.2.4 → 4.3.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 +26 -0
- package/bin/cdsc.js +8 -0
- package/bin/cdshi.js +3 -3
- package/doc/CHANGELOG_BETA.md +7 -0
- package/lib/api/main.js +19 -0
- package/lib/base/location.js +16 -0
- package/lib/base/message-registry.js +47 -16
- package/lib/base/messages.js +49 -38
- package/lib/base/model.js +1 -1
- package/lib/checks/checkPathsInStoredCalcElement.js +83 -0
- package/lib/checks/existsExpressionsOnlyForeignKeys.js +71 -0
- package/lib/checks/existsMustEndInAssoc.js +27 -0
- package/lib/checks/onConditions.js +47 -1
- package/lib/checks/validator.js +10 -1
- package/lib/compiler/assert-consistency.js +23 -15
- package/lib/compiler/base.js +31 -14
- package/lib/compiler/builtins.js +21 -20
- package/lib/compiler/checks.js +36 -49
- package/lib/compiler/define.js +71 -91
- package/lib/compiler/extend.js +27 -25
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +67 -87
- package/lib/compiler/kick-start.js +7 -5
- package/lib/compiler/populate.js +32 -30
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +29 -25
- package/lib/compiler/shared.js +57 -31
- package/lib/compiler/tweak-assocs.js +203 -22
- package/lib/compiler/utils.js +0 -18
- package/lib/gen/Dictionary.json +10 -4
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/languageParser.js +3 -3
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +63 -28
- package/lib/json/to-csn.js +23 -13
- package/lib/language/antlrParser.js +1 -1
- package/lib/language/errorStrategy.js +5 -1
- package/lib/language/genericAntlrParser.js +67 -61
- package/lib/main.d.ts +26 -1
- package/lib/main.js +2 -1
- package/lib/model/csnRefs.js +1 -0
- package/lib/model/csnUtils.js +28 -0
- package/lib/model/revealInternalProperties.js +3 -9
- package/lib/optionProcessor.js +17 -1
- package/lib/render/toCdl.js +1 -1
- package/lib/transform/db/associations.js +3 -4
- package/lib/transform/db/backlinks.js +293 -0
- package/lib/transform/db/expansion.js +9 -7
- package/lib/transform/db/flattening.js +3 -2
- package/lib/transform/db/rewriteCalculatedElements.js +1 -67
- package/lib/transform/db/transformExists.js +3 -58
- package/lib/transform/db/views.js +8 -14
- package/lib/transform/effective/.eslintrc.json +4 -0
- package/lib/transform/effective/associations.js +101 -0
- package/lib/transform/effective/main.js +88 -0
- package/lib/transform/effective/misc.js +61 -0
- package/lib/transform/effective/queries.js +42 -0
- package/lib/transform/effective/types.js +121 -0
- package/lib/transform/forRelationalDB.js +12 -235
- package/lib/transform/localized.js +22 -3
- package/lib/transform/parseExpr.js +7 -3
- package/lib/transform/transformUtils.js +5 -22
- package/lib/transform/translateAssocsToJoins.js +42 -38
- package/lib/transform/universalCsn/universalCsnEnricher.js +17 -1
- package/package.json +1 -2
- package/lib/language/language.g4 +0 -3260
package/lib/json/to-csn.js
CHANGED
|
@@ -1036,7 +1036,7 @@ function originRef( art, user ) {
|
|
|
1036
1036
|
while (isMember( parent ) && parent.kind !== 'select') {
|
|
1037
1037
|
const nkind = normalizedKind[parent.kind];
|
|
1038
1038
|
const name = parent.name || parent._outer.name;
|
|
1039
|
-
if (name.id || !r.length)
|
|
1039
|
+
if (name.id && parent.kind !== '$inline' || !r.length)
|
|
1040
1040
|
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
1041
1041
|
// eslint-disable-next-line no-nested-ternary, max-len
|
|
1042
1042
|
r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
|
|
@@ -1047,17 +1047,17 @@ function originRef( art, user ) {
|
|
|
1047
1047
|
return false; // do not write, probably use $origin: {...}
|
|
1048
1048
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
1049
1049
|
|
|
1050
|
-
r.push( parent.name.
|
|
1050
|
+
r.push( (parent._main || parent).name.id );
|
|
1051
1051
|
r.reverse();
|
|
1052
1052
|
return r;
|
|
1053
1053
|
}
|
|
1054
1054
|
|
|
1055
1055
|
function kind( k, csn, node ) {
|
|
1056
1056
|
if (k === 'annotate' || k === 'extend') {
|
|
1057
|
-
// We just use `name.
|
|
1057
|
+
// We just use `name.id` because it is very likely a "constructed"
|
|
1058
1058
|
// extensions. The CSN parser must produce name.path like for other refs.
|
|
1059
1059
|
if (!node._main)
|
|
1060
|
-
csn[k] = node.name.
|
|
1060
|
+
csn[k] = node.name.id || artifactRef( node.name, true );
|
|
1061
1061
|
else if (k === 'extend')
|
|
1062
1062
|
csn.kind = k;
|
|
1063
1063
|
}
|
|
@@ -1102,7 +1102,7 @@ function artifactRef( node, terse ) {
|
|
|
1102
1102
|
// Works also on XSN directly coming from parser and with XSN from CDL->CSN transformation
|
|
1103
1103
|
// Shortcut for many cases:
|
|
1104
1104
|
if (terse && node._artifact && !node._artifact._main && terse !== '.path')
|
|
1105
|
-
return node._artifact.name.
|
|
1105
|
+
return node._artifact.name.id;
|
|
1106
1106
|
let { path } = node;
|
|
1107
1107
|
if (!path)
|
|
1108
1108
|
return undefined; // TODO: complain with strict
|
|
@@ -1111,25 +1111,35 @@ function artifactRef( node, terse ) {
|
|
|
1111
1111
|
|
|
1112
1112
|
const head = path[0];
|
|
1113
1113
|
const root = head._artifact;
|
|
1114
|
-
const
|
|
1114
|
+
const main = root?._main || root;
|
|
1115
|
+
const id = (main?.extern || main?.name)?.id;
|
|
1115
1116
|
const scope = node.scope || path.length;
|
|
1116
1117
|
|
|
1117
1118
|
if (typeof scope === 'number' && scope > 1) {
|
|
1118
1119
|
const item = path[scope - 1];
|
|
1119
1120
|
const name = item._artifact?.name;
|
|
1120
|
-
const absolute = name?.
|
|
1121
|
+
const absolute = name?.id ||
|
|
1121
1122
|
`${ id || head.id }.${ path.slice( 1, scope ).map( i => i.id ).join('.') }`;
|
|
1122
1123
|
path = [ Object.assign( {}, item, { id: absolute } ), ...path.slice( scope ) ];
|
|
1123
1124
|
}
|
|
1124
1125
|
else if (scope === 'typeOf') { // TYPE OF without ':' in path
|
|
1125
|
-
if (root) {
|
|
1126
|
-
const structs = root.name.element?.split('.').map( n => ({ id: n }) );
|
|
1127
|
-
// TODO: change (follow parents) if we introduce sparse names
|
|
1128
|
-
path = [ { id }, ...(structs || []), ...path.slice(1) ];
|
|
1129
|
-
}
|
|
1130
|
-
else if (strictMode) {
|
|
1126
|
+
if (!root) {
|
|
1131
1127
|
throw new CompilerAssertion( `Unexpected TYPE OF in ${ locationString(node.location) }`);
|
|
1132
1128
|
}
|
|
1129
|
+
else if (!root._main) {
|
|
1130
|
+
path = [ { id }, ...path.slice(1) ];
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
path = path.slice(1).reverse();
|
|
1134
|
+
let parent = root;
|
|
1135
|
+
while (parent._main) {
|
|
1136
|
+
path.push( { id: parent.name.id } );
|
|
1137
|
+
parent = parent._parent;
|
|
1138
|
+
parent = parent._outer || parent; // for anonymous aspect
|
|
1139
|
+
}
|
|
1140
|
+
path.push( { id } );
|
|
1141
|
+
path.reverse();
|
|
1142
|
+
}
|
|
1133
1143
|
}
|
|
1134
1144
|
else if (root && id !== head.id) {
|
|
1135
1145
|
path = [ Object.assign( {}, head, { id } ), ...path.slice( 1 ) ];
|
|
@@ -50,7 +50,7 @@ class RewriteTypeTokenStream extends antlr4.CommonTokenStream {
|
|
|
50
50
|
}
|
|
51
51
|
else if (t.type === this.NEW) {
|
|
52
52
|
const n = super.LT(k + 1);
|
|
53
|
-
// TODO
|
|
53
|
+
// TODO v5: rewrite token in grammar via `this.setLocalToken`
|
|
54
54
|
if (n?.type === this.Identifier) {
|
|
55
55
|
const o = super.LT(k + 2);
|
|
56
56
|
if (o?.type === this.PAREN)
|
|
@@ -180,7 +180,7 @@ function sync( recognizer ) {
|
|
|
180
180
|
|
|
181
181
|
case ATNState.PLUS_LOOP_BACK: // 11
|
|
182
182
|
case ATNState.STAR_LOOP_BACK: { // 9
|
|
183
|
-
// TODO: do not delete a '}'
|
|
183
|
+
// TODO: do not delete a '}', ')', ',', ';'
|
|
184
184
|
this.reportUnwantedToken(recognizer);
|
|
185
185
|
const expecting = new IntervalSet();
|
|
186
186
|
expecting.addSet(recognizer.getExpectedTokens());
|
|
@@ -345,6 +345,10 @@ function recoverInline( recognizer ) {
|
|
|
345
345
|
return this._super.recoverInline.call( this, recognizer );
|
|
346
346
|
|
|
347
347
|
const token = recognizer.getCurrentToken();
|
|
348
|
+
// TODO: do not delete `)`, `}`,
|
|
349
|
+
|
|
350
|
+
// TODO: overwrite singleTokenDeletion do not delete parens etc for identifier
|
|
351
|
+
// or non-reserved keywords
|
|
348
352
|
if (!keywordRegexp.test( token.text ))
|
|
349
353
|
return this._super.recoverInline.call( this, recognizer );
|
|
350
354
|
|
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
XsnArtifact, XsnName, CsnLocation, XsnSource,
|
|
23
23
|
} = require('../compiler/classes');
|
|
24
24
|
const { isBetaEnabled } = require('../base/model');
|
|
25
|
-
const { weakLocation } = require('../base/
|
|
25
|
+
const { weakLocation } = require('../base/location');
|
|
26
26
|
const { normalizeNewLine } = require('./textUtils');
|
|
27
27
|
|
|
28
28
|
const $location = Symbol.for('cds.$location');
|
|
@@ -370,14 +370,15 @@ function assignAnnotation( art, anno, prefix = '' ) {
|
|
|
370
370
|
absolute = `${ prefix }${ pathname }`;
|
|
371
371
|
}
|
|
372
372
|
else {
|
|
373
|
-
absolute = prefix.slice( 0, -1 );
|
|
373
|
+
absolute = prefix.slice( 0, -1 ); // remove final dot
|
|
374
374
|
}
|
|
375
|
+
|
|
375
376
|
if ($flatten) {
|
|
376
377
|
for (const a of $flatten)
|
|
377
378
|
this.assignAnnotation( art, a, `${ absolute }.` );
|
|
378
379
|
}
|
|
379
380
|
else {
|
|
380
|
-
name.
|
|
381
|
+
name.id = absolute;
|
|
381
382
|
this.addAnnotation( art, `@${ absolute }`, anno );
|
|
382
383
|
}
|
|
383
384
|
if (!prefix) { // set deprecated $annotations for cds-lsp
|
|
@@ -706,6 +707,18 @@ function fragileAlias( ast, safe = false ) {
|
|
|
706
707
|
|
|
707
708
|
// Return AST for identifier token `token`. Also check that identifier is not empty.
|
|
708
709
|
function identAst( token, category, noTokenTypeCheck = false ) {
|
|
710
|
+
if (!token) { // for rule identAst
|
|
711
|
+
const { start, stop } = this._ctx; // token.tokenIndex
|
|
712
|
+
// - correct parsing: start = stop
|
|
713
|
+
// - singleTokenDeletion(), e.g. with `| Ident`: start < stop → stop
|
|
714
|
+
// - after recoverInline: start > stop (!) → stop = the previous token, if it is
|
|
715
|
+
// ident-like and the one before not in `.@#`, → start ('') otherwise
|
|
716
|
+
token = stop;
|
|
717
|
+
if (start.tokenIndex > stop.tokenIndex &&
|
|
718
|
+
(stop.type !== this.constructor.Identifier && !/^[a-zA-Z_]+$/.test( stop.text ) ||
|
|
719
|
+
[ '.', '@', '#' ].includes( this._input.LT(-2)?.text )))
|
|
720
|
+
token = start;
|
|
721
|
+
}
|
|
709
722
|
token.isIdentifier = category;
|
|
710
723
|
let id = token.text;
|
|
711
724
|
if (!noTokenTypeCheck &&
|
|
@@ -776,12 +789,7 @@ function valuePathAst( ref ) {
|
|
|
776
789
|
const { path } = ref;
|
|
777
790
|
if (!path || path.broken)
|
|
778
791
|
return ref;
|
|
779
|
-
if (path.length
|
|
780
|
-
const item = path.find( i => i.args && i.$syntax !== ':' );
|
|
781
|
-
if (!item) // also covers empty paths
|
|
782
|
-
return ref;
|
|
783
|
-
}
|
|
784
|
-
else if (path.length === 1) {
|
|
792
|
+
if (path.length === 1) {
|
|
785
793
|
const { args, id, location } = path[0];
|
|
786
794
|
if (args
|
|
787
795
|
? path[0].$syntax === ':'
|
|
@@ -799,40 +807,46 @@ function valuePathAst( ref ) {
|
|
|
799
807
|
: { op, func: ref, location: ref.location };
|
|
800
808
|
}
|
|
801
809
|
|
|
802
|
-
// method call ---------------------------
|
|
803
810
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
811
|
+
// $syntax === ':' => path(P: 1)
|
|
812
|
+
// $syntax !== ':' => path(P => 1) or path(1) or path()
|
|
813
|
+
const firstFunc = path.findIndex( i => i.args && i.$syntax !== ':' );
|
|
814
|
+
if (firstFunc === -1) // also covers empty paths
|
|
815
|
+
return ref;
|
|
807
816
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
refPath.push(pathHead);
|
|
821
|
-
pathHead = pathRest.shift();
|
|
817
|
+
|
|
818
|
+
// Method Call ---------------------------
|
|
819
|
+
// Transform the path into `.`-operators.
|
|
820
|
+
// Everything after the first function is also a function, and not a reference.
|
|
821
|
+
|
|
822
|
+
for (let i = firstFunc; i < path.length; ++i) {
|
|
823
|
+
if (path[i].args && path[i].$syntax === ':') {
|
|
824
|
+
// Error for `a(P => 1).b.c(P: 1)`: no ref after function.
|
|
825
|
+
this.$messageFunctions.error('syntax-invalid-ref', path[i].args[$location], {
|
|
826
|
+
code: '=>',
|
|
827
|
+
}, 'References after function calls can\'t be resolved. Use $(CODE) in function arguments');
|
|
828
|
+
break;
|
|
822
829
|
}
|
|
823
|
-
args.push({ path: refPath, location: refPath[0].location });
|
|
824
830
|
}
|
|
825
831
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
for (const method of pathRest) {
|
|
832
|
+
const args = [];
|
|
833
|
+
if (firstFunc > 0) {
|
|
830
834
|
args.push({
|
|
831
|
-
|
|
832
|
-
location:
|
|
833
|
-
val: '.',
|
|
834
|
-
literal: 'token',
|
|
835
|
+
path: path.slice(0, firstFunc),
|
|
836
|
+
location: locUtils.combinedLocation(path[0].location, path[path.length - 1].location),
|
|
835
837
|
});
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const pathRest = path.slice(firstFunc);
|
|
841
|
+
for (const method of pathRest) {
|
|
842
|
+
if (method !== pathRest[0] || firstFunc > 0) {
|
|
843
|
+
args.push({
|
|
844
|
+
// TODO: Update parser to have proper location for `.`?
|
|
845
|
+
location: weakLocation(method.location),
|
|
846
|
+
val: '.',
|
|
847
|
+
literal: 'token',
|
|
848
|
+
});
|
|
849
|
+
}
|
|
836
850
|
const func = {
|
|
837
851
|
op: { location: method.location, val: 'call' },
|
|
838
852
|
func: { path: [ method ] },
|
|
@@ -1026,46 +1040,38 @@ function reportUnexpectedSpace( prefix = this._input.LT(-1),
|
|
|
1026
1040
|
//
|
|
1027
1041
|
// If argument `kind` is provided, set `art.kind` to that value.
|
|
1028
1042
|
// If argument `name` is provided, set `art.name`:
|
|
1029
|
-
// - if `name` is an array,
|
|
1043
|
+
// - if `name` is an array, `name.id` consist of the ID of the last array item
|
|
1030
1044
|
// (for elements via columns, foreign keys, table aliases)
|
|
1031
|
-
// - if `name` is an object,
|
|
1032
|
-
// (for main artifact definitions)
|
|
1045
|
+
// - if `name` is an object, `name.id` is either set, or the (local) name is calculated
|
|
1046
|
+
// from the IDs of all items in `name.path` (for main artifact definitions).
|
|
1033
1047
|
function addDef( art, parent, env, kind, name ) {
|
|
1034
1048
|
if (Array.isArray(name)) {
|
|
1035
|
-
// XSN TODO: clearly say: definitions have name.path, members have name.id
|
|
1036
1049
|
const last = name.length && name[name.length - 1];
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1050
|
+
art.name = { // A.B.C -> 'C'
|
|
1051
|
+
id: last?.id || '', location: last.location, $inferred: 'as',
|
|
1052
|
+
};
|
|
1042
1053
|
}
|
|
1043
1054
|
else if (name) {
|
|
1044
1055
|
art.name = name;
|
|
1045
|
-
if (name.id
|
|
1046
|
-
name.id = pathName( name
|
|
1047
|
-
|
|
1056
|
+
if (!name.id && kind === null) // namedValue, fortunately no `variant` there
|
|
1057
|
+
art.name.id = pathName( art.name?.path );
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
art.name = { id: '' };
|
|
1048
1061
|
}
|
|
1049
|
-
|
|
1050
1062
|
if (kind)
|
|
1051
1063
|
art.kind = kind;
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
// TODO: add to parent[env]['']
|
|
1055
|
-
// which could be tested in name search (then no undefined-ref error)
|
|
1056
|
-
// console.log( kind+': !!', art, parent, env, name )
|
|
1057
|
-
return art;
|
|
1058
|
-
}
|
|
1059
|
-
// console.log( kind+':', art, parent, env, name )
|
|
1064
|
+
|
|
1065
|
+
const id = art.name?.id || pathName( art.name?.path ); // returns '' for corrupted name
|
|
1060
1066
|
|
|
1061
1067
|
if (env === 'artifacts' || env === 'vocabularies') {
|
|
1062
|
-
dictAddArray( parent[env],
|
|
1068
|
+
dictAddArray( parent[env], id, art );
|
|
1063
1069
|
}
|
|
1064
|
-
else if (kind || this.options.parseOnly) {
|
|
1065
|
-
dictAdd( parent[env],
|
|
1070
|
+
else if (kind || this.options.parseOnly) { // TODO: do not check parseOnly
|
|
1071
|
+
dictAdd( parent[env], id, art );
|
|
1066
1072
|
}
|
|
1067
1073
|
else {
|
|
1068
|
-
dictAdd( parent[env],
|
|
1074
|
+
dictAdd( parent[env], id, art, ( duplicateName, loc ) => {
|
|
1069
1075
|
// do not use function(), otherwise `this` is wrong:
|
|
1070
1076
|
if (kind === 0) {
|
|
1071
1077
|
this.error( 'syntax-duplicate-argument', loc, { name: duplicateName },
|
package/lib/main.d.ts
CHANGED
|
@@ -248,6 +248,18 @@ declare namespace compiler {
|
|
|
248
248
|
serviceNames?: string[]
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Options used by the `for.effective()` CSN transformation.
|
|
253
|
+
*
|
|
254
|
+
* WORK IN PROGRESS
|
|
255
|
+
*
|
|
256
|
+
* @internal
|
|
257
|
+
* @see _for.effective()
|
|
258
|
+
*/
|
|
259
|
+
export interface EffectiveCsnOptions extends SqlOptions {
|
|
260
|
+
// TODO
|
|
261
|
+
}
|
|
262
|
+
|
|
251
263
|
/**
|
|
252
264
|
* Options used by SQL `to.sql()` backend.
|
|
253
265
|
*
|
|
@@ -818,11 +830,24 @@ declare namespace compiler {
|
|
|
818
830
|
*/
|
|
819
831
|
export namespace _for {
|
|
820
832
|
/**
|
|
821
|
-
* Transform the given (
|
|
833
|
+
* Transform the given (inferred/client) CSN into one that is used for OData.
|
|
822
834
|
* Changes include flattening, type resolution and more, according to
|
|
823
835
|
* the provided options.
|
|
824
836
|
*/
|
|
825
837
|
function odata(csn: CSN, options?: ODataOptions): CSN;
|
|
838
|
+
/**
|
|
839
|
+
* Transform the given CSN into one that has these properties:
|
|
840
|
+
* - types are resolved
|
|
841
|
+
* - elements are flattened
|
|
842
|
+
* - …
|
|
843
|
+
*
|
|
844
|
+
* THIS IS HIGHLY EXPERIMENTAL
|
|
845
|
+
*
|
|
846
|
+
* Beta flag `effectiveCsn` is required.
|
|
847
|
+
*
|
|
848
|
+
* @internal
|
|
849
|
+
*/
|
|
850
|
+
function effective(csn: CSN, options?: EffectiveCsnOptions): CSN;
|
|
826
851
|
}
|
|
827
852
|
|
|
828
853
|
export { _for as for };
|
package/lib/main.js
CHANGED
|
@@ -115,7 +115,8 @@ module.exports = {
|
|
|
115
115
|
},
|
|
116
116
|
// SNAPI
|
|
117
117
|
for: {
|
|
118
|
-
odata: (...args) => snapi.odata(...args)
|
|
118
|
+
odata: (...args) => snapi.odata(...args),
|
|
119
|
+
effective: (...args) => snapi.for_effective(...args),
|
|
119
120
|
},
|
|
120
121
|
to: {
|
|
121
122
|
cdl: Object.assign((...args) => snapi.cdl(...args), {
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -433,6 +433,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
433
433
|
return getOriginExplicit( $origin.$origin );
|
|
434
434
|
const [ head, ...tail ] = $origin;
|
|
435
435
|
const main = csn.definitions[head];
|
|
436
|
+
// if (!main) throw Error(JSON.stringify({$origin,csn}))
|
|
436
437
|
initDefinition( main );
|
|
437
438
|
return tail.reduce( originNavigation, main );
|
|
438
439
|
}
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -1411,6 +1411,32 @@ function functionList( functions, thisArg ) {
|
|
|
1411
1411
|
};
|
|
1412
1412
|
}
|
|
1413
1413
|
|
|
1414
|
+
/**
|
|
1415
|
+
* Return true if 'arg' is an expression argument denoting "$self" || "$projection"
|
|
1416
|
+
* @param {object} arg
|
|
1417
|
+
* @returns {boolean}
|
|
1418
|
+
*/
|
|
1419
|
+
function isDollarSelfOrProjectionOperand( arg ) {
|
|
1420
|
+
return arg.ref && arg.ref.length === 1 && (arg.ref[0] === '$self' || arg.ref[0] === '$projection');
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
/**
|
|
1424
|
+
* Return true if 'arg' is an expression argument of type association or composition
|
|
1425
|
+
* @param {object} arg
|
|
1426
|
+
* @param {CSN.Path} path
|
|
1427
|
+
* @param {function} inspectRef
|
|
1428
|
+
* @returns {boolean}
|
|
1429
|
+
*/
|
|
1430
|
+
function isAssociationOperand( arg, path, inspectRef ) {
|
|
1431
|
+
if (!arg.ref) {
|
|
1432
|
+
// Not a path, hence not an association (literal, expression, function, whatever ...)
|
|
1433
|
+
return false;
|
|
1434
|
+
}
|
|
1435
|
+
const { art } = inspectRef(path);
|
|
1436
|
+
// If it has a target, it is an association or composition
|
|
1437
|
+
return art && art.target !== undefined;
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1414
1440
|
module.exports = {
|
|
1415
1441
|
getUtils,
|
|
1416
1442
|
cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
|
|
@@ -1456,4 +1482,6 @@ module.exports = {
|
|
|
1456
1482
|
isDeepEqual,
|
|
1457
1483
|
functionList,
|
|
1458
1484
|
cardinality2str,
|
|
1485
|
+
isAssociationOperand,
|
|
1486
|
+
isDollarSelfOrProjectionOperand,
|
|
1459
1487
|
};
|
|
@@ -159,14 +159,8 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
159
159
|
|
|
160
160
|
function shortenName( node, parent ) {
|
|
161
161
|
const name = reveal( node, parent );
|
|
162
|
-
if (name && typeof name === 'object' &&
|
|
162
|
+
if (name && typeof name === 'object' && parent.kind) {
|
|
163
163
|
const text = artifactIdentifier( parent );
|
|
164
|
-
delete name.absolute;
|
|
165
|
-
delete name.select;
|
|
166
|
-
delete name.action;
|
|
167
|
-
delete name.parameter;
|
|
168
|
-
delete name.alias;
|
|
169
|
-
delete name.element;
|
|
170
164
|
name['-->'] = text;
|
|
171
165
|
}
|
|
172
166
|
return name;
|
|
@@ -193,7 +187,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
193
187
|
const { name, $flatten } = anno.value || anno;
|
|
194
188
|
const value = ($flatten)
|
|
195
189
|
? { name: reveal( name ), $flatten: $flatten.map( $annotation ) }
|
|
196
|
-
: `@${ name?.
|
|
190
|
+
: `@${ name?.id }`;
|
|
197
191
|
return { value, location: locationString( anno.location || anno.name.location ) };
|
|
198
192
|
}
|
|
199
193
|
|
|
@@ -349,7 +343,7 @@ function artifactIdentifier( node, parent ) {
|
|
|
349
343
|
? artifactIdentifier( node._artifact )
|
|
350
344
|
: JSON.stringify(node.name);
|
|
351
345
|
case 'builtin':
|
|
352
|
-
return
|
|
346
|
+
return msg.artName(node);
|
|
353
347
|
case 'source':
|
|
354
348
|
case 'using':
|
|
355
349
|
return `source:${ quoted( node.location && node.location.file )
|
package/lib/optionProcessor.js
CHANGED
|
@@ -105,8 +105,9 @@ optionProcessor
|
|
|
105
105
|
--beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
|
|
106
106
|
Valid values are:
|
|
107
107
|
${
|
|
108
|
-
Object.keys(availableBetaFlags)
|
|
108
|
+
Object.keys(availableBetaFlags)
|
|
109
109
|
.filter(flag => availableBetaFlags[flag])
|
|
110
|
+
.sort()
|
|
110
111
|
.join('\n' + ' '.repeat(30))
|
|
111
112
|
}
|
|
112
113
|
--deprecated <list> Comma separated list of deprecated options.
|
|
@@ -151,6 +152,7 @@ optionProcessor
|
|
|
151
152
|
manageConstraints [options] <files...> (internal) Generate ALTER TABLE statements to
|
|
152
153
|
add / modify referential constraints.
|
|
153
154
|
inspect [options] <files...> (internal) Inspect the given CDS files.
|
|
155
|
+
toEffectiveCsn [options] <files...> (internal) Get an effective CSN for SEAL; requires beta mode
|
|
154
156
|
|
|
155
157
|
Environment variables
|
|
156
158
|
NO_COLOR If set, compiler messages (/output) will not be colored.
|
|
@@ -523,6 +525,20 @@ optionProcessor.command('inspect')
|
|
|
523
525
|
--propagation <art> Show propagation sources for <art>
|
|
524
526
|
`);
|
|
525
527
|
|
|
528
|
+
optionProcessor.command('toEffectiveCsn')
|
|
529
|
+
.option('-h, --help')
|
|
530
|
+
.positionalArgument('<files...>')
|
|
531
|
+
.help(`
|
|
532
|
+
Usage: cdsc toEffectiveCsn [options] <files...>
|
|
533
|
+
|
|
534
|
+
(internal): Get the effective CSN model compiled from the provided CDS files.
|
|
535
|
+
This command may change any time, including its name.
|
|
536
|
+
Beta mode is required.
|
|
537
|
+
|
|
538
|
+
Options
|
|
539
|
+
-h, --help Show this help text
|
|
540
|
+
`);
|
|
541
|
+
|
|
526
542
|
module.exports = {
|
|
527
543
|
optionProcessor
|
|
528
544
|
};
|
package/lib/render/toCdl.js
CHANGED
|
@@ -593,7 +593,7 @@ function csnToCdl( csn, options ) {
|
|
|
593
593
|
result += env.indent;
|
|
594
594
|
result += element.virtual ? 'virtual ' : '';
|
|
595
595
|
result += element.key ? 'key ' : '';
|
|
596
|
-
// TODO(
|
|
596
|
+
// TODO(v5): Remove once deprecated flag for `masked` is removed.
|
|
597
597
|
result += element.masked ? 'masked ' : '';
|
|
598
598
|
result += quoteNonIdentifierOrKeyword(elementName, env);
|
|
599
599
|
if (element.val !== undefined) { // enum value
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const {
|
|
4
4
|
applyTransformationsOnNonDictionary,
|
|
5
5
|
applyTransformations,
|
|
6
|
+
implicitAs,
|
|
6
7
|
} = require('../../model/csnUtils');
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -15,9 +16,7 @@ const {
|
|
|
15
16
|
* @returns {CSN.Model} Return the input csn, with the transformations applied
|
|
16
17
|
*/
|
|
17
18
|
function attachOnConditions( csn, csnUtils, pathDelimiter ) {
|
|
18
|
-
const {
|
|
19
|
-
isManagedAssociation,
|
|
20
|
-
} = csnUtils;
|
|
19
|
+
const { isManagedAssociation } = csnUtils;
|
|
21
20
|
|
|
22
21
|
const alreadyHandled = new WeakMap();
|
|
23
22
|
applyTransformations(csn, {
|
|
@@ -59,7 +58,7 @@ function attachOnConditions( csn, csnUtils, pathDelimiter ) {
|
|
|
59
58
|
elemName,
|
|
60
59
|
].concat(foreignKey.ref),
|
|
61
60
|
};
|
|
62
|
-
const fkName = `${elemName}${pathDelimiter}${foreignKey.as}`;
|
|
61
|
+
const fkName = `${elemName}${pathDelimiter}${foreignKey.as || implicitAs(foreignKey.ref)}`;
|
|
63
62
|
const fKeyArg = {
|
|
64
63
|
ref: [
|
|
65
64
|
fkName,
|