@sap/cds-compiler 2.10.4 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/bin/cdsc.js +42 -25
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +4 -0
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +9 -23
- package/lib/api/options.js +12 -4
- package/lib/api/validate.js +23 -2
- package/lib/backends.js +9 -8
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/message-registry.js +10 -2
- package/lib/base/messages.js +23 -9
- package/lib/base/model.js +5 -4
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/selectItems.js +4 -0
- package/lib/checks/unknownMagic.js +6 -3
- package/lib/compiler/assert-consistency.js +7 -0
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +28 -1
- package/lib/compiler/checks.js +2 -1
- package/lib/compiler/definer.js +58 -91
- package/lib/compiler/index.js +16 -4
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +93 -34
- package/lib/compiler/shared.js +29 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +1 -1
- package/lib/edm/csn2edm.js +3 -2
- package/lib/edm/edmPreprocessor.js +31 -36
- 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 +169 -34
- package/lib/language/antlrParser.js +11 -0
- package/lib/language/genericAntlrParser.js +72 -14
- package/lib/language/language.g4 +73 -0
- package/lib/main.d.ts +136 -17
- package/lib/main.js +3 -1
- package/lib/model/api.js +2 -2
- package/lib/model/csnRefs.js +108 -31
- package/lib/model/csnUtils.js +63 -29
- package/lib/model/enrichCsn.js +36 -9
- package/lib/model/revealInternalProperties.js +20 -4
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +29 -18
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/toCdl.js +9 -3
- package/lib/render/toHdbcds.js +16 -36
- package/lib/render/toSql.js +23 -5
- package/lib/transform/db/constraints.js +278 -119
- package/lib/transform/db/draft.js +3 -2
- package/lib/transform/db/expansion.js +6 -4
- package/lib/transform/db/flattening.js +17 -1
- package/lib/transform/db/transformExists.js +61 -2
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +56 -435
- package/lib/transform/forOdataNew.js +9 -2
- package/lib/transform/localized.js +2 -0
- package/lib/transform/transformUtilsNew.js +10 -0
- package/lib/transform/translateAssocsToJoins.js +5 -13
- package/lib/utils/file.js +5 -3
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
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,15 @@ 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
|
+
|
|
36
40
|
// IMPORTANT: the order of these properties determine the order of properties
|
|
37
41
|
// in the resulting CSN !!! Also check const `csnPropertyNames`.
|
|
38
42
|
const transformers = {
|
|
39
43
|
// early and modifiers (without null / not null) ---------------------------
|
|
40
44
|
kind,
|
|
41
|
-
_outer: ( _, csn, node ) => addOrigin( csn, node ),
|
|
42
45
|
id: n => n, // in path item
|
|
43
46
|
doc: value,
|
|
44
47
|
'@': value,
|
|
@@ -67,7 +70,7 @@ const transformers = {
|
|
|
67
70
|
targetAspect,
|
|
68
71
|
target,
|
|
69
72
|
foreignKeys,
|
|
70
|
-
enum:
|
|
73
|
+
enum: enumDict,
|
|
71
74
|
items,
|
|
72
75
|
includes: arrayOf( artifactRef ), // also entities
|
|
73
76
|
// late expressions / query properties -------------------------------------
|
|
@@ -199,6 +202,15 @@ const operators = {
|
|
|
199
202
|
partitionBy: exprs => [
|
|
200
203
|
'partition', 'by', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
201
204
|
],
|
|
205
|
+
rows: exprs => [
|
|
206
|
+
'rows', ...exprs[0].concat( ...exprs.slice(1).map( e => [ ',', ...e ] ) ),
|
|
207
|
+
],
|
|
208
|
+
preceding: postfix( [ 'preceding' ] ),
|
|
209
|
+
unboundedPreceding: [ 'unbounded', 'preceding' ],
|
|
210
|
+
currentRow: [ 'current', 'row' ],
|
|
211
|
+
unboundedFollowing: [ 'unbounded', 'following' ],
|
|
212
|
+
following: postfix( [ 'following' ] ),
|
|
213
|
+
frameBetween: exprs => [ 'between', ...exprs[0], 'and', ...exprs[1] ],
|
|
202
214
|
// xpr: (exprs) => [].concat( ...exprs ), see below - handled extra
|
|
203
215
|
};
|
|
204
216
|
|
|
@@ -622,7 +634,7 @@ function target( val, _csn, node ) {
|
|
|
622
634
|
}
|
|
623
635
|
|
|
624
636
|
function items( obj, csn, node ) {
|
|
625
|
-
if (!keepElements( node ))
|
|
637
|
+
if (!keepElements( node, obj ))
|
|
626
638
|
return undefined;
|
|
627
639
|
return standard( obj ); // no 'elements' with inferred elements with gensrc
|
|
628
640
|
}
|
|
@@ -637,6 +649,18 @@ function elements( dict, csn, node ) {
|
|
|
637
649
|
return insertOrderDict( dict );
|
|
638
650
|
}
|
|
639
651
|
|
|
652
|
+
function enumDict( dict, csn, node ) {
|
|
653
|
+
if (gensrcFlavor && dict[$inferred] ||
|
|
654
|
+
!keepElements( node ))
|
|
655
|
+
// no 'elements' with SELECT or inferred elements with gensrc;
|
|
656
|
+
// hidden or visible 'elements' will be set in query()
|
|
657
|
+
return undefined;
|
|
658
|
+
if (universalCsn && node.type && !node.type.$inferred && node.$expand === 'annotate')
|
|
659
|
+
// derived type of enum type with individual annotations: also set $origin
|
|
660
|
+
csn.$origin = originRef( node.type._artifact );
|
|
661
|
+
return insertOrderDict( dict );
|
|
662
|
+
}
|
|
663
|
+
|
|
640
664
|
function enumerableQueryElements( select ) {
|
|
641
665
|
if (!universalCsn || select === select._main._leadingQuery)
|
|
642
666
|
return false;
|
|
@@ -647,13 +671,24 @@ function enumerableQueryElements( select ) {
|
|
|
647
671
|
}
|
|
648
672
|
|
|
649
673
|
// Should we render the elements? (and items?)
|
|
650
|
-
function keepElements( node ) {
|
|
674
|
+
function keepElements( node, line ) {
|
|
651
675
|
if (universalCsn)
|
|
652
676
|
// $expand = null/undefined: not elements not via expansion
|
|
653
677
|
// $expand = 'target'/'annotate': with redirections / individual annotations
|
|
654
678
|
return node.$expand !== 'origin';
|
|
655
679
|
if (!node.type || node.kind === 'type')
|
|
656
680
|
return true;
|
|
681
|
+
// keep many SimpleType/Entity
|
|
682
|
+
if (line) {
|
|
683
|
+
if (!node.type)
|
|
684
|
+
return true;
|
|
685
|
+
const array = node.type._artifact; // see function items() in propagator.js
|
|
686
|
+
const ltype = line.type && line.type._artifact;
|
|
687
|
+
if (!array || // reference errors
|
|
688
|
+
array._main && !line.elements && !line.enum && !line.items && !line.notNull &&
|
|
689
|
+
(!ltype || !ltype._main)) // many Foo:bar -> not SimpleType
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
657
692
|
// even if expanded elements have no new target or direct annotation,
|
|
658
693
|
// they might have got one via propagation – any new target/annos during their
|
|
659
694
|
// way from the original structure type definition to the current usage
|
|
@@ -801,44 +836,143 @@ function definition( art, _csn, _node, prop ) {
|
|
|
801
836
|
delete c.elements;
|
|
802
837
|
c.returns = { elements: elems };
|
|
803
838
|
}
|
|
839
|
+
if (kind && kind !== 'key')
|
|
840
|
+
addOrigin( c, art, art._origin );
|
|
804
841
|
return c;
|
|
805
842
|
}
|
|
806
843
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
844
|
+
// create $origin specification for `includes` of `art`
|
|
845
|
+
function includesOrigin( includes, art ) {
|
|
846
|
+
const $origin = originRef( includes[0]._artifact );
|
|
847
|
+
if (includes.length === 1)
|
|
848
|
+
return $origin;
|
|
849
|
+
const result = { $origin };
|
|
850
|
+
for (const incl of includes.slice(1)) {
|
|
851
|
+
const aspect = incl._artifact;
|
|
852
|
+
for (const prop in aspect) {
|
|
853
|
+
if (prop.charAt(0) === '@' && (!art[prop] || art[prop].$inferred)) {
|
|
854
|
+
const anno = aspect[prop];
|
|
855
|
+
if (anno.val !== null)
|
|
856
|
+
// matererialize non-null annos (whether direct or inherited)
|
|
857
|
+
result[prop] = value( Object.create( anno, { $inferred: { value: null } } ) );
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return (Object.keys( result ).length === 1) ? $origin : result;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function addOrigin( csn, xsn, origin ) {
|
|
865
|
+
if (!universalCsn || hasExplicitProp( xsn.type ))
|
|
866
|
+
return;
|
|
810
867
|
if (xsn._from) {
|
|
811
|
-
|
|
868
|
+
const source = xsn._from[0]._origin;
|
|
869
|
+
csn.$origin = originRef( source );
|
|
870
|
+
if (source.params && !xsn.params)
|
|
871
|
+
csn.params = null; // discontinue `params` inheritance
|
|
872
|
+
if (source.actions && !xsn.actions)
|
|
873
|
+
csn.actions = null; // discontinue `actions` inheritance
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
else if (xsn.includes) {
|
|
877
|
+
csn.$origin = includesOrigin( xsn.includes, xsn );
|
|
878
|
+
return;
|
|
812
879
|
}
|
|
813
|
-
else if (xsn.
|
|
814
|
-
|
|
880
|
+
else if (!xsn._main || xsn.kind === 'select') {
|
|
881
|
+
return;
|
|
815
882
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
883
|
+
const parent = getParent( xsn );
|
|
884
|
+
const parentOrigin = getOrigin( parent );
|
|
885
|
+
if (!xsn._origin || xsn._origin.kind === 'builtin') { // or $dollarVariable
|
|
886
|
+
if (parentOrigin && (!parent.enum || parent.$origin || !parent.type))
|
|
887
|
+
csn.$origin = null;
|
|
888
|
+
return;
|
|
821
889
|
}
|
|
822
|
-
|
|
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
|
+
if (parentOrigin === getParent( origin )) {
|
|
898
|
+
// implicit prototype or shortened reference
|
|
899
|
+
const { id } = origin.name || {};
|
|
900
|
+
if (id && xsn.name && id !== xsn.name.id)
|
|
901
|
+
csn.$origin = id;
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
if (origin.kind === 'mixin') {
|
|
905
|
+
// currently, target and on are always set - nothing to do here, just set type
|
|
906
|
+
csn.type = 'cds.Association';
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const ref = originRef( origin, xsn );
|
|
910
|
+
if (ref) {
|
|
911
|
+
csn.$origin = ref;
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
// An element of a query with a query in FROM:
|
|
915
|
+
const anon = definition( origin ); // use $origin: {...} if necessary
|
|
916
|
+
// as there are no implicit $origin prototypes on sub query elements (yet),
|
|
917
|
+
// we do not have to care about $origin not being set
|
|
918
|
+
const { $origin } = anon;
|
|
919
|
+
if ($origin && typeof $origin === 'object' && !Array.isArray( $origin )) {
|
|
920
|
+
// repeated anon: flatten
|
|
921
|
+
csn.$origin = Object.assign( $origin, anon );
|
|
922
|
+
}
|
|
923
|
+
else if (Object.keys( anon )
|
|
924
|
+
// (we can use the properties in `csn`, because addOrigin() is called last)
|
|
925
|
+
.every( p => p in csn || p === '$origin' || p === '$location')) {
|
|
926
|
+
// nothing new in $origin: {...}
|
|
927
|
+
addOrigin( csn, xsn, origin._origin );
|
|
928
|
+
}
|
|
929
|
+
else {
|
|
930
|
+
csn.$origin = anon;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
function getParent( art ) {
|
|
935
|
+
const parent = art._parent;
|
|
936
|
+
const main = parent._main;
|
|
937
|
+
return (main && parent === main._leadingQuery) ? main : parent;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function getOrigin( art ) {
|
|
941
|
+
if (art._origin)
|
|
942
|
+
return art._origin;
|
|
943
|
+
if (hasExplicitProp( art.type ))
|
|
944
|
+
return art.type._artifact;
|
|
945
|
+
if (art.includes)
|
|
946
|
+
return art.includes[0]._artifact;
|
|
947
|
+
if (art._from)
|
|
948
|
+
return art._from[0]._origin;
|
|
949
|
+
return undefined;
|
|
823
950
|
}
|
|
824
951
|
|
|
825
952
|
function hasExplicitProp( ref ) {
|
|
826
953
|
return ref && !ref.$inferred;
|
|
827
954
|
}
|
|
828
955
|
|
|
829
|
-
function originRef( art ) {
|
|
956
|
+
function originRef( art, user ) {
|
|
830
957
|
const r = [];
|
|
831
958
|
// do not use name.element, as we allow `.`s in name
|
|
832
|
-
let
|
|
833
|
-
while (
|
|
834
|
-
const nkind = normalizedKind[
|
|
835
|
-
if (
|
|
836
|
-
|
|
837
|
-
|
|
959
|
+
let parent = art;
|
|
960
|
+
while (parent._main && parent.kind !== 'select') {
|
|
961
|
+
const nkind = normalizedKind[parent.kind];
|
|
962
|
+
if (parent.name.id || !r.length)
|
|
963
|
+
// Return parameter is in XSN - kind: 'param', name.id: ''
|
|
964
|
+
// eslint-disable-next-line no-nested-ternary, max-len
|
|
965
|
+
r.push( !nkind ? parent.name.id : parent.name.id ? { [nkind]: parent.name.id } : { return: true } );
|
|
966
|
+
parent = parent._parent;
|
|
838
967
|
}
|
|
839
|
-
if (
|
|
840
|
-
|
|
968
|
+
if (user && parent._main && parent._main === user._main && parent !== user._main._leadingQuery)
|
|
969
|
+
// well, an element of an query in FROM (TODO: try with sub elem), but not the leading query
|
|
970
|
+
return null; // probably use $origin: {...}
|
|
841
971
|
// for sub query in FROM in sub query in FROM, we could condense the info
|
|
972
|
+
|
|
973
|
+
// Now the ref, with ["absolute", "action"] instead of ["absolute", {action:"action"}]
|
|
974
|
+
if (r.length === 1 && normalizedKind[art.kind] === 'action')
|
|
975
|
+
return [ art.name.absolute, art.name.id ];
|
|
842
976
|
r.push( art.name.absolute );
|
|
843
977
|
r.reverse();
|
|
844
978
|
return r;
|
|
@@ -853,13 +987,11 @@ function kind( k, csn, node ) {
|
|
|
853
987
|
else if (k === 'extend')
|
|
854
988
|
csn.kind = k;
|
|
855
989
|
}
|
|
856
|
-
else
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
csn.kind = k;
|
|
862
|
-
addOrigin( csn, node );
|
|
990
|
+
else if (![
|
|
991
|
+
'element', 'key', 'param', 'enum', 'select', '$join',
|
|
992
|
+
'$tableAlias', 'annotation', 'mixin',
|
|
993
|
+
].includes(k)) {
|
|
994
|
+
csn.kind = k;
|
|
863
995
|
}
|
|
864
996
|
}
|
|
865
997
|
|
|
@@ -997,7 +1129,10 @@ function value( node ) {
|
|
|
997
1129
|
|
|
998
1130
|
function enumValue( v, csn, node ) {
|
|
999
1131
|
// Enums can have values but if enums are extended, their kind is 'element',
|
|
1000
|
-
// so we check whether the node is inside an extension.
|
|
1132
|
+
// so we check whether the node is inside an extension. (TODO: still?)
|
|
1133
|
+
if (universalCsn && v.$inferred)
|
|
1134
|
+
return;
|
|
1135
|
+
// (with gensrc, the symbol itself would not make it into the CSN)
|
|
1001
1136
|
if (node.kind === 'enum' || node._parent && node._parent.kind === 'extend')
|
|
1002
1137
|
Object.assign( csn, expression( v, true ) );
|
|
1003
1138
|
}
|
|
@@ -1185,7 +1320,7 @@ function columns( xsnColumns, csn, xsn ) {
|
|
|
1185
1320
|
addElementAsColumn( col, csnColumns );
|
|
1186
1321
|
}
|
|
1187
1322
|
}
|
|
1188
|
-
else {
|
|
1323
|
+
else { // null = use elements - TODO: still used by A2J? -> remove
|
|
1189
1324
|
for (const name in xsn.elements)
|
|
1190
1325
|
addElementAsColumn( xsn.elements[name], csnColumns );
|
|
1191
1326
|
}
|
|
@@ -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',
|
|
@@ -49,6 +49,8 @@ GenericAntlrParser.prototype = Object.assign(
|
|
|
49
49
|
attachLocation,
|
|
50
50
|
startLocation,
|
|
51
51
|
tokenLocation,
|
|
52
|
+
multiLineTokenLocation,
|
|
53
|
+
previousTokenAtLocation,
|
|
52
54
|
combinedLocation,
|
|
53
55
|
surroundByParens,
|
|
54
56
|
unaryOpForParens,
|
|
@@ -245,9 +247,12 @@ function prepareGenericKeywords( pathItem ) {
|
|
|
245
247
|
// TODO: If not just at the beginning, we need a stack for $genericKeywords,
|
|
246
248
|
// as we can have nested special functions
|
|
247
249
|
this.$genericKeywords.argFull = Object.keys( spec );
|
|
250
|
+
// @ts-ignore
|
|
248
251
|
const token = this.getCurrentToken() || { text: '' };
|
|
249
|
-
if (spec[token.text.toUpperCase()] === 'argFull')
|
|
252
|
+
if (spec[token.text.toUpperCase()] === 'argFull') {
|
|
253
|
+
// @ts-ignore
|
|
250
254
|
token.type = this.constructor.GenericArgFull;
|
|
255
|
+
}
|
|
251
256
|
}
|
|
252
257
|
|
|
253
258
|
// Attach location matched by current rule to node `art`. If a location is
|
|
@@ -297,7 +302,7 @@ function tokenLocation( token, endToken, val ) {
|
|
|
297
302
|
file: this.filename,
|
|
298
303
|
line: token.line,
|
|
299
304
|
col: token.column + 1,
|
|
300
|
-
// we only have single-line tokens
|
|
305
|
+
// we only have single-line tokens, except for docComments
|
|
301
306
|
endLine: endToken.line,
|
|
302
307
|
endCol: endToken.stop - endToken.start + endToken.column + 2, // after the last char (special for EOF?)
|
|
303
308
|
};
|
|
@@ -306,6 +311,51 @@ function tokenLocation( token, endToken, val ) {
|
|
|
306
311
|
return r;
|
|
307
312
|
}
|
|
308
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Return location of `token`. In contrast to `tokenLocation()`, this function
|
|
316
|
+
* can handle multiline tokens.
|
|
317
|
+
*/
|
|
318
|
+
function multiLineTokenLocation(token, val) {
|
|
319
|
+
if (!token)
|
|
320
|
+
return undefined;
|
|
321
|
+
|
|
322
|
+
// Count the number of newlines in the token.
|
|
323
|
+
// TODO: I want to avoid a substring, that's why I don't use RegEx here
|
|
324
|
+
const source = token.source[1].data;
|
|
325
|
+
let newLineCount = 0;
|
|
326
|
+
let lastNewlineIndex = token.start;
|
|
327
|
+
for (let i = token.start; i < token.stop; i++) {
|
|
328
|
+
if (source[i] === 10) { // ASCII code for '\n'
|
|
329
|
+
newLineCount++;
|
|
330
|
+
lastNewlineIndex = i;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (newLineCount === 0)
|
|
334
|
+
// endCol calculation below requires at least one newLine.
|
|
335
|
+
return this.tokenLocation(token, token, val);
|
|
336
|
+
|
|
337
|
+
/** @type {CSN.Location} */
|
|
338
|
+
const r = {
|
|
339
|
+
file: this.filename,
|
|
340
|
+
line: token.line,
|
|
341
|
+
col: token.column + 1,
|
|
342
|
+
endLine: token.line + newLineCount,
|
|
343
|
+
endCol: token.stop - lastNewlineIndex + 1, // after the last char (special for EOF?)
|
|
344
|
+
};
|
|
345
|
+
if (val !== undefined)
|
|
346
|
+
return { location: r, val };
|
|
347
|
+
return r;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function previousTokenAtLocation( location ) {
|
|
351
|
+
let k = -1;
|
|
352
|
+
let token = this._input.LT(k);
|
|
353
|
+
while (token.line > location.line ||
|
|
354
|
+
token.line === location.line && token.column >= location.col)
|
|
355
|
+
token = this._input.LT(--k);
|
|
356
|
+
return (token.line === location.line && token.column + 1 === location.col) && token;
|
|
357
|
+
}
|
|
358
|
+
|
|
309
359
|
// Create a location with location properties `filename` and `start` from
|
|
310
360
|
// argument `start`, and location property `end` from argument `end`.
|
|
311
361
|
function combinedLocation( start, end ) {
|
|
@@ -342,16 +392,20 @@ function unaryOpForParens( query, val ) {
|
|
|
342
392
|
// - would influence the prediction, probably even induce adaptivePredict() calls,
|
|
343
393
|
// - is only slightly "more declarative" in the grammar.
|
|
344
394
|
function docComment( node ) {
|
|
345
|
-
if (!this.options.docComment)
|
|
346
|
-
return;
|
|
347
395
|
const token = this._input.getHiddenTokenToLeft( this.constructor.DocComment );
|
|
348
396
|
if (!token)
|
|
349
397
|
return;
|
|
398
|
+
|
|
399
|
+
// This token is actually used by / assigned to an artifact.
|
|
400
|
+
token.isUsed = true;
|
|
401
|
+
|
|
402
|
+
if (!this.options.docComment)
|
|
403
|
+
return;
|
|
350
404
|
if (node.doc) {
|
|
351
405
|
this.warning( 'syntax-duplicate-doc-comment', token, {},
|
|
352
406
|
'Repeated doc comment - previous doc is replaced' );
|
|
353
407
|
}
|
|
354
|
-
node.doc = this.
|
|
408
|
+
node.doc = this.multiLineTokenLocation( token, parseDocComment( token.text ) );
|
|
355
409
|
}
|
|
356
410
|
|
|
357
411
|
// Classify token (identifier category) for implicit names,
|
|
@@ -435,14 +489,18 @@ function valuePathAst( ref ) {
|
|
|
435
489
|
path.length = 1;
|
|
436
490
|
}
|
|
437
491
|
const { args, id, location } = path[0];
|
|
438
|
-
if (args
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
492
|
+
if (args
|
|
493
|
+
? path[0].$syntax === ':'
|
|
494
|
+
: path[0].$delimited || !functionsWithoutParens.includes( id.toUpperCase() ))
|
|
495
|
+
return ref;
|
|
496
|
+
|
|
497
|
+
const implicit = this.previousTokenAtLocation( location );
|
|
498
|
+
if (implicit && implicit.isIdentifier)
|
|
499
|
+
implicit.isIdentifier = 'func';
|
|
500
|
+
const op = { location, val: 'call' };
|
|
501
|
+
return (args)
|
|
502
|
+
? { op, func: ref, location: ref.location, args }
|
|
503
|
+
: { op, func: ref, location: ref.location };
|
|
446
504
|
}
|
|
447
505
|
|
|
448
506
|
// If a '-' is directly before an unsigned number, consider it part of the number;
|
|
@@ -601,7 +659,7 @@ function addDef( parent, env, kind, name, annos, props, location ) {
|
|
|
601
659
|
// which could be tested in name search (then no undefined-ref error)
|
|
602
660
|
return art;
|
|
603
661
|
}
|
|
604
|
-
else if (env === 'artifacts') {
|
|
662
|
+
else if (env === 'artifacts' || env === 'vocabularies') {
|
|
605
663
|
dictAddArray( parent[env], art.name.id, art );
|
|
606
664
|
}
|
|
607
665
|
else if (kind || this.options.parseOnly) {
|
package/lib/language/language.g4
CHANGED
|
@@ -498,6 +498,8 @@ projectionSpec returns[ query ] locals[ src ]
|
|
|
498
498
|
fromPath[ $src, 'ref']
|
|
499
499
|
)?
|
|
500
500
|
( AS aliasName=ident['FromAlias'] { $src.name = $aliasName.id } )?
|
|
501
|
+
// ANTLR errors are better if we use ( A )? instead of ( A | ):
|
|
502
|
+
{ if (!$src.name) this.classifyImplicitName( $src.scope ? 'FromAlias' : 'Without' ); }
|
|
501
503
|
bracedSelectItemListDef[ $query ]?
|
|
502
504
|
excludingClause[ $query ]?
|
|
503
505
|
;
|
|
@@ -1776,6 +1778,62 @@ partitionByClause returns [ expr ]
|
|
|
1776
1778
|
( ',' en=expression { $expr.args.push( $en.expr ); } )*
|
|
1777
1779
|
;
|
|
1778
1780
|
|
|
1781
|
+
windowFrameClause returns [ wf ]
|
|
1782
|
+
:
|
|
1783
|
+
r=ROWS { $wf = { op: this.tokenLocation($r, null, 'rows' ) , args: [] }}
|
|
1784
|
+
wfe=windowFrameExtentSpec { $wf.args.push( $wfe.wfe ); }
|
|
1785
|
+
;
|
|
1786
|
+
|
|
1787
|
+
windowFrameExtentSpec returns[ wfe ]
|
|
1788
|
+
:
|
|
1789
|
+
{ $wfe = {} }
|
|
1790
|
+
windowFrameStartSpec [ $wfe ]
|
|
1791
|
+
|
|
|
1792
|
+
b=BETWEEN
|
|
1793
|
+
{ $wfe = { op: this.tokenLocation( $b, null, 'frameBetween' ), args: [] } }
|
|
1794
|
+
wfb1=windowFrameBoundSpec { $wfe.args.push( $wfb1.wfb ); }
|
|
1795
|
+
AND
|
|
1796
|
+
wfb2=windowFrameBoundSpec { $wfe.args.push( $wfb2.wfb ); }
|
|
1797
|
+
;
|
|
1798
|
+
|
|
1799
|
+
windowFrameBoundSpec returns [ wfb ]
|
|
1800
|
+
@after{ /* #ATN 1 */ }
|
|
1801
|
+
:
|
|
1802
|
+
// #ATN: Not ll1 because `UNBOUNDED` could also be part of the windowFrameStartSpec
|
|
1803
|
+
// `UNBOUNDED` would then be immediately followed by `PRECEDING`
|
|
1804
|
+
u=UNBOUNDED f=FOLLOWING
|
|
1805
|
+
{ $wfb = { op: this.tokenLocation($u, $f, 'unboundedFollowing' ), args: []} }
|
|
1806
|
+
|
|
|
1807
|
+
// #ATN: Not ll1 because `Number` could also be part of the windowFrameStartSpec
|
|
1808
|
+
// `Number` would then be immediately followed by `PRECEDING`
|
|
1809
|
+
n=Number f=FOLLOWING
|
|
1810
|
+
{ $wfb = { op: this.tokenLocation($n, $f, 'following' ), args: [ this.numberLiteral( $n ) ]} }
|
|
1811
|
+
|
|
|
1812
|
+
{ $wfb = {} }
|
|
1813
|
+
windowFrameStartSpec [ $wfb ]
|
|
1814
|
+
;
|
|
1815
|
+
|
|
1816
|
+
windowFrameStartSpec [ wf ]
|
|
1817
|
+
:
|
|
1818
|
+
u=UNBOUNDED p=PRECEDING
|
|
1819
|
+
{
|
|
1820
|
+
$wf.op = this.tokenLocation($u, $p, 'unboundedPreceding' );
|
|
1821
|
+
$wf.args = [];
|
|
1822
|
+
}
|
|
1823
|
+
|
|
|
1824
|
+
n=Number p=PRECEDING
|
|
1825
|
+
{
|
|
1826
|
+
$wf.op = this.tokenLocation($p, null, 'preceding' );
|
|
1827
|
+
$wf.args = [ this.numberLiteral( $n ) ];
|
|
1828
|
+
}
|
|
1829
|
+
|
|
|
1830
|
+
c=CURRENT r=ROW
|
|
1831
|
+
{
|
|
1832
|
+
$wf.op = this.tokenLocation($c, $r, 'currentRow' );
|
|
1833
|
+
$wf.args = [];
|
|
1834
|
+
}
|
|
1835
|
+
;
|
|
1836
|
+
|
|
1779
1837
|
overClause returns [ over ]
|
|
1780
1838
|
@after { this.attachLocation($over); }
|
|
1781
1839
|
:
|
|
@@ -1783,6 +1841,7 @@ overClause returns [ over ]
|
|
|
1783
1841
|
'('
|
|
1784
1842
|
( pb=partitionByClause { $over.args.push( $pb.expr ); } )?
|
|
1785
1843
|
( ob=overOrderByClause { $over.args.push( $ob.expr ); } )?
|
|
1844
|
+
( wf=windowFrameClause { $over.args.push( $wf.wf ); } )?
|
|
1786
1845
|
')'
|
|
1787
1846
|
;
|
|
1788
1847
|
|
|
@@ -1948,6 +2007,8 @@ tableTerm returns [ table ]
|
|
|
1948
2007
|
// if we would use rule `ident`, we would either had to make all JOIN
|
|
1949
2008
|
// kinds reserved or introduce ATN
|
|
1950
2009
|
)?
|
|
2010
|
+
// ANTLR errors are better if we use ( A | B )? instead of ( A | B | ):
|
|
2011
|
+
{ if (!$table.name) this.classifyImplicitName( $table.scope ? 'FromAlias' : 'Without' ); }
|
|
1951
2012
|
|
|
|
1952
2013
|
open='('
|
|
1953
2014
|
// #ATN: The following alternative is not LL1, because both can start with
|
|
@@ -2602,6 +2663,7 @@ ident[ category ] returns[ id ]
|
|
|
2602
2663
|
| COMPOSITION
|
|
2603
2664
|
| CONTEXT
|
|
2604
2665
|
| CROSS
|
|
2666
|
+
| CURRENT
|
|
2605
2667
|
| DAY
|
|
2606
2668
|
| DEFAULT
|
|
2607
2669
|
| DEFINE
|
|
@@ -2619,6 +2681,7 @@ ident[ category ] returns[ id ]
|
|
|
2619
2681
|
| EXTEND
|
|
2620
2682
|
| FIRST
|
|
2621
2683
|
| FLOATING
|
|
2684
|
+
| FOLLOWING
|
|
2622
2685
|
| FULL
|
|
2623
2686
|
| FUNCTION
|
|
2624
2687
|
| GROUP
|
|
@@ -2650,10 +2713,13 @@ ident[ category ] returns[ id ]
|
|
|
2650
2713
|
| OUTER
|
|
2651
2714
|
| PARAMETERS
|
|
2652
2715
|
| PARTITION
|
|
2716
|
+
| PRECEDING
|
|
2653
2717
|
| PROJECTION
|
|
2654
2718
|
| REDIRECTED
|
|
2655
2719
|
| RETURNS
|
|
2656
2720
|
| RIGHT
|
|
2721
|
+
| ROW
|
|
2722
|
+
| ROWS
|
|
2657
2723
|
| SECOND
|
|
2658
2724
|
| SERVICE
|
|
2659
2725
|
| THEN
|
|
@@ -2662,6 +2728,7 @@ ident[ category ] returns[ id ]
|
|
|
2662
2728
|
| TO
|
|
2663
2729
|
| TYPE
|
|
2664
2730
|
| USING
|
|
2731
|
+
| UNBOUNDED
|
|
2665
2732
|
| VARIABLE
|
|
2666
2733
|
| VIEW
|
|
2667
2734
|
| YEAR
|
|
@@ -2763,6 +2830,7 @@ BOTH : [bB][oO][tT][hH] ;
|
|
|
2763
2830
|
COMPOSITION : [cC][oO][mM][pP][oO][sS][iI][tT][iI][oO][nN] ;
|
|
2764
2831
|
CONTEXT : [cC][oO][nN][tT][eE][xX][tT] ;
|
|
2765
2832
|
CROSS : [cC][rR][oO][sS][sS] ;
|
|
2833
|
+
CURRENT : [cC][uU][rR][rR][eE][nN][tT] ;
|
|
2766
2834
|
DAY : [dD][aA][yY] ;
|
|
2767
2835
|
DEFAULT : [dD][eE][fF][aA][uU][lL][tT] ;
|
|
2768
2836
|
DEFINE : [dD][eE][fF][iI][nN][eE] ;
|
|
@@ -2780,6 +2848,7 @@ EXCLUDING : [eE][xX][cC][lL][uU][dD][iI][nN][gG] ;
|
|
|
2780
2848
|
EXTEND : [eE][xX][tT][eE][nN][dD] ;
|
|
2781
2849
|
FIRST : [fF][iI][rR][sS][tT] ;
|
|
2782
2850
|
FLOATING : [fF][lL][oO][aA][tT][iI][nN][gG] ;
|
|
2851
|
+
FOLLOWING : [fF][oO][lL][lL][oO][wW][iI][nN][gG] ;
|
|
2783
2852
|
FULL : [fF][uU][lL][lL] ;
|
|
2784
2853
|
FUNCTION : [fF][uU][nN][cC][tT][iI][oO][nN] ;
|
|
2785
2854
|
GROUP : [gG][rR][oO][uU][pP] ;
|
|
@@ -2812,10 +2881,13 @@ OUTER : [oO][uU][tT][eE][rR] ;
|
|
|
2812
2881
|
// OVER : [oO][vV][eE][rR] ;
|
|
2813
2882
|
PARAMETERS : [pP][aA][rR][aA][mM][eE][tT][eE][rR][sS] ;
|
|
2814
2883
|
PARTITION: [pP][aA][rR][tT][iI][tT][iI][oO][nN] ;
|
|
2884
|
+
PRECEDING: [pP][rR][eE][cC][eE][dD][iI][nN][gG] ;
|
|
2815
2885
|
PROJECTION : [pP][rR][oO][jJ][eE][cC][tT][iI][oO][nN] ;
|
|
2816
2886
|
REDIRECTED : [rR][eE][dD][iI][rR][eE][cC][tT][eE][dD] ;
|
|
2817
2887
|
RETURNS : [rR][eE][tT][uU][rR][nN][sS] ;
|
|
2818
2888
|
RIGHT : [rR][iI][gG][hH][tT] ;
|
|
2889
|
+
ROW : [rR][oO][wW] ;
|
|
2890
|
+
ROWS : [rR][oO][wW][sS] ;
|
|
2819
2891
|
SECOND : [sS][eE][cC][oO][nN][dD] ;
|
|
2820
2892
|
SERVICE : [sS][eE][rR][vV][iI][cC][eE] ;
|
|
2821
2893
|
THEN : [tT][hH][eE][nN] ;
|
|
@@ -2823,6 +2895,7 @@ TRAILING : [tT][rR][aA][iI][lL][iI][nN][gG] ;
|
|
|
2823
2895
|
TO : [tT][oO] ; // or make reserved? (is in SQL-92)
|
|
2824
2896
|
TYPE : [tT][yY][pP][eE] ;
|
|
2825
2897
|
UNION : [uU][nN][iI][oO][nN] ;
|
|
2898
|
+
UNBOUNDED : [uU][nN][bB][oO][uU][nN][dD][eE][dD] ;
|
|
2826
2899
|
USING : [uU][sS][iI][nN][gG] ;
|
|
2827
2900
|
VARIABLE : [vV][aA][rR][iI][aA][bB][lL][eE] ;
|
|
2828
2901
|
VIEW : [vV][iI][eE][wW] ;
|