@sap/cds-compiler 2.12.0 → 2.13.6
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 +110 -15
- package/bin/cdsc.js +13 -13
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +28 -63
- package/lib/api/options.js +3 -3
- 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 +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- 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 +32 -13
- 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 +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- 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 +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- package/lib/render/utils/sql.js +4 -3
- 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/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 +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- 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} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- 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 +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- 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/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/universalCsnEnricher.js +0 -237
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
|
} );
|
|
@@ -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 );
|
|
@@ -1177,6 +1186,7 @@ function annoValue( val, spec ) {
|
|
|
1177
1186
|
if (lit !== 'object')
|
|
1178
1187
|
return { val, literal: lit, location: location() };
|
|
1179
1188
|
if (Array.isArray( val )) {
|
|
1189
|
+
/** @type {string|boolean} */
|
|
1180
1190
|
let seenEllipsis = false;
|
|
1181
1191
|
if (arrayLevelCount > 0) { // TODO: also inside structure (possible in CSN!)
|
|
1182
1192
|
if (val.some( isEllipsis )) {
|
|
@@ -1435,6 +1445,7 @@ function excluding( array, spec, xsn ) {
|
|
|
1435
1445
|
if (!isArray( array, spec ))
|
|
1436
1446
|
return;
|
|
1437
1447
|
const r = Object.create(null);
|
|
1448
|
+
r[$location] = location();
|
|
1438
1449
|
++virtualLine;
|
|
1439
1450
|
for (const ex of array) {
|
|
1440
1451
|
const id = string( ex, spec ) || '';
|
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!
|
|
@@ -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
|
*
|
|
@@ -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;
|
|
@@ -779,7 +779,7 @@ function addLocation( loc, csn ) {
|
|
|
779
779
|
// Remove endLine/endCol:
|
|
780
780
|
// Reasoning: $location is mostly attached to definitions/members but the name
|
|
781
781
|
// is often not the reason for an error or warning. So we gain little benefit for
|
|
782
|
-
// two more properties.
|
|
782
|
+
// two more properties. It is also an indication that the location is not exact.
|
|
783
783
|
const val = { file: loc.file, line: loc.line, col: loc.col };
|
|
784
784
|
Object.defineProperty( csn, '$location', {
|
|
785
785
|
value: val, configurable: true, writable: true, enumerable: withLocations,
|
|
@@ -807,6 +807,13 @@ function actions( dict ) {
|
|
|
807
807
|
: undefined;
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
+
function params( dict ) {
|
|
811
|
+
const keys = Object.keys( dict );
|
|
812
|
+
return (keys.length)
|
|
813
|
+
? insertOrderDict( dict )
|
|
814
|
+
: undefined;
|
|
815
|
+
}
|
|
816
|
+
|
|
810
817
|
function dictionary( dict, keys, prop ) {
|
|
811
818
|
const csn = Object.create( dictionaryPrototype );
|
|
812
819
|
for (const name of keys) {
|
|
@@ -863,7 +870,7 @@ function definition( art, _csn, _node, prop ) {
|
|
|
863
870
|
c.returns = { elements: elems };
|
|
864
871
|
}
|
|
865
872
|
// precondition already fulfilled: art.kind !== 'key'
|
|
866
|
-
addOrigin( c, art, art
|
|
873
|
+
addOrigin( c, art, art );
|
|
867
874
|
return c;
|
|
868
875
|
}
|
|
869
876
|
|
|
@@ -876,7 +883,8 @@ function includesOrigin( includes, art ) {
|
|
|
876
883
|
for (const incl of includes.slice(1)) {
|
|
877
884
|
const aspect = incl._artifact;
|
|
878
885
|
for (const prop in aspect) {
|
|
879
|
-
if (prop.charAt(0) === '@'
|
|
886
|
+
if ((prop.charAt(0) === '@' || prop === 'doc') &&
|
|
887
|
+
(!art[prop] || art[prop].$inferred)) {
|
|
880
888
|
const anno = aspect[prop];
|
|
881
889
|
if (anno.val !== null)
|
|
882
890
|
// matererialize non-null annos (whether direct or inherited)
|
|
@@ -887,9 +895,23 @@ function includesOrigin( includes, art ) {
|
|
|
887
895
|
return (Object.keys( result ).length === 1) ? $origin : result;
|
|
888
896
|
}
|
|
889
897
|
|
|
890
|
-
function addOrigin( csn, xsn,
|
|
891
|
-
if (!universalCsn
|
|
898
|
+
function addOrigin( csn, xsn, node ) {
|
|
899
|
+
if (!universalCsn)
|
|
892
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;
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
893
915
|
if (xsn._from) {
|
|
894
916
|
const source = xsn._from[0]._origin;
|
|
895
917
|
csn.$origin = originRef( source );
|
|
@@ -903,23 +925,22 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
903
925
|
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
904
926
|
return;
|
|
905
927
|
}
|
|
906
|
-
|
|
928
|
+
let origin = getOrigin( node );
|
|
929
|
+
if (xsn.$inferred === 'composition-entity') {
|
|
930
|
+
csn.$origin = originRef( origin, xsn );
|
|
907
931
|
return;
|
|
908
932
|
}
|
|
933
|
+
else if (!isMember( xsn ) || xsn.kind === 'select') {
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
// from here on: member:
|
|
909
937
|
const parent = getParent( xsn );
|
|
910
938
|
const parentOrigin = getOrigin( parent );
|
|
911
|
-
if (!
|
|
912
|
-
if (parentOrigin && (
|
|
939
|
+
if (!origin) {
|
|
940
|
+
if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
|
|
913
941
|
csn.$origin = null;
|
|
914
942
|
return;
|
|
915
943
|
}
|
|
916
|
-
// Skip all proxies which do not make it into the CSN, as there are no
|
|
917
|
-
// individual annotations or redirection targets on it:
|
|
918
|
-
while (origin._parent && origin._parent.$expand === 'origin')
|
|
919
|
-
origin = origin._origin || origin.type._artifact;
|
|
920
|
-
// The while loop is not only for the else case below: when setting implicit
|
|
921
|
-
// prototypes, it is important that we do not have to follow the prototypes of
|
|
922
|
-
// other object; we would need to ensure a right order to avoid issues otherwise.
|
|
923
944
|
if (parentOrigin === getParent( origin )) {
|
|
924
945
|
// implicit prototype or shortened reference
|
|
925
946
|
const { id } = origin.name || {};
|
|
@@ -928,16 +949,21 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
928
949
|
return;
|
|
929
950
|
}
|
|
930
951
|
if (origin.kind === 'mixin') {
|
|
931
|
-
|
|
932
|
-
csn
|
|
952
|
+
set( 'type', csn, origin );
|
|
953
|
+
set( 'cardinality', csn, origin );
|
|
954
|
+
// currently, target and on are always set - nothing to do here
|
|
933
955
|
return;
|
|
934
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;
|
|
935
961
|
const ref = originRef( origin, xsn );
|
|
936
962
|
if (ref) {
|
|
937
963
|
csn.$origin = ref;
|
|
938
964
|
return;
|
|
939
965
|
}
|
|
940
|
-
// An element of a query with a query in FROM:
|
|
966
|
+
// An element of a query with a query in FROM: -----------------------------
|
|
941
967
|
const anon = definition( origin ); // use $origin: {...} if necessary
|
|
942
968
|
// as there are no implicit $origin prototypes on sub query elements (yet),
|
|
943
969
|
// we do not have to care about $origin not being set
|
|
@@ -945,15 +971,26 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
945
971
|
if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
|
|
946
972
|
// repeated anon: flatten
|
|
947
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];
|
|
948
986
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
addOrigin( csn, xsn, origin._origin );
|
|
987
|
+
if (Object.keys( annos ).length) {
|
|
988
|
+
if (!csn.type && $origin)
|
|
989
|
+
annos.$origin = $origin;
|
|
990
|
+
csn.$origin = annos;
|
|
954
991
|
}
|
|
955
|
-
else {
|
|
956
|
-
csn
|
|
992
|
+
else if (!csn.type) {
|
|
993
|
+
addOrigin( csn, xsn, origin );
|
|
957
994
|
}
|
|
958
995
|
}
|
|
959
996
|
|
|
@@ -963,10 +1000,27 @@ function getParent( art ) {
|
|
|
963
1000
|
return (main && parent === main._leadingQuery) ? main : parent;
|
|
964
1001
|
}
|
|
965
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)
|
|
966
1017
|
function getOrigin( art ) {
|
|
967
|
-
if (art
|
|
968
|
-
return
|
|
969
|
-
|
|
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' ))
|
|
970
1024
|
return art.type._artifact;
|
|
971
1025
|
if (art.includes)
|
|
972
1026
|
return art.includes[0]._artifact;
|
|
@@ -975,31 +1029,31 @@ function getOrigin( art ) {
|
|
|
975
1029
|
return undefined;
|
|
976
1030
|
}
|
|
977
1031
|
|
|
978
|
-
function hasExplicitProp( ref ) {
|
|
979
|
-
return ref && !ref.$inferred;
|
|
1032
|
+
function hasExplicitProp( ref, alsoLikeExplicit ) {
|
|
1033
|
+
return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
|
|
980
1034
|
}
|
|
981
1035
|
|
|
982
1036
|
function originRef( art, user ) {
|
|
983
1037
|
const r = [];
|
|
984
|
-
// do not use name.element, as we allow `.`s in name
|
|
1038
|
+
// do not use name.element, as we might allow `.`s in name
|
|
985
1039
|
let parent = art;
|
|
986
|
-
|
|
1040
|
+
if (parent._outer && parent.kind === 'aspect')
|
|
1041
|
+
r.push( { target: true } );
|
|
1042
|
+
while (isMember( parent ) && parent.kind !== 'select') {
|
|
987
1043
|
const nkind = normalizedKind[parent.kind];
|
|
988
|
-
|
|
1044
|
+
const name = parent.name || parent._outer.name;
|
|
1045
|
+
if (name.id || !r.length)
|
|
989
1046
|
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
990
1047
|
// eslint-disable-next-line no-nested-ternary, max-len
|
|
991
|
-
r.push( !nkind ?
|
|
1048
|
+
r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
|
|
992
1049
|
parent = parent._parent;
|
|
993
1050
|
}
|
|
994
1051
|
if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
|
|
995
1052
|
// well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
|
|
996
|
-
return
|
|
1053
|
+
return false; // do not write, probably use $origin: {...}
|
|
997
1054
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
998
1055
|
|
|
999
|
-
|
|
1000
|
-
if (r.length === 1 && normalizedKind[art.kind] === 'action')
|
|
1001
|
-
return [ art.name.absolute, art.name.id ];
|
|
1002
|
-
r.push( art.name.absolute );
|
|
1056
|
+
r.push( parent.name.absolute );
|
|
1003
1057
|
r.reverse();
|
|
1004
1058
|
return r;
|
|
1005
1059
|
}
|
|
@@ -1018,6 +1072,9 @@ function kind( k, csn, node ) {
|
|
|
1018
1072
|
else if (k === 'action' && node._main && universalCsn && node.$inferred) {
|
|
1019
1073
|
// Universal CSN: do not mention kind: 'action' on expanded action
|
|
1020
1074
|
}
|
|
1075
|
+
else if (k === 'aspect' && (node._outer || node.$inferred)) {
|
|
1076
|
+
return; // do not show kind for anonymous aspect
|
|
1077
|
+
}
|
|
1021
1078
|
else if (![
|
|
1022
1079
|
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
1023
1080
|
'$tableAlias', 'annotation', 'mixin',
|
|
@@ -1032,26 +1089,20 @@ function kind( k, csn, node ) {
|
|
|
1032
1089
|
function type( node, csn, xsn ) {
|
|
1033
1090
|
if (!universalCsn)
|
|
1034
1091
|
return artifactRef( node, !node.$extra );
|
|
1035
|
-
if (node.$inferred)
|
|
1092
|
+
if (node.$inferred && node.$inferred !== 'cast')
|
|
1036
1093
|
return undefined;
|
|
1037
1094
|
if (xsn._origin) {
|
|
1038
1095
|
if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
|
|
1039
1096
|
csn.$origin = definition( xsn._origin );
|
|
1040
|
-
return undefined;
|
|
1041
1097
|
}
|
|
1042
1098
|
}
|
|
1043
|
-
else if ( xsn.targetAspect && xsn.target ) {
|
|
1044
|
-
// type moved to $origin: { type: … }
|
|
1045
|
-
return undefined;
|
|
1046
|
-
}
|
|
1047
1099
|
return artifactRef( node, !node.$extra );
|
|
1048
1100
|
}
|
|
1049
1101
|
|
|
1050
|
-
function cardinality( node
|
|
1102
|
+
function cardinality( node ) {
|
|
1051
1103
|
if (!universalCsn)
|
|
1052
1104
|
return standard( node );
|
|
1053
|
-
|
|
1054
|
-
if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
|
|
1105
|
+
if (node.$inferred)
|
|
1055
1106
|
return undefined;
|
|
1056
1107
|
return standard( node );
|
|
1057
1108
|
}
|
|
@@ -1159,8 +1210,12 @@ function value( node ) {
|
|
|
1159
1210
|
// "Short" value form, e.g. for annotation assignments
|
|
1160
1211
|
if (!node)
|
|
1161
1212
|
return true; // `@aBool` short for `@aBool: true`
|
|
1162
|
-
if (universalCsn && node.$inferred
|
|
1163
|
-
|
|
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
|
+
}
|
|
1164
1219
|
if (node.$inferred && gensrcFlavor)
|
|
1165
1220
|
return undefined;
|
|
1166
1221
|
if (node.path) {
|
|
@@ -1329,7 +1384,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
|
1329
1384
|
if (node.op.val === 'SELECT') {
|
|
1330
1385
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
1331
1386
|
node.from && node.from.path && !projectionAsQuery) {
|
|
1332
|
-
csn.projection = standard( node );
|
|
1387
|
+
csn.projection = addLocation( node.location, standard( node ) );
|
|
1333
1388
|
return undefined;
|
|
1334
1389
|
}
|
|
1335
1390
|
const select = { SELECT: extra( standard( node ), node, expectedParens ) };
|
|
@@ -1351,6 +1406,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
|
1351
1406
|
gensrcFlavor = gensrcSaved;
|
|
1352
1407
|
}
|
|
1353
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 );
|
|
1354
1412
|
return addLocation( node.location, select );
|
|
1355
1413
|
}
|
|
1356
1414
|
const union = {};
|
|
@@ -1399,11 +1457,13 @@ function from( node ) {
|
|
|
1399
1457
|
else if (node.query) {
|
|
1400
1458
|
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1401
1459
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
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;
|
|
1407
1467
|
const dot = name.lastIndexOf('.');
|
|
1408
1468
|
return name.substring( dot + 1 ) !== id;
|
|
1409
1469
|
}), node );
|
|
@@ -1452,7 +1512,7 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1452
1512
|
}
|
|
1453
1513
|
|
|
1454
1514
|
function orderBy( node ) {
|
|
1455
|
-
const expr = expression( node
|
|
1515
|
+
const expr = expression( node );
|
|
1456
1516
|
if (node.sort)
|
|
1457
1517
|
expr.sort = node.sort.val;
|
|
1458
1518
|
if (node.nulls)
|
|
@@ -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;
|
|
@@ -14,6 +14,7 @@ const { parseDocComment } = require('./docCommentParser');
|
|
|
14
14
|
const { parseMultiLineStringLiteral } = require('./multiLineStringParser');
|
|
15
15
|
const { functionsWithoutParens, specialFunctions } = require('../compiler/builtins');
|
|
16
16
|
|
|
17
|
+
const $location = Symbol.for('cds.$location');
|
|
17
18
|
|
|
18
19
|
// Push message `msg` with location `loc` to array of errors:
|
|
19
20
|
function _message( parser, severity, id, loc, ...args ) {
|
|
@@ -35,6 +36,19 @@ function GenericAntlrParser( ...args ) {
|
|
|
35
36
|
// ANTLR restriction: we cannot add parameters to the constructor.
|
|
36
37
|
antlr4.Parser.call( this, ...args );
|
|
37
38
|
this.buildParseTrees = false;
|
|
39
|
+
|
|
40
|
+
// Common properties.
|
|
41
|
+
// We set them here so that they are available in the prototype.
|
|
42
|
+
// This improved performance by 25% for certain scenario tests.
|
|
43
|
+
// Probably because there was no need to look up the prototype chain anymore.
|
|
44
|
+
this.$adaptExpectedToken = null;
|
|
45
|
+
this.$adaptExpectedExcludes = [ ];
|
|
46
|
+
this.$nextTokensToken = null;
|
|
47
|
+
this.$nextTokensContext = null;
|
|
48
|
+
|
|
49
|
+
this.prepareGenericKeywords();
|
|
50
|
+
this.options = {};
|
|
51
|
+
|
|
38
52
|
return this;
|
|
39
53
|
}
|
|
40
54
|
|
|
@@ -55,6 +69,8 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
55
69
|
valueWithTokenLocation,
|
|
56
70
|
previousTokenAtLocation,
|
|
57
71
|
combinedLocation,
|
|
72
|
+
createDict,
|
|
73
|
+
setDictEndLocation,
|
|
58
74
|
surroundByParens,
|
|
59
75
|
unaryOpForParens,
|
|
60
76
|
leftAssocBinaryOp,
|
|
@@ -70,6 +86,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
70
86
|
docComment,
|
|
71
87
|
addDef,
|
|
72
88
|
addItem,
|
|
89
|
+
artifactForElementAnnotateOrExtend,
|
|
73
90
|
assignProps,
|
|
74
91
|
createPrefixOp,
|
|
75
92
|
setOnce,
|
|
@@ -79,6 +96,7 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
79
96
|
reportExpandInline,
|
|
80
97
|
notSupportedYet,
|
|
81
98
|
csnParseOnly,
|
|
99
|
+
disallowElementExtension,
|
|
82
100
|
noAssignmentInSameLine,
|
|
83
101
|
noSemicolonHere,
|
|
84
102
|
setLocalToken,
|
|
@@ -213,6 +231,22 @@ function setLocalTokenIfBefore( string, tokenName, before, inSameLine ) {
|
|
|
213
231
|
// // throw new antlr4.error.InputMismatchException(this);
|
|
214
232
|
// }
|
|
215
233
|
|
|
234
|
+
/**
|
|
235
|
+
* For element extensions (`extend E:elem` syntax).
|
|
236
|
+
* If `elemName.path` is set, remove the last extension from `$outer` and
|
|
237
|
+
* emit an error that the extension is invalid.
|
|
238
|
+
*
|
|
239
|
+
* @param {object} elemName
|
|
240
|
+
* @param {object} outer
|
|
241
|
+
* @param {string} extensionVariant
|
|
242
|
+
*/
|
|
243
|
+
function disallowElementExtension(elemName, outer, extensionVariant) {
|
|
244
|
+
if (elemName.path) {
|
|
245
|
+
this.message( 'syntax-invalid-extend', this.tokenLocation(this.getCurrentToken()), { 'kind': extensionVariant } );
|
|
246
|
+
outer.extensions.length = outer.extensions.length - 1; // remove last, i.e. new extension
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
216
250
|
function noAssignmentInSameLine() {
|
|
217
251
|
const t = this.getCurrentToken();
|
|
218
252
|
if (t.text === '@' && t.line <= this._input.LT(-1).line) {
|
|
@@ -373,6 +407,20 @@ function combinedLocation( start, end ) {
|
|
|
373
407
|
return locUtils.combinedLocation( start, end );
|
|
374
408
|
}
|
|
375
409
|
|
|
410
|
+
function createDict( location = null ) {
|
|
411
|
+
const dict = Object.create(null);
|
|
412
|
+
dict[$location] = location || this.startLocation( this._input.LT(-1) );
|
|
413
|
+
return dict;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function setDictEndLocation( dict ) {
|
|
417
|
+
const stop = this._input.LT(-1);
|
|
418
|
+
Object.assign( dict[$location], {
|
|
419
|
+
endLine: stop.line,
|
|
420
|
+
endCol: stop.stop - stop.start + stop.column + 2,
|
|
421
|
+
} );
|
|
422
|
+
}
|
|
423
|
+
|
|
376
424
|
function surroundByParens( expr, open, close, asQuery = false ) {
|
|
377
425
|
if (!expr)
|
|
378
426
|
return expr;
|
|
@@ -664,13 +712,13 @@ function addDef( parent, env, kind, name, annos, props, location ) {
|
|
|
664
712
|
}
|
|
665
713
|
}
|
|
666
714
|
else if (name && name.id == null) {
|
|
667
|
-
name.id = pathName(name.path ); // A.B.C -> 'A.B.C'
|
|
715
|
+
name.id = pathName( name.path ); // A.B.C -> 'A.B.C'
|
|
668
716
|
}
|
|
669
717
|
const art = this.assignProps( { name }, annos, props, location );
|
|
670
718
|
if (kind)
|
|
671
719
|
art.kind = kind;
|
|
672
|
-
if (!parent[env])
|
|
673
|
-
parent[env] = Object.create(null);
|
|
720
|
+
if (!parent[env]) // TODO: dump with --test-mode, env !== 'artifacts'
|
|
721
|
+
parent[env] = env === 'args' ? Object.create(null) : this.createDict( { ...location } );
|
|
674
722
|
if (!art.name || art.name.id == null) {
|
|
675
723
|
// no id was parsed, but with error recovery: no further error
|
|
676
724
|
// TODO: add to parent[env]['']
|
|
@@ -727,6 +775,31 @@ function addItem( parent, env, kind, annos, props, location ) {
|
|
|
727
775
|
return art;
|
|
728
776
|
}
|
|
729
777
|
|
|
778
|
+
/**
|
|
779
|
+
* For `annotate/extend E:elem.sub`, create the `elements` structure
|
|
780
|
+
* that can be used by the core compiler to annotate/extend elements.
|
|
781
|
+
*
|
|
782
|
+
* @param {string} kind Either `annotate` or `extend`
|
|
783
|
+
* @param {object} artifact Main artifact that shall have `elements`.
|
|
784
|
+
* @param {XSN.Path} elementPath Path as returned by `simplePath` token.
|
|
785
|
+
* @param {object[]} annos Existing annotations that shall be added to the _last_ path step.
|
|
786
|
+
* @param {XSN.Location} artifactLocation Start location of the `annotate` statement.
|
|
787
|
+
* @returns {object} Deepest element
|
|
788
|
+
*/
|
|
789
|
+
function artifactForElementAnnotateOrExtend(kind, artifact, elementPath, annos, artifactLocation ) {
|
|
790
|
+
if (!Array.isArray(elementPath) || elementPath.broken || elementPath.length < 1)
|
|
791
|
+
return artifact;
|
|
792
|
+
|
|
793
|
+
for (const seg of elementPath.slice(0, -1)) {
|
|
794
|
+
artifact = this.addDef( artifact, 'elements', kind,
|
|
795
|
+
{ path: [seg], location: seg.location }, null, {}, artifactLocation );
|
|
796
|
+
}
|
|
797
|
+
const last = elementPath[elementPath.length - 1];
|
|
798
|
+
artifact = this.addDef( artifact, 'elements', kind,
|
|
799
|
+
{ path: [ last ], location: last.location }, annos, {}, artifactLocation );
|
|
800
|
+
return artifact;
|
|
801
|
+
}
|
|
802
|
+
|
|
730
803
|
/** Assign all non-empty (undefined, null, {}, []) properties in argument
|
|
731
804
|
* `props` and argument `annos` as property `$annotations` to `target`
|
|
732
805
|
* and return it. Hack: if argument `annos` is exactly `true`, return
|