@sap/cds-compiler 2.10.2 → 2.11.4
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 +90 -5
- package/bin/.eslintrc.json +1 -2
- package/bin/cds_update_identifiers.js +3 -1
- package/bin/cdsc.js +49 -25
- package/bin/cdsse.js +1 -0
- package/bin/cdsv2m.js +3 -2
- package/doc/CHANGELOG_BETA.md +10 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +8 -36
- package/lib/api/options.js +15 -6
- package/lib/api/validate.js +30 -3
- package/lib/backends.js +12 -13
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +3 -2
- package/lib/base/message-registry.js +34 -10
- package/lib/base/messages.js +38 -18
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +57 -23
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +9 -2
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +62 -16
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +66 -108
- package/lib/compiler/index.js +29 -29
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +225 -58
- package/lib/compiler/shared.js +53 -229
- package/lib/compiler/utils.js +184 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +34 -38
- package/lib/edm/edmUtils.js +3 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +17 -1
- package/lib/gen/language.tokens +79 -73
- package/lib/gen/languageLexer.interp +19 -1
- package/lib/gen/languageLexer.js +779 -731
- package/lib/gen/languageLexer.tokens +71 -65
- package/lib/gen/languageParser.js +4668 -4072
- package/lib/json/from-csn.js +10 -10
- package/lib/json/to-csn.js +228 -47
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/errorStrategy.js +26 -8
- package/lib/language/genericAntlrParser.js +73 -14
- package/lib/language/language.g4 +79 -3
- package/lib/main.d.ts +215 -18
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +117 -33
- package/lib/model/csnUtils.js +65 -133
- package/lib/model/enrichCsn.js +62 -37
- package/lib/model/revealInternalProperties.js +25 -8
- package/lib/model/sortViews.js +8 -1
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +33 -18
- package/lib/render/.eslintrc.json +1 -2
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +15 -8
- package/lib/render/toHdbcds.js +26 -49
- package/lib/render/toSql.js +61 -39
- package/lib/render/utils/common.js +1 -1
- package/lib/transform/db/applyTransformations.js +189 -0
- package/lib/transform/db/constraints.js +273 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +19 -3
- package/lib/transform/db/transformExists.js +102 -9
- package/lib/transform/db/views.js +485 -0
- package/lib/transform/forHanaNew.js +93 -448
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/odata/structuralPath.js +1 -5
- package/lib/transform/transformUtilsNew.js +22 -8
- package/lib/transform/translateAssocsToJoins.js +7 -15
- package/lib/utils/file.js +11 -5
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/transform/db/helpers.js +0 -58
package/lib/json/from-csn.js
CHANGED
|
@@ -773,12 +773,8 @@ function arrayOf( fn, filter = undefined ) {
|
|
|
773
773
|
} );
|
|
774
774
|
const minLength = spec.minLength || 0;
|
|
775
775
|
if (minLength > val.length) {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
{
|
|
779
|
-
std: 'Expected array in $(PROP) to have at least $(N) items',
|
|
780
|
-
one: 'Expected array in $(PROP) to have at least one item',
|
|
781
|
-
} );
|
|
776
|
+
message( 'syntax-csn-expected-length', location(true),
|
|
777
|
+
{ prop: spec.prop, n: minLength, '#': minLength === 1 ? 'one' : 'std' });
|
|
782
778
|
}
|
|
783
779
|
if (val.length)
|
|
784
780
|
++virtualLine; // [] in one JSON line
|
|
@@ -913,7 +909,7 @@ function definition( def, spec, xsn, csn, name ) {
|
|
|
913
909
|
++virtualLine;
|
|
914
910
|
}
|
|
915
911
|
}
|
|
916
|
-
if (!r.name && name) {
|
|
912
|
+
if (!r.name && name != null) {
|
|
917
913
|
r.name = { id: name, location: r.location };
|
|
918
914
|
if (prop === 'columns' || prop === 'keys' || prop === 'foreignKeys')
|
|
919
915
|
r.name.$inferred = 'as';
|
|
@@ -1002,13 +998,13 @@ function selectItem( def, spec, xsn, csn ) {
|
|
|
1002
998
|
return definition( def, spec, xsn, csn, null ); // definer sets name
|
|
1003
999
|
}
|
|
1004
1000
|
|
|
1005
|
-
function returnsDefinition( def, spec, xsn, csn
|
|
1001
|
+
function returnsDefinition( def, spec, xsn, csn ) {
|
|
1006
1002
|
// TODO: be stricter in what is allowed inside returns
|
|
1007
1003
|
if (!inExtensions)
|
|
1008
|
-
return definition( def, spec, xsn, csn,
|
|
1004
|
+
return definition( def, spec, xsn, csn, '' );
|
|
1009
1005
|
// for the moment, flatten elements in returns in an annotate
|
|
1010
1006
|
// TODO: bigger Core Compiler changes would have to be done otherwise
|
|
1011
|
-
xsn.elements = definition( def, spec, xsn, csn,
|
|
1007
|
+
xsn.elements = definition( def, spec, xsn, csn, '' ).elements;
|
|
1012
1008
|
xsn.$syntax = 'returns';
|
|
1013
1009
|
return undefined;
|
|
1014
1010
|
}
|
|
@@ -1271,6 +1267,10 @@ function func( val, spec, xsn ) {
|
|
|
1271
1267
|
|
|
1272
1268
|
function xpr( exprs, spec, xsn, csn ) {
|
|
1273
1269
|
if (csn.func) {
|
|
1270
|
+
if (!exprs.length) {
|
|
1271
|
+
message( 'syntax-csn-expected-length', location(true),
|
|
1272
|
+
{ prop: 'xpr', otherprop: 'func', '#': 'suffix' });
|
|
1273
|
+
}
|
|
1274
1274
|
xsn.suffix = exprArgs( exprs, spec );
|
|
1275
1275
|
}
|
|
1276
1276
|
else {
|
package/lib/json/to-csn.js
CHANGED
|
@@ -33,12 +33,23 @@ let projectionAsQuery = false;
|
|
|
33
33
|
let withLocations = false;
|
|
34
34
|
let dictionaryPrototype = null;
|
|
35
35
|
|
|
36
|
+
// Properties for dictionaries, set in compileX() and TODO: parseX(), must be
|
|
37
|
+
// stored with symbols as keys, as we do not want to disallow any key name:
|
|
38
|
+
const $inferred = Symbol.for('cds.$inferred');
|
|
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
|
+
|
|
36
48
|
// IMPORTANT: the order of these properties determine the order of properties
|
|
37
49
|
// in the resulting CSN !!! Also check const `csnPropertyNames`.
|
|
38
50
|
const transformers = {
|
|
39
51
|
// early and modifiers (without null / not null) ---------------------------
|
|
40
52
|
kind,
|
|
41
|
-
_outer: ( _, csn, node ) => addOrigin( csn, node ),
|
|
42
53
|
id: n => n, // in path item
|
|
43
54
|
doc: value,
|
|
44
55
|
'@': value,
|
|
@@ -63,11 +74,11 @@ const transformers = {
|
|
|
63
74
|
precision: value,
|
|
64
75
|
scale: value,
|
|
65
76
|
srid: value,
|
|
66
|
-
cardinality
|
|
77
|
+
cardinality, // also in pathItem: after 'id', before 'where'
|
|
67
78
|
targetAspect,
|
|
68
79
|
target,
|
|
69
80
|
foreignKeys,
|
|
70
|
-
enum:
|
|
81
|
+
enum: enumDict,
|
|
71
82
|
items,
|
|
72
83
|
includes: arrayOf( artifactRef ), // also entities
|
|
73
84
|
// late expressions / query properties -------------------------------------
|
|
@@ -199,6 +210,15 @@ const operators = {
|
|
|
199
210
|
partitionBy: exprs => [
|
|
200
211
|
'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
201
212
|
],
|
|
213
|
+
rows: exprs => [
|
|
214
|
+
'rows', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
215
|
+
],
|
|
216
|
+
preceding: postfix( [ 'preceding' ] ),
|
|
217
|
+
unboundedPreceding: [ 'unbounded', 'preceding' ],
|
|
218
|
+
currentRow: [ 'current', 'row' ],
|
|
219
|
+
unboundedFollowing: [ 'unbounded', 'following' ],
|
|
220
|
+
following: postfix( [ 'following' ] ),
|
|
221
|
+
frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
|
|
202
222
|
// xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
|
|
203
223
|
};
|
|
204
224
|
|
|
@@ -597,10 +617,21 @@ function set( prop, csn, node ) {
|
|
|
597
617
|
}
|
|
598
618
|
|
|
599
619
|
function targetAspect( val, csn, node ) {
|
|
620
|
+
if (universalCsn) {
|
|
621
|
+
if (val.$inferred)
|
|
622
|
+
return undefined;
|
|
623
|
+
if (node.target) {
|
|
624
|
+
csn.$origin = { type: 'cds.Composition' };
|
|
625
|
+
if (node.cardinality)
|
|
626
|
+
csn.$origin.cardinality = standard( node.cardinality );
|
|
627
|
+
csn.$origin.target = (val.elements) ? standard( val ) : artifactRef( val, true );
|
|
628
|
+
return undefined;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
600
631
|
const ta = (val.elements)
|
|
601
632
|
? addLocation( val.location, standard( val ) )
|
|
602
633
|
: artifactRef( val, true );
|
|
603
|
-
if (!gensrcFlavor || node.target && !node.target.$inferred)
|
|
634
|
+
if (!gensrcFlavor && !universalCsn || node.target && !node.target.$inferred)
|
|
604
635
|
return ta;
|
|
605
636
|
// For compatibility, put aspect in 'target' with parse.cdl and csn flavor 'gensrc'
|
|
606
637
|
csn.target = ta;
|
|
@@ -622,7 +653,7 @@ function target( val, _csn, node ) {
|
|
|
622
653
|
}
|
|
623
654
|
|
|
624
655
|
function items( obj, csn, node ) {
|
|
625
|
-
if (!keepElements( node ))
|
|
656
|
+
if (!keepElements( node, obj ))
|
|
626
657
|
return undefined;
|
|
627
658
|
return standard( obj ); // no 'elements' with inferred elements with gensrc
|
|
628
659
|
}
|
|
@@ -637,23 +668,41 @@ function elements( dict, csn, node ) {
|
|
|
637
668
|
return insertOrderDict( dict );
|
|
638
669
|
}
|
|
639
670
|
|
|
671
|
+
function enumDict( dict, csn, node ) {
|
|
672
|
+
if (gensrcFlavor && dict[$inferred] ||
|
|
673
|
+
!keepElements( node ))
|
|
674
|
+
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
675
|
+
// hidden or visible 'elements' will be set in query()
|
|
676
|
+
return undefined;
|
|
677
|
+
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate')
|
|
678
|
+
// derived type of enum type with individual annotations: also set $origin
|
|
679
|
+
csn.$origin = originRef( node.type._artifact );
|
|
680
|
+
return insertOrderDict( dict );
|
|
681
|
+
}
|
|
682
|
+
|
|
640
683
|
function enumerableQueryElements( select ) {
|
|
641
|
-
|
|
642
|
-
return false;
|
|
643
|
-
if (select.orderBy || select.$orderBy)
|
|
644
|
-
return true;
|
|
645
|
-
const alias = select._parent;
|
|
646
|
-
return alias.query && (alias.query._leadingQuery || alias.query) === select;
|
|
684
|
+
return (universalCsn && select !== select._main._leadingQuery);
|
|
647
685
|
}
|
|
648
686
|
|
|
649
687
|
// Should we render the elements? (and items?)
|
|
650
|
-
function keepElements( node ) {
|
|
688
|
+
function keepElements( node, line ) {
|
|
651
689
|
if (universalCsn)
|
|
652
690
|
// $expand = null/undefined: not elements not via expansion
|
|
653
691
|
// $expand = 'target'/'annotate': with redirections / individual annotations
|
|
654
692
|
return node.$expand !== 'origin';
|
|
655
693
|
if (!node.type || node.kind === 'type')
|
|
656
694
|
return true;
|
|
695
|
+
// keep many SimpleType/Entity
|
|
696
|
+
if (line) {
|
|
697
|
+
if (!node.type)
|
|
698
|
+
return true;
|
|
699
|
+
const array = node.type._artifact; // see function items() in propagator.js
|
|
700
|
+
const ltype = line.type && line.type._artifact;
|
|
701
|
+
if (!array || // reference errors
|
|
702
|
+
array._main && !line.elements && !line.enum && !line.items && !line.notNull &&
|
|
703
|
+
(!ltype || !ltype._main)) // many Foo:bar -> not SimpleType
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
657
706
|
// even if expanded elements have no new target or direct annotation,
|
|
658
707
|
// they might have got one via propagation – any new target/annos during their
|
|
659
708
|
// way from the original structure type definition to the current usage
|
|
@@ -711,7 +760,8 @@ function ignore() { /* no-op: ignore property */ }
|
|
|
711
760
|
|
|
712
761
|
function location( loc, csn, xsn ) {
|
|
713
762
|
if (xsn.kind && xsn.kind.charAt(0) !== '$' && xsn.kind !== 'select' &&
|
|
714
|
-
(!xsn.$inferred || !xsn._main)
|
|
763
|
+
(!xsn.$inferred || !xsn._main) &&
|
|
764
|
+
xsn.$inferred !== 'REDIRECTED') { // TODO: also for 'select'
|
|
715
765
|
// Also include $location for elements in queries (if not via '*')
|
|
716
766
|
addLocation( xsn.name && xsn.name.location || loc, csn );
|
|
717
767
|
}
|
|
@@ -767,8 +817,10 @@ function dictionary( dict, keys, prop ) {
|
|
|
767
817
|
}
|
|
768
818
|
|
|
769
819
|
function foreignKeys( dict, csn, node ) {
|
|
770
|
-
if (universalCsn
|
|
771
|
-
|
|
820
|
+
if (universalCsn) {
|
|
821
|
+
if (!target( node.target, csn, node ) || dict[$inferred] === 'keys')
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
772
824
|
if (gensrcFlavor && node._origin && node._origin.$inferred === 'REDIRECTED')
|
|
773
825
|
dict = node._origin.foreignKeys;
|
|
774
826
|
const keys = [];
|
|
@@ -801,50 +853,151 @@ function definition( art, _csn, _node, prop ) {
|
|
|
801
853
|
delete c.elements;
|
|
802
854
|
c.returns = { elements: elems };
|
|
803
855
|
}
|
|
856
|
+
// precondition already fulfilled: art.kind !== 'key'
|
|
857
|
+
addOrigin( c, art, art._origin );
|
|
804
858
|
return c;
|
|
805
859
|
}
|
|
806
860
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
861
|
+
// create $origin specification for `includes` of `art`
|
|
862
|
+
function includesOrigin( includes, art ) {
|
|
863
|
+
const $origin = originRef( includes[0]._artifact );
|
|
864
|
+
if (includes.length === 1)
|
|
865
|
+
return $origin;
|
|
866
|
+
const result = { $origin };
|
|
867
|
+
for (const incl of includes.slice(1)) {
|
|
868
|
+
const aspect = incl._artifact;
|
|
869
|
+
for (const prop in aspect) {
|
|
870
|
+
if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
|
|
871
|
+
const anno = aspect[prop];
|
|
872
|
+
if (anno.val !== null)
|
|
873
|
+
// matererialize non-null annos (whether direct or inherited)
|
|
874
|
+
result[prop] = value( Object.create( anno, { $inferred: { value: null } } ) );
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return (Object.keys( result ).length === 1) ? $origin : result;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function addOrigin( csn, xsn, origin ) {
|
|
882
|
+
if (!universalCsn || hasExplicitProp( xsn.type ))
|
|
883
|
+
return;
|
|
810
884
|
if (xsn._from) {
|
|
811
|
-
|
|
885
|
+
const source = xsn._from[0]._origin;
|
|
886
|
+
csn.$origin = originRef( source );
|
|
887
|
+
if (source.params && !xsn.params)
|
|
888
|
+
csn.params = null; // discontinue `params` inheritance
|
|
889
|
+
if (source.actions && !xsn.actions)
|
|
890
|
+
csn.actions = null; // discontinue `actions` inheritance
|
|
891
|
+
return;
|
|
812
892
|
}
|
|
813
|
-
else if (xsn.includes
|
|
814
|
-
csn.$origin =
|
|
893
|
+
else if (xsn.includes) {
|
|
894
|
+
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
895
|
+
return;
|
|
815
896
|
}
|
|
816
|
-
else if (
|
|
817
|
-
|
|
818
|
-
while (origin._parent && origin._parent.$expand === 'origin')
|
|
819
|
-
origin = origin._origin || origin.type._artifact;
|
|
820
|
-
csn.$origin = originRef( origin );
|
|
897
|
+
else if (!xsn._main || xsn.kind === 'select') {
|
|
898
|
+
return;
|
|
821
899
|
}
|
|
822
|
-
|
|
900
|
+
const parent = getParent( xsn );
|
|
901
|
+
const parentOrigin = getOrigin( parent );
|
|
902
|
+
if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
|
|
903
|
+
if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
|
|
904
|
+
csn.$origin = null;
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
// Skip all proxies which do not make it into the CSN, as there are no
|
|
908
|
+
// individual annotations or redirection targets on it:
|
|
909
|
+
while (origin._parent && origin._parent.$expand === 'origin')
|
|
910
|
+
origin = origin._origin || origin.type._artifact;
|
|
911
|
+
// The while loop is not only for the else case below: when setting implicit
|
|
912
|
+
// prototypes, it is important that we do not have to follow the prototypes of
|
|
913
|
+
// other object; we would need to ensure a right order to avoid issues otherwise.
|
|
914
|
+
if (parentOrigin === getParent( origin )) {
|
|
915
|
+
// implicit prototype or shortened reference
|
|
916
|
+
const { id } = origin.name || {};
|
|
917
|
+
if (id && xsn.name && id !== xsn.name.id)
|
|
918
|
+
csn.$origin = id;
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (origin.kind === 'mixin') {
|
|
922
|
+
// currently, target and on are always set - nothing to do here, just set type
|
|
923
|
+
csn.type = 'cds.Association';
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const ref = originRef( origin, xsn );
|
|
927
|
+
if (ref) {
|
|
928
|
+
csn.$origin = ref;
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
// An element of a query with a query in FROM:
|
|
932
|
+
const anon = definition( origin ); // use $origin: {...} if necessary
|
|
933
|
+
// as there are no implicit $origin prototypes on sub query elements (yet),
|
|
934
|
+
// we do not have to care about $origin not being set
|
|
935
|
+
const { $origin } = anon;
|
|
936
|
+
if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
|
|
937
|
+
// repeated anon: flatten
|
|
938
|
+
csn.$origin = Object.assign( $origin, anon );
|
|
939
|
+
}
|
|
940
|
+
else if (Object.keys( anon )
|
|
941
|
+
// (we can use the properties in `csn`, because addOrigin() is called last)
|
|
942
|
+
.every( p => p in csn || p === '$origin' || p === '$location')) {
|
|
943
|
+
// nothing new in $origin: {...}
|
|
944
|
+
addOrigin( csn, xsn, origin._origin );
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
csn.$origin = anon;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function getParent( art ) {
|
|
952
|
+
const parent = art._parent;
|
|
953
|
+
const main = parent._main;
|
|
954
|
+
return (main && parent === main._leadingQuery) ? main : parent;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
function getOrigin( art ) {
|
|
958
|
+
if (art._origin)
|
|
959
|
+
return art._origin;
|
|
960
|
+
if (hasExplicitProp( art.type ))
|
|
961
|
+
return art.type._artifact;
|
|
962
|
+
if (art.includes)
|
|
963
|
+
return art.includes[0]._artifact;
|
|
964
|
+
if (art._from)
|
|
965
|
+
return art._from[0]._origin;
|
|
966
|
+
return undefined;
|
|
823
967
|
}
|
|
824
968
|
|
|
825
969
|
function hasExplicitProp( ref ) {
|
|
826
970
|
return ref && !ref.$inferred;
|
|
827
971
|
}
|
|
828
972
|
|
|
829
|
-
function originRef( art ) {
|
|
973
|
+
function originRef( art, user ) {
|
|
830
974
|
const r = [];
|
|
831
975
|
// do not use name.element, as we allow `.`s in name
|
|
832
|
-
let
|
|
833
|
-
while (
|
|
834
|
-
const nkind = normalizedKind[
|
|
835
|
-
if (
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
976
|
+
let parent = art;
|
|
977
|
+
while (parent._main && parent.kind !== 'select') {
|
|
978
|
+
const nkind = normalizedKind[parent.kind];
|
|
979
|
+
if (parent.name.id || !r.length)
|
|
980
|
+
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
981
|
+
// eslint-disable-next-line no-nested-ternary, max-len
|
|
982
|
+
r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
|
|
983
|
+
parent = parent._parent;
|
|
984
|
+
}
|
|
985
|
+
if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
|
|
986
|
+
// well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
|
|
987
|
+
return null; // probably use $origin: {...}
|
|
841
988
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
989
|
+
|
|
990
|
+
// Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
|
|
991
|
+
if (r.length === 1 && normalizedKind[art.kind] === 'action')
|
|
992
|
+
return [ art.name.absolute, art.name.id ];
|
|
842
993
|
r.push( art.name.absolute );
|
|
843
994
|
r.reverse();
|
|
844
995
|
return r;
|
|
845
996
|
}
|
|
846
997
|
|
|
847
998
|
function kind( k, csn, node ) {
|
|
999
|
+
if (node.$inferred === 'REDIRECTED')
|
|
1000
|
+
return;
|
|
848
1001
|
if (k === 'annotate' || k === 'extend') {
|
|
849
1002
|
// We just use `name.absolute` because it is very likely a "constructed"
|
|
850
1003
|
// extensions. The CSN parser must produce name.path like for other refs.
|
|
@@ -853,22 +1006,47 @@ function kind( k, csn, node ) {
|
|
|
853
1006
|
else if (k === 'extend')
|
|
854
1007
|
csn.kind = k;
|
|
855
1008
|
}
|
|
856
|
-
else {
|
|
857
|
-
|
|
858
|
-
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
859
|
-
'$tableAlias', 'annotation', 'mixin',
|
|
860
|
-
].includes(k))
|
|
861
|
-
csn.kind = k;
|
|
862
|
-
addOrigin( csn, node );
|
|
1009
|
+
else if (k === 'action' && node._main && universalCsn && node.$inferred) {
|
|
1010
|
+
// Universal CSN: do not mention kind: 'action' on expanded action
|
|
863
1011
|
}
|
|
1012
|
+
else if (![
|
|
1013
|
+
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
1014
|
+
'$tableAlias', 'annotation', 'mixin',
|
|
1015
|
+
].includes(k)) {
|
|
1016
|
+
csn.kind = k;
|
|
1017
|
+
}
|
|
1018
|
+
const generated = universalCsn && inferredAsGenerated[node.$inferred];
|
|
1019
|
+
if (typeof generated === 'string')
|
|
1020
|
+
csn.$generated = generated;
|
|
864
1021
|
}
|
|
865
1022
|
|
|
866
1023
|
function type( node, csn, xsn ) {
|
|
867
|
-
if (universalCsn
|
|
1024
|
+
if (!universalCsn)
|
|
1025
|
+
return artifactRef( node, !node.$extra );
|
|
1026
|
+
if (node.$inferred)
|
|
1027
|
+
return undefined;
|
|
1028
|
+
if (xsn._origin) {
|
|
1029
|
+
if (xsn._origin.$inferred === 'REDIRECTED') { // auto-redirected user-provided target
|
|
1030
|
+
csn.$origin = definition( xsn._origin );
|
|
1031
|
+
return undefined;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
else if ( xsn.targetAspect && xsn.target ) {
|
|
1035
|
+
// type moved to $origin: { type: … }
|
|
868
1036
|
return undefined;
|
|
1037
|
+
}
|
|
869
1038
|
return artifactRef( node, !node.$extra );
|
|
870
1039
|
}
|
|
871
1040
|
|
|
1041
|
+
function cardinality( node, csn, xsn ) {
|
|
1042
|
+
if (!universalCsn)
|
|
1043
|
+
return standard( node );
|
|
1044
|
+
// cardinality might be moved to $origin: { cardinality: … }
|
|
1045
|
+
if (node.$inferred || xsn.targetAspect && !xsn.targetAspect.$inferred && xsn.target)
|
|
1046
|
+
return undefined;
|
|
1047
|
+
return standard( node );
|
|
1048
|
+
}
|
|
1049
|
+
|
|
872
1050
|
function artifactRef( node, terse ) {
|
|
873
1051
|
// When called as transformer function, a CSN node is provided as argument
|
|
874
1052
|
// for `terse`, i.e. it is usually truthy, except for FROM
|
|
@@ -997,7 +1175,10 @@ function value( node ) {
|
|
|
997
1175
|
|
|
998
1176
|
function enumValue( v, csn, node ) {
|
|
999
1177
|
// Enums can have values but if enums are extended, their kind is 'element',
|
|
1000
|
-
// so we check whether the node is inside an extension.
|
|
1178
|
+
// so we check whether the node is inside an extension. (TODO: still?)
|
|
1179
|
+
if (universalCsn && v.$inferred)
|
|
1180
|
+
return;
|
|
1181
|
+
// (with gensrc, the symbol itself would not make it into the CSN)
|
|
1001
1182
|
if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
|
|
1002
1183
|
Object.assign( csn, expression( v, true ) );
|
|
1003
1184
|
}
|
|
@@ -1185,7 +1366,7 @@ function columns( xsnColumns, csn, xsn ) {
|
|
|
1185
1366
|
addElementAsColumn( col, csnColumns );
|
|
1186
1367
|
}
|
|
1187
1368
|
}
|
|
1188
|
-
else {
|
|
1369
|
+
else { // null = use elements - TODO: still used by A2J? -> remove
|
|
1189
1370
|
for (const name in xsn.elements)
|
|
1190
1371
|
addElementAsColumn( xsn.elements[name], csnColumns );
|
|
1191
1372
|
}
|
|
@@ -162,6 +162,17 @@ function parse( source, filename = '<undefined>.cds', options = {}, messageFunct
|
|
|
162
162
|
if (rulespec.$frontend)
|
|
163
163
|
ast.$frontend = rulespec.$frontend;
|
|
164
164
|
|
|
165
|
+
// Warn about unused doc-comments.
|
|
166
|
+
// Do not warn if docComments are explicitly disabled.
|
|
167
|
+
if (options.docComment !== false) {
|
|
168
|
+
for (const token of tokenStream.tokens) {
|
|
169
|
+
if (token.channel === antlr4.Token.HIDDEN_CHANNEL && token.type === parser.constructor.DocComment && !token.isUsed) {
|
|
170
|
+
messageFunctions.info('syntax-ignoring-doc-comment', parser.multiLineTokenLocation(token), {},
|
|
171
|
+
"Ignoring doc-comment as it does not belong to any artifact");
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
165
176
|
// TODO: clarify with LSP colleagues: still necessary?
|
|
166
177
|
if (parser.messages) {
|
|
167
178
|
Object.defineProperty( ast, 'messages',
|
|
@@ -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 );
|