@sap/cds-compiler 2.11.2 → 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 +175 -2
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +10 -8
- package/bin/cdsc.js +23 -17
- package/bin/cdsse.js +2 -2
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +32 -79
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +2 -1
- package/lib/backends.js +16 -26
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +10 -19
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +75 -9
- package/lib/base/messages.js +31 -35
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/unknownMagic.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +12 -8
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +42 -21
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +46 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +100 -65
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +215 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +189 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +144 -53
- package/lib/language/language.g4 +424 -200
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +550 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +322 -198
- package/lib/model/csnUtils.js +226 -370
- package/lib/model/enrichCsn.js +124 -69
- package/lib/model/revealInternalProperties.js +29 -7
- package/lib/model/sortViews.js +10 -2
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +8 -3
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +203 -122
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +161 -82
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +212 -0
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +556 -106
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +103 -28
- package/lib/transform/db/views.js +92 -44
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +98 -783
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +13 -30
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +8 -3
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2349
- package/lib/compiler/resolver.js +0 -2922
- package/lib/transform/db/helpers.js +0 -58
- package/lib/transform/universalCsnEnricher.js +0 -67
package/lib/json/to-csn.js
CHANGED
|
@@ -37,6 +37,14 @@ let dictionaryPrototype = null;
|
|
|
37
37
|
// stored with symbols as keys, as we do not want to disallow any key name:
|
|
38
38
|
const $inferred = Symbol.for('cds.$inferred');
|
|
39
39
|
|
|
40
|
+
// XSN $inferred values mapped to Universal CSN $generated values:
|
|
41
|
+
const inferredAsGenerated = {
|
|
42
|
+
autoexposed: 'exposed',
|
|
43
|
+
'localized-entity': 'localized',
|
|
44
|
+
localized: 'localized', // on elements (texts, localized)
|
|
45
|
+
'composition-entity': 'composed', // ('aspect-composition' on element not in CSN)
|
|
46
|
+
};
|
|
47
|
+
|
|
40
48
|
// IMPORTANT: the order of these properties determine the order of properties
|
|
41
49
|
// in the resulting CSN !!! Also check const `csnPropertyNames`.
|
|
42
50
|
const transformers = {
|
|
@@ -49,7 +57,7 @@ const transformers = {
|
|
|
49
57
|
key: value,
|
|
50
58
|
unique: value,
|
|
51
59
|
masked: value,
|
|
52
|
-
params
|
|
60
|
+
params,
|
|
53
61
|
// early expression / query properties -------------------------------------
|
|
54
62
|
op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
|
|
55
63
|
from, // before elements!
|
|
@@ -66,7 +74,7 @@ const transformers = {
|
|
|
66
74
|
precision: value,
|
|
67
75
|
scale: value,
|
|
68
76
|
srid: value,
|
|
69
|
-
cardinality
|
|
77
|
+
cardinality, // also in pathItem: after 'id', before 'where'
|
|
70
78
|
targetAspect,
|
|
71
79
|
target,
|
|
72
80
|
foreignKeys,
|
|
@@ -92,7 +100,7 @@ const transformers = {
|
|
|
92
100
|
offset: expression,
|
|
93
101
|
on: onCondition,
|
|
94
102
|
// definitions, extensions, members ----------------------------------------
|
|
95
|
-
returns
|
|
103
|
+
returns, // storing the return type of actions
|
|
96
104
|
notNull: value,
|
|
97
105
|
default: expression,
|
|
98
106
|
// targetElement: ignore, // special display of foreign key, renameTo: select
|
|
@@ -177,7 +185,7 @@ const propertyOrder = (function orderPositions() {
|
|
|
177
185
|
|
|
178
186
|
// sync with definition in from-csn.js:
|
|
179
187
|
const typeProperties = [
|
|
180
|
-
'target', 'elements', 'enum', 'items',
|
|
188
|
+
'target', 'elements', 'enum', 'items', // TODO: notNull?
|
|
181
189
|
'type', 'length', 'precision', 'scale', 'srid', 'localized',
|
|
182
190
|
'foreignKeys', 'on', // for explicit ON/keys with REDIRECTED
|
|
183
191
|
];
|
|
@@ -196,7 +204,10 @@ const operators = {
|
|
|
196
204
|
when: exprs => [ 'when', ...exprs[0], 'then', ...exprs[1] ],
|
|
197
205
|
case: exprs => [ 'case' ].concat( ...exprs, [ 'end' ] ),
|
|
198
206
|
over: exprs => [ 'over', { xpr: [].concat( ...exprs ) } ],
|
|
199
|
-
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
|
|
200
211
|
'order', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
201
212
|
],
|
|
202
213
|
partitionBy: exprs => [
|
|
@@ -269,7 +280,7 @@ function sortCsn( csn, cloneOptions = false ) {
|
|
|
269
280
|
}
|
|
270
281
|
|
|
271
282
|
/**
|
|
272
|
-
* Check
|
|
283
|
+
* Check whether the given object has non enumerable property.
|
|
273
284
|
* Ensure that we don't take it from the prototype, only "directly" - we accidentally
|
|
274
285
|
* cloned elements with a cds.linked input otherwise.
|
|
275
286
|
*
|
|
@@ -548,7 +559,7 @@ function sources( srcDict, csn ) {
|
|
|
548
559
|
}
|
|
549
560
|
}
|
|
550
561
|
|
|
551
|
-
function attachAnnotations( annotate, prop, dict, inferred,
|
|
562
|
+
function attachAnnotations( annotate, prop, dict, inferred, insideReturns = false ) {
|
|
552
563
|
const annoDict = Object.create( dictionaryPrototype );
|
|
553
564
|
for (const name in dict) {
|
|
554
565
|
const elem = dict[name];
|
|
@@ -566,7 +577,7 @@ function attachAnnotations( annotate, prop, dict, inferred, returns = false ) {
|
|
|
566
577
|
annoDict[name] = sub;
|
|
567
578
|
}
|
|
568
579
|
if (Object.keys( annoDict ).length) {
|
|
569
|
-
if (
|
|
580
|
+
if (insideReturns)
|
|
570
581
|
annotate.returns = { elements: annoDict };
|
|
571
582
|
else
|
|
572
583
|
annotate[prop] = annoDict;
|
|
@@ -609,10 +620,18 @@ function set( prop, csn, node ) {
|
|
|
609
620
|
}
|
|
610
621
|
|
|
611
622
|
function targetAspect( val, csn, node ) {
|
|
623
|
+
if (universalCsn) {
|
|
624
|
+
if (val.$inferred)
|
|
625
|
+
return undefined;
|
|
626
|
+
if (node.target) {
|
|
627
|
+
csn.$origin = { target: (val.elements) ? standard( val ) : artifactRef( val, true ) };
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
612
631
|
const ta = (val.elements)
|
|
613
632
|
? addLocation( val.location, standard( val ) )
|
|
614
633
|
: artifactRef( val, true );
|
|
615
|
-
if (!gensrcFlavor || node.target && !node.target.$inferred)
|
|
634
|
+
if (!gensrcFlavor && !universalCsn || node.target && !node.target.$inferred)
|
|
616
635
|
return ta;
|
|
617
636
|
// For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
|
|
618
637
|
csn.target = ta;
|
|
@@ -624,7 +643,7 @@ function target( val, _csn, node ) {
|
|
|
624
643
|
val = node._origin.target;
|
|
625
644
|
if (val.elements)
|
|
626
645
|
return standard( val ); // elements in target (parse-cdl)
|
|
627
|
-
if (!universalCsn || node.on)
|
|
646
|
+
if (!universalCsn || gensrcFlavor || node.on)
|
|
628
647
|
return artifactRef( val, true );
|
|
629
648
|
const tref = artifactRef( val, true );
|
|
630
649
|
const proto = node.type && !node.type.$inferred ? node.type._artifact : node._origin;
|
|
@@ -655,19 +674,15 @@ function enumDict( dict, csn, node ) {
|
|
|
655
674
|
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
656
675
|
// hidden or visible 'elements' will be set in query()
|
|
657
676
|
return undefined;
|
|
658
|
-
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate'
|
|
677
|
+
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate' &&
|
|
678
|
+
node.type._artifact && !node.type._artifact.builtin)
|
|
659
679
|
// derived type of enum type with individual annotations: also set $origin
|
|
660
680
|
csn.$origin = originRef( node.type._artifact );
|
|
661
681
|
return insertOrderDict( dict );
|
|
662
682
|
}
|
|
663
683
|
|
|
664
684
|
function enumerableQueryElements( select ) {
|
|
665
|
-
|
|
666
|
-
return false;
|
|
667
|
-
if (select.orderBy || select.$orderBy)
|
|
668
|
-
return true;
|
|
669
|
-
const alias = select._parent;
|
|
670
|
-
return alias.query && (alias.query._leadingQuery || alias.query) === select;
|
|
685
|
+
return (universalCsn && select !== select._main._leadingQuery);
|
|
671
686
|
}
|
|
672
687
|
|
|
673
688
|
// Should we render the elements? (and items?)
|
|
@@ -746,7 +761,8 @@ function ignore() { /* no-op: ignore property */ }
|
|
|
746
761
|
|
|
747
762
|
function location( loc, csn, xsn ) {
|
|
748
763
|
if (xsn.kind && xsn.kind.charAt(0) !== '$' && xsn.kind !== 'select' &&
|
|
749
|
-
(!xsn.$inferred || !xsn._main)
|
|
764
|
+
(!xsn.$inferred || !xsn._main) &&
|
|
765
|
+
xsn.$inferred !== 'REDIRECTED') { // TODO: also for 'select'
|
|
750
766
|
// Also include $location for elements in queries (if not via '*')
|
|
751
767
|
addLocation( xsn.name && xsn.name.location || loc, csn );
|
|
752
768
|
}
|
|
@@ -763,7 +779,7 @@ function addLocation( loc, csn ) {
|
|
|
763
779
|
// Remove endLine/endCol:
|
|
764
780
|
// Reasoning: $location is mostly attached to definitions/members but the name
|
|
765
781
|
// is often not the reason for an error or warning. So we gain little benefit for
|
|
766
|
-
// two more properties.
|
|
782
|
+
// two more properties. It is also an indication that the location is not exact.
|
|
767
783
|
const val = { file: loc.file, line: loc.line, col: loc.col };
|
|
768
784
|
Object.defineProperty( csn, '$location', {
|
|
769
785
|
value: val, configurable: true, writable: true, enumerable: withLocations,
|
|
@@ -791,6 +807,13 @@ function actions( dict ) {
|
|
|
791
807
|
: undefined;
|
|
792
808
|
}
|
|
793
809
|
|
|
810
|
+
function params( dict ) {
|
|
811
|
+
const keys = Object.keys( dict );
|
|
812
|
+
return (keys.length)
|
|
813
|
+
? insertOrderDict( dict )
|
|
814
|
+
: undefined;
|
|
815
|
+
}
|
|
816
|
+
|
|
794
817
|
function dictionary( dict, keys, prop ) {
|
|
795
818
|
const csn = Object.create( dictionaryPrototype );
|
|
796
819
|
for (const name of keys) {
|
|
@@ -802,8 +825,10 @@ function dictionary( dict, keys, prop ) {
|
|
|
802
825
|
}
|
|
803
826
|
|
|
804
827
|
function foreignKeys( dict, csn, node ) {
|
|
805
|
-
if (universalCsn
|
|
806
|
-
|
|
828
|
+
if (universalCsn) {
|
|
829
|
+
if (!target( node.target, csn, node ) || dict[$inferred] === 'keys')
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
807
832
|
if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
|
|
808
833
|
dict = node._origin.foreignKeys;
|
|
809
834
|
const keys = [];
|
|
@@ -817,6 +842,14 @@ function foreignKeys( dict, csn, node ) {
|
|
|
817
842
|
csn.keys = keys;
|
|
818
843
|
}
|
|
819
844
|
|
|
845
|
+
function returns( art, csn, _node, prop ) {
|
|
846
|
+
// TODO: currently, the `returns` structure might just have been created by the propagator
|
|
847
|
+
// if that is the case, there should be no reason to store it in universal CSN
|
|
848
|
+
if (universalCsn && art.$inferred === 'proxy')
|
|
849
|
+
return undefined;
|
|
850
|
+
return definition( art, csn, _node, prop );
|
|
851
|
+
}
|
|
852
|
+
|
|
820
853
|
function definition( art, _csn, _node, prop ) {
|
|
821
854
|
if (!art || typeof art !== 'object')
|
|
822
855
|
return undefined; // TODO: complain with strict
|
|
@@ -836,8 +869,8 @@ function definition( art, _csn, _node, prop ) {
|
|
|
836
869
|
delete c.elements;
|
|
837
870
|
c.returns = { elements: elems };
|
|
838
871
|
}
|
|
839
|
-
|
|
840
|
-
|
|
872
|
+
// precondition already fulfilled: art.kind !== 'key'
|
|
873
|
+
addOrigin( c, art, art );
|
|
841
874
|
return c;
|
|
842
875
|
}
|
|
843
876
|
|
|
@@ -850,7 +883,8 @@ function includesOrigin( includes, art ) {
|
|
|
850
883
|
for (const incl of includes.slice(1)) {
|
|
851
884
|
const aspect = incl._artifact;
|
|
852
885
|
for (const prop in aspect) {
|
|
853
|
-
if (prop.charAt(0) === '@'
|
|
886
|
+
if ((prop.charAt(0) === '@' || prop === 'doc') &&
|
|
887
|
+
(!art[prop] || art[prop].$inferred)) {
|
|
854
888
|
const anno = aspect[prop];
|
|
855
889
|
if (anno.val !== null)
|
|
856
890
|
// matererialize non-null annos (whether direct or inherited)
|
|
@@ -861,9 +895,23 @@ function includesOrigin( includes, art ) {
|
|
|
861
895
|
return (Object.keys( result ).length === 1) ? $origin : result;
|
|
862
896
|
}
|
|
863
897
|
|
|
864
|
-
function addOrigin( csn, xsn,
|
|
865
|
-
if (!universalCsn
|
|
898
|
+
function addOrigin( csn, xsn, node ) {
|
|
899
|
+
if (!universalCsn)
|
|
900
|
+
return;
|
|
901
|
+
if (hasExplicitProp( xsn.type, 'cast' )) {
|
|
902
|
+
const main = xsn._main || xsn;
|
|
903
|
+
let count = 0;
|
|
904
|
+
let source = xsn;
|
|
905
|
+
while (source && source._main === main) {
|
|
906
|
+
source = source.value && source.value._artifact;
|
|
907
|
+
++count;
|
|
908
|
+
}
|
|
909
|
+
if (count > 0 && source && source.kind !== 'builtin')
|
|
910
|
+
csn.$source = originRef( source, xsn );
|
|
911
|
+
else if (count > 1)
|
|
912
|
+
csn.$source = null;
|
|
866
913
|
return;
|
|
914
|
+
}
|
|
867
915
|
if (xsn._from) {
|
|
868
916
|
const source = xsn._from[0]._origin;
|
|
869
917
|
csn.$origin = originRef( source );
|
|
@@ -877,23 +925,22 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
877
925
|
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
878
926
|
return;
|
|
879
927
|
}
|
|
880
|
-
|
|
928
|
+
let origin = getOrigin( node );
|
|
929
|
+
if (xsn.$inferred === 'composition-entity') {
|
|
930
|
+
csn.$origin = originRef( origin, xsn );
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
else if (!isMember( xsn ) || xsn.kind === 'select') {
|
|
881
934
|
return;
|
|
882
935
|
}
|
|
936
|
+
// from here on: member:
|
|
883
937
|
const parent = getParent( xsn );
|
|
884
938
|
const parentOrigin = getOrigin( parent );
|
|
885
|
-
if (!
|
|
886
|
-
if (parentOrigin && (
|
|
939
|
+
if (!origin) {
|
|
940
|
+
if (parentOrigin && !(parent.enum && !parent.$origin && parent.type))
|
|
887
941
|
csn.$origin = null;
|
|
888
942
|
return;
|
|
889
943
|
}
|
|
890
|
-
// Skip all proxies which do not make it into the CSN, as there are no
|
|
891
|
-
// individual annotations or redirection targets on it:
|
|
892
|
-
while (origin._parent && origin._parent.$expand === 'origin')
|
|
893
|
-
origin = origin._origin || origin.type._artifact;
|
|
894
|
-
// The while loop is not only for the else case below: when setting implicit
|
|
895
|
-
// prototypes, it is important that we do not have to follow the prototypes of
|
|
896
|
-
// other object; we would need to ensure a right order to avoid issues otherwise.
|
|
897
944
|
if (parentOrigin === getParent( origin )) {
|
|
898
945
|
// implicit prototype or shortened reference
|
|
899
946
|
const { id } = origin.name || {};
|
|
@@ -902,16 +949,21 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
902
949
|
return;
|
|
903
950
|
}
|
|
904
951
|
if (origin.kind === 'mixin') {
|
|
905
|
-
|
|
906
|
-
csn
|
|
952
|
+
set( 'type', csn, origin );
|
|
953
|
+
set( 'cardinality', csn, origin );
|
|
954
|
+
// currently, target and on are always set - nothing to do here
|
|
907
955
|
return;
|
|
908
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;
|
|
909
961
|
const ref = originRef( origin, xsn );
|
|
910
962
|
if (ref) {
|
|
911
963
|
csn.$origin = ref;
|
|
912
964
|
return;
|
|
913
965
|
}
|
|
914
|
-
// An element of a query with a query in FROM:
|
|
966
|
+
// An element of a query with a query in FROM: -----------------------------
|
|
915
967
|
const anon = definition( origin ); // use $origin: {...} if necessary
|
|
916
968
|
// as there are no implicit $origin prototypes on sub query elements (yet),
|
|
917
969
|
// we do not have to care about $origin not being set
|
|
@@ -919,15 +971,26 @@ function addOrigin( csn, xsn, origin ) {
|
|
|
919
971
|
if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
|
|
920
972
|
// repeated anon: flatten
|
|
921
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];
|
|
922
986
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
addOrigin( csn, xsn, origin._origin );
|
|
987
|
+
if (Object.keys( annos ).length) {
|
|
988
|
+
if (!csn.type && $origin)
|
|
989
|
+
annos.$origin = $origin;
|
|
990
|
+
csn.$origin = annos;
|
|
928
991
|
}
|
|
929
|
-
else {
|
|
930
|
-
csn
|
|
992
|
+
else if (!csn.type) {
|
|
993
|
+
addOrigin( csn, xsn, origin );
|
|
931
994
|
}
|
|
932
995
|
}
|
|
933
996
|
|
|
@@ -937,10 +1000,27 @@ function getParent( art ) {
|
|
|
937
1000
|
return (main && parent === main._leadingQuery) ? main : parent;
|
|
938
1001
|
}
|
|
939
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)
|
|
940
1017
|
function getOrigin( art ) {
|
|
941
|
-
if (art
|
|
942
|
-
return
|
|
943
|
-
|
|
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' ))
|
|
944
1024
|
return art.type._artifact;
|
|
945
1025
|
if (art.includes)
|
|
946
1026
|
return art.includes[0]._artifact;
|
|
@@ -949,36 +1029,38 @@ function getOrigin( art ) {
|
|
|
949
1029
|
return undefined;
|
|
950
1030
|
}
|
|
951
1031
|
|
|
952
|
-
function hasExplicitProp( ref ) {
|
|
953
|
-
return ref && !ref.$inferred;
|
|
1032
|
+
function hasExplicitProp( ref, alsoLikeExplicit ) {
|
|
1033
|
+
return ref && (!ref.$inferred || ref.$inferred === alsoLikeExplicit );
|
|
954
1034
|
}
|
|
955
1035
|
|
|
956
1036
|
function originRef( art, user ) {
|
|
957
1037
|
const r = [];
|
|
958
|
-
// do not use name.element, as we allow `.`s in name
|
|
1038
|
+
// do not use name.element, as we might allow `.`s in name
|
|
959
1039
|
let parent = art;
|
|
960
|
-
|
|
1040
|
+
if (parent._outer && parent.kind === 'aspect')
|
|
1041
|
+
r.push( { target: true } );
|
|
1042
|
+
while (isMember( parent ) && parent.kind !== 'select') {
|
|
961
1043
|
const nkind = normalizedKind[parent.kind];
|
|
962
|
-
|
|
1044
|
+
const name = parent.name || parent._outer.name;
|
|
1045
|
+
if (name.id || !r.length)
|
|
963
1046
|
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
964
1047
|
// eslint-disable-next-line no-nested-ternary, max-len
|
|
965
|
-
r.push( !nkind ?
|
|
1048
|
+
r.push( !nkind ? name.id : name.id ? { [nkind]: name.id } : { returns: true } );
|
|
966
1049
|
parent = parent._parent;
|
|
967
1050
|
}
|
|
968
1051
|
if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
|
|
969
1052
|
// well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
|
|
970
|
-
return
|
|
1053
|
+
return false; // do not write, probably use $origin: {...}
|
|
971
1054
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
972
1055
|
|
|
973
|
-
|
|
974
|
-
if (r.length === 1 && normalizedKind[art.kind] === 'action')
|
|
975
|
-
return [ art.name.absolute, art.name.id ];
|
|
976
|
-
r.push( art.name.absolute );
|
|
1056
|
+
r.push( parent.name.absolute );
|
|
977
1057
|
r.reverse();
|
|
978
1058
|
return r;
|
|
979
1059
|
}
|
|
980
1060
|
|
|
981
1061
|
function kind( k, csn, node ) {
|
|
1062
|
+
if (node.$inferred === 'REDIRECTED')
|
|
1063
|
+
return;
|
|
982
1064
|
if (k === 'annotate' || k === 'extend') {
|
|
983
1065
|
// We just use `name.absolute` because it is very likely a "constructed"
|
|
984
1066
|
// extensions. The CSN parser must produce name.path like for other refs.
|
|
@@ -987,20 +1069,44 @@ function kind( k, csn, node ) {
|
|
|
987
1069
|
else if (k === 'extend')
|
|
988
1070
|
csn.kind = k;
|
|
989
1071
|
}
|
|
1072
|
+
else if (k === 'action' && node._main && universalCsn && node.$inferred) {
|
|
1073
|
+
// Universal CSN: do not mention kind: 'action' on expanded action
|
|
1074
|
+
}
|
|
1075
|
+
else if (k === 'aspect' && (node._outer || node.$inferred)) {
|
|
1076
|
+
return; // do not show kind for anonymous aspect
|
|
1077
|
+
}
|
|
990
1078
|
else if (![
|
|
991
1079
|
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
992
1080
|
'$tableAlias', 'annotation', 'mixin',
|
|
993
1081
|
].includes(k)) {
|
|
994
1082
|
csn.kind = k;
|
|
995
1083
|
}
|
|
1084
|
+
const generated = universalCsn && inferredAsGenerated[node.$inferred];
|
|
1085
|
+
if (typeof generated === 'string')
|
|
1086
|
+
csn.$generated = generated;
|
|
996
1087
|
}
|
|
997
1088
|
|
|
998
1089
|
function type( node, csn, xsn ) {
|
|
999
|
-
if (universalCsn
|
|
1090
|
+
if (!universalCsn)
|
|
1091
|
+
return artifactRef( node, !node.$extra );
|
|
1092
|
+
if (node.$inferred && node.$inferred !== 'cast')
|
|
1000
1093
|
return undefined;
|
|
1094
|
+
if (xsn._origin) {
|
|
1095
|
+
if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
|
|
1096
|
+
csn.$origin = definition( xsn._origin );
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1001
1099
|
return artifactRef( node, !node.$extra );
|
|
1002
1100
|
}
|
|
1003
1101
|
|
|
1102
|
+
function cardinality( node ) {
|
|
1103
|
+
if (!universalCsn)
|
|
1104
|
+
return standard( node );
|
|
1105
|
+
if (node.$inferred)
|
|
1106
|
+
return undefined;
|
|
1107
|
+
return standard( node );
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1004
1110
|
function artifactRef( node, terse ) {
|
|
1005
1111
|
// When called as transformer function, a CSN node is provided as argument
|
|
1006
1112
|
// for `terse`, i.e. it is usually truthy, except for FROM
|
|
@@ -1104,8 +1210,12 @@ function value( node ) {
|
|
|
1104
1210
|
// "Short" value form, e.g. for annotation assignments
|
|
1105
1211
|
if (!node)
|
|
1106
1212
|
return true; // `@aBool` short for `@aBool: true`
|
|
1107
|
-
if (universalCsn && node.$inferred
|
|
1108
|
-
|
|
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
|
+
}
|
|
1109
1219
|
if (node.$inferred && gensrcFlavor)
|
|
1110
1220
|
return undefined;
|
|
1111
1221
|
if (node.path) {
|
|
@@ -1117,7 +1227,7 @@ function value( node ) {
|
|
|
1117
1227
|
if (node.literal === 'array')
|
|
1118
1228
|
return node.val.map( value );
|
|
1119
1229
|
if (node.literal === 'token' && node.val === '...')
|
|
1120
|
-
return extra( { '...':
|
|
1230
|
+
return extra( { '...': !node.upTo || value( node.upTo ) } );
|
|
1121
1231
|
if (node.literal !== 'struct')
|
|
1122
1232
|
// no val (undefined) as true only for annotation values (and struct elem values)
|
|
1123
1233
|
return node.name && !('val' in node) || node.val;
|
|
@@ -1274,7 +1384,7 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
|
1274
1384
|
if (node.op.val === 'SELECT') {
|
|
1275
1385
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
1276
1386
|
node.from && node.from.path && !projectionAsQuery) {
|
|
1277
|
-
csn.projection = standard( node );
|
|
1387
|
+
csn.projection = addLocation( node.location, standard( node ) );
|
|
1278
1388
|
return undefined;
|
|
1279
1389
|
}
|
|
1280
1390
|
const select = { SELECT: extra( standard( node ), node, expectedParens ) };
|
|
@@ -1296,6 +1406,9 @@ function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
|
1296
1406
|
gensrcFlavor = gensrcSaved;
|
|
1297
1407
|
}
|
|
1298
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 );
|
|
1299
1412
|
return addLocation( node.location, select );
|
|
1300
1413
|
}
|
|
1301
1414
|
const union = {};
|
|
@@ -1344,11 +1457,13 @@ function from( node ) {
|
|
|
1344
1457
|
else if (node.query) {
|
|
1345
1458
|
return addExplicitAs( query( node.query, null, null, null, 1 ), node.name );
|
|
1346
1459
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
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;
|
|
1352
1467
|
const dot = name.lastIndexOf('.');
|
|
1353
1468
|
return name.substring( dot + 1 ) !== id;
|
|
1354
1469
|
}), node );
|
|
@@ -1397,7 +1512,7 @@ function addElementAsColumn( elem, cols ) {
|
|
|
1397
1512
|
}
|
|
1398
1513
|
|
|
1399
1514
|
function orderBy( node ) {
|
|
1400
|
-
const expr = expression( node
|
|
1515
|
+
const expr = expression( node );
|
|
1401
1516
|
if (node.sort)
|
|
1402
1517
|
expr.sort = node.sort.val;
|
|
1403
1518
|
if (node.nulls)
|
|
@@ -1470,6 +1585,9 @@ function compactExpr( e ) { // TODO: options
|
|
|
1470
1585
|
return e && expression( e, true );
|
|
1471
1586
|
}
|
|
1472
1587
|
|
|
1588
|
+
/**
|
|
1589
|
+
* @param {CSN.Options} options
|
|
1590
|
+
*/
|
|
1473
1591
|
function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
1474
1592
|
gensrcFlavor = options.parseCdl || options.csnFlavor === 'gensrc' ||
|
|
1475
1593
|
options.toCsn && options.toCsn.flavor === 'gensrc';
|
|
@@ -112,7 +112,7 @@ const rules = {
|
|
|
112
112
|
expr: { func: 'conditionEOF', returns: 'cond' }, // yes, condition
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
-
function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions, rule = 'cdl' ) {
|
|
115
|
+
function parse( source, filename = '<undefined>.cds', options = {}, messageFunctions = null, rule = 'cdl' ) {
|
|
116
116
|
const lexer = new Lexer( new antlr4.InputStream(source) );
|
|
117
117
|
const tokenStream = new RewriteTypeTokenStream(lexer);
|
|
118
118
|
/** @type {object} */
|
|
@@ -166,8 +166,8 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
|
|
|
166
166
|
// Do not warn if docComments are explicitly disabled.
|
|
167
167
|
if (options.docComment !== false) {
|
|
168
168
|
for (const token of tokenStream.tokens) {
|
|
169
|
-
if (token.
|
|
170
|
-
messageFunctions.info('syntax-ignoring-doc-comment', parser.
|
|
169
|
+
if (token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
170
|
+
messageFunctions.info('syntax-ignoring-doc-comment', parser.tokenLocation(token), {},
|
|
171
171
|
"Ignoring doc-comment as it does not belong to any artifact");
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -69,7 +69,7 @@ function parseDocComment(comment) {
|
|
|
69
69
|
* @param {string} content
|
|
70
70
|
*/
|
|
71
71
|
function isWhiteSpaceOnly(content) {
|
|
72
|
-
return content
|
|
72
|
+
return /^\s*$/.test(content);
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -127,8 +127,8 @@ function removeFooterFence(line) {
|
|
|
127
127
|
* @param {string[]} lines
|
|
128
128
|
*/
|
|
129
129
|
function isFencedComment(lines) {
|
|
130
|
-
const index = lines.findIndex((line,
|
|
131
|
-
const exclude = (
|
|
130
|
+
const index = lines.findIndex((line, i) => {
|
|
131
|
+
const exclude = (i === 0 || i === lines.length - 1);
|
|
132
132
|
return !exclude && !(/^\s*[*]/.test(line));
|
|
133
133
|
});
|
|
134
134
|
return index === -1 && lines.length > 2;
|
|
@@ -114,9 +114,10 @@ function sync( recognizer ) {
|
|
|
114
114
|
}
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
// TODO: expected token is identifier, current is KEYWORD
|
|
118
117
|
|
|
119
118
|
if (nextTokens.contains(antlr4.Token.EPSILON)) {
|
|
119
|
+
// when exiting a (innermost) rule, remember the state to make
|
|
120
|
+
// getExpectedTokensForMessage() calculate the full "expected set"
|
|
120
121
|
if (recognizer.$nextTokensToken !== token) {
|
|
121
122
|
// console.log('SET:',token.type,recognizer.state,recognizer.$nextTokensToken && recognizer.$nextTokensToken.type)
|
|
122
123
|
recognizer.$nextTokensToken = token;
|
|
@@ -126,13 +127,29 @@ function sync( recognizer ) {
|
|
|
126
127
|
return;
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
// Expected token is identifier, current is (reserved) KEYWORD:
|
|
131
|
+
// TODO: do not use this if "close enough" (1 char diff) to a keyword in nextTokens
|
|
132
|
+
//
|
|
133
|
+
// NOTE: it is important to do this only if EPSILON is not in `nextTokens`,
|
|
134
|
+
// which means that we cannot bring the better special syntax-fragile-ident
|
|
135
|
+
// in all cases. Reason: high performance impact of the alternative,
|
|
136
|
+
// i.e. calling method Parser#isExpectedToken() = invoking the ATN
|
|
137
|
+
// interpreter to see behind EPSILON.
|
|
138
|
+
const identType = recognizer.constructor.Identifier;
|
|
139
|
+
if (keywordRegexp.test( token.text ) && nextTokens.contains( identType )) {
|
|
140
|
+
recognizer.message( 'syntax-fragile-ident', token, { id: token.text, delimited: token.text },
|
|
141
|
+
'$(ID) is a reserved name here - write $(DELIMITED) instead if you want to use it' );
|
|
142
|
+
token.type = identType; // make next ANTLR decision assume identifier
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
129
146
|
if (recognizer._ctx._sync === 'nop')
|
|
130
147
|
return;
|
|
131
148
|
switch (s.stateType) {
|
|
132
|
-
case ATNState.BLOCK_START:
|
|
133
|
-
case ATNState.STAR_BLOCK_START:
|
|
134
|
-
case ATNState.PLUS_BLOCK_START:
|
|
135
|
-
case ATNState.STAR_LOOP_ENTRY:
|
|
149
|
+
case ATNState.BLOCK_START: // 3
|
|
150
|
+
case ATNState.STAR_BLOCK_START: // 5
|
|
151
|
+
case ATNState.PLUS_BLOCK_START: // 4
|
|
152
|
+
case ATNState.STAR_LOOP_ENTRY: // 10
|
|
136
153
|
// report error and recover if possible
|
|
137
154
|
if ( token.text !== '}' && // do not just delete a '}'
|
|
138
155
|
this.singleTokenDeletion(recognizer) !== null) { // also calls reportUnwantedToken
|
|
@@ -145,8 +162,8 @@ function sync( recognizer ) {
|
|
|
145
162
|
}
|
|
146
163
|
throw new InputMismatchException(recognizer);
|
|
147
164
|
|
|
148
|
-
case ATNState.PLUS_LOOP_BACK:
|
|
149
|
-
case ATNState.STAR_LOOP_BACK: {
|
|
165
|
+
case ATNState.PLUS_LOOP_BACK: // 11
|
|
166
|
+
case ATNState.STAR_LOOP_BACK: { // 9
|
|
150
167
|
// TODO: do not delete a '}'
|
|
151
168
|
this.reportUnwantedToken(recognizer);
|
|
152
169
|
const expecting = new IntervalSet.IntervalSet();
|
|
@@ -425,7 +442,8 @@ function getExpectedTokensForMessage( recognizer, offendingToken, deadEnds ) {
|
|
|
425
442
|
}
|
|
426
443
|
else if (offendingToken && recognizer.$nextTokensContext &&
|
|
427
444
|
offendingToken === recognizer.$nextTokensToken) {
|
|
428
|
-
//
|
|
445
|
+
// Before exiting a rule, we had a state (via sync()) with a bigger
|
|
446
|
+
// "expecting set" for the same token
|
|
429
447
|
ll1._LOOK( atn.states[recognizer.$nextTokensState], null,
|
|
430
448
|
predictionContext( atn, recognizer.$nextTokensContext ),
|
|
431
449
|
expected, lookBusy, calledRules, true, true );
|