@sap/cds-compiler 3.7.2 → 3.8.2
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 +71 -4
- package/bin/cdsc.js +3 -0
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +15 -0
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +61 -22
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +5 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +64 -22
- package/lib/base/messages.js +12 -7
- package/lib/base/model.js +3 -2
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/hasPersistedElements.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/onConditions.js +9 -6
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +1 -2
- package/lib/compiler/assert-consistency.js +25 -6
- package/lib/compiler/base.js +51 -2
- package/lib/compiler/builtins.js +15 -6
- package/lib/compiler/checks.js +4 -4
- package/lib/compiler/define.js +59 -80
- package/lib/compiler/extend.js +717 -498
- package/lib/compiler/finalize-parse-cdl.js +4 -3
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +2 -2
- package/lib/compiler/populate.js +17 -9
- package/lib/compiler/propagator.js +12 -5
- package/lib/compiler/resolve.js +26 -173
- package/lib/compiler/shared.js +20 -58
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +2 -2
- package/lib/edm/annotations/genericTranslation.js +124 -46
- package/lib/edm/csn2edm.js +22 -1
- package/lib/edm/edmPreprocessor.js +41 -21
- package/lib/gen/Dictionary.json +4 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4844 -4508
- package/lib/inspect/inspectPropagation.js +20 -36
- package/lib/json/from-csn.js +56 -7
- package/lib/json/to-csn.js +71 -110
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +49 -9
- package/lib/language/language.g4 +106 -83
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +19 -4
- package/lib/model/csnUtils.js +11 -74
- package/lib/model/revealInternalProperties.js +3 -0
- package/lib/optionProcessor.js +3 -0
- package/lib/render/toCdl.js +203 -104
- package/lib/render/toHdbcds.js +0 -1
- package/lib/render/toRename.js +14 -51
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/rewriteCalculatedElements.js +55 -14
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +21 -14
- package/lib/transform/parseExpr.js +2 -0
- package/lib/transform/transformUtilsNew.js +36 -9
- package/lib/transform/translateAssocsToJoins.js +11 -4
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
package/lib/language/language.g4
CHANGED
|
@@ -581,17 +581,17 @@ elementDefInner[ art, outer, mightBeEnum ]
|
|
|
581
581
|
@after{ this.attachLocation( $art ); }
|
|
582
582
|
:
|
|
583
583
|
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
584
|
-
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[
|
|
584
|
+
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[;:{@=}]$/ ); }
|
|
585
585
|
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
586
586
|
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
587
|
-
{ this.setLocalToken( 'MASKED', 'MASKED', /^[
|
|
587
|
+
{ this.setLocalToken( 'MASKED', 'MASKED', /^[;:{@=}]$/ ); }
|
|
588
588
|
( masked=MASKED
|
|
589
589
|
{
|
|
590
590
|
$art.masked = this.valueWithTokenLocation( true, $masked ) ;
|
|
591
591
|
this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
|
|
592
592
|
}
|
|
593
593
|
)?
|
|
594
|
-
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[
|
|
594
|
+
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[;:{@=}]$/ ); }
|
|
595
595
|
( ELEMENT { $mightBeEnum = false; } )? // auto-recognizable at other places
|
|
596
596
|
name=ident['Element']
|
|
597
597
|
{ this.addDef( $art, $outer, 'elements', 'element', $name.id );
|
|
@@ -610,13 +610,15 @@ elementDefInner[ art, outer, mightBeEnum ]
|
|
|
610
610
|
'=' e=expression // SQL has syntax variant using AS - we DO NOT
|
|
611
611
|
{ $art.value = $e.expr;
|
|
612
612
|
// this.setIntroLocation( eq ); -- future
|
|
613
|
-
if ($mightBeEnum && ($e.expr
|
|
613
|
+
if ($mightBeEnum && ($e.expr?.val !== undefined || $e.expr?.sym !== undefined) &&
|
|
614
614
|
!$virtual && !$key && !$masked && !$art.elements && !$art.type)
|
|
615
615
|
$art['$'+'syntax'] = 'enum';
|
|
616
616
|
}
|
|
617
617
|
{ this.docComment( $art ); }
|
|
618
618
|
annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
|
|
619
619
|
requiredSemi
|
|
620
|
+
|
|
|
621
|
+
requiredSemi
|
|
620
622
|
)
|
|
621
623
|
;
|
|
622
624
|
|
|
@@ -716,14 +718,19 @@ elementType[ art ] // TODO: split this monster rule
|
|
|
716
718
|
;
|
|
717
719
|
|
|
718
720
|
elementProperties[ elem ]
|
|
721
|
+
:
|
|
722
|
+
defaultAndNullablity[ $elem ]
|
|
723
|
+
|
|
|
724
|
+
'=' e=expression { $elem.value = $e.expr; }
|
|
725
|
+
;
|
|
726
|
+
|
|
727
|
+
defaultAndNullablity[ elem ]
|
|
719
728
|
:
|
|
720
729
|
defaultValue[ $elem ]
|
|
721
730
|
nullability[ $elem ]? // placement accoring to SQL spec
|
|
722
731
|
|
|
|
723
732
|
nullability[ $elem ]
|
|
724
733
|
defaultValue[ $elem ]?
|
|
725
|
-
|
|
|
726
|
-
'=' e=expression { $elem.value = $e.expr; }
|
|
727
734
|
;
|
|
728
735
|
|
|
729
736
|
defaultValue[ art ] locals[ elem, elements = {} ]
|
|
@@ -761,13 +768,13 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
761
768
|
|
|
|
762
769
|
requiredSemi
|
|
763
770
|
|
|
|
764
|
-
ELEMENTS
|
|
771
|
+
ELEMENTS { $art.elements = this.createDict(); } '{'
|
|
765
772
|
elementDefOrExtend[ $art ]*
|
|
766
773
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
767
774
|
{ this.checkExtensionDict( $art.elements ); }
|
|
768
775
|
optionalSemi
|
|
769
776
|
|
|
|
770
|
-
ENUM
|
|
777
|
+
ENUM { $art.enum = this.createDict(); } '{'
|
|
771
778
|
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
772
779
|
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
773
780
|
optionalSemi
|
|
@@ -782,24 +789,22 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
782
789
|
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
783
790
|
requiredSemi
|
|
784
791
|
|
|
|
785
|
-
DEFINITIONS
|
|
786
|
-
'{' { $art.artifacts = this.createDict(); }
|
|
792
|
+
DEFINITIONS { $art.artifacts = this.createDict(); } '{'
|
|
787
793
|
artifactDefOrExtend[ $art, 'definitions' ]*
|
|
788
794
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
789
795
|
optionalSemi
|
|
790
796
|
|
|
|
791
|
-
COLUMNS
|
|
792
|
-
'{' { $art.columns = []; }
|
|
797
|
+
COLUMNS { $art.columns = this.createArray(); } '{'
|
|
793
798
|
(
|
|
794
799
|
selectItemDef[ $art.columns ]
|
|
795
800
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
796
801
|
selectItemDef[ $art.columns ]
|
|
797
802
|
)*
|
|
798
803
|
)?
|
|
799
|
-
'}'
|
|
804
|
+
'}' { this.finalizeDictOrArray( $art.columns ); }
|
|
800
805
|
optionalSemi
|
|
801
806
|
|
|
|
802
|
-
ACTIONS
|
|
807
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
803
808
|
actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
|
|
804
809
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
805
810
|
optionalSemi
|
|
@@ -811,7 +816,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
811
816
|
extendService[ art, outer ] locals[ name = {} ]
|
|
812
817
|
@after { this.attachLocation( $art ); }
|
|
813
818
|
:
|
|
814
|
-
SERVICE { $art.expectedKind =
|
|
819
|
+
SERVICE { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
815
820
|
simplePath[ $name, 'Service' ]
|
|
816
821
|
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
817
822
|
( WITH { this.noSemicolonHere(); } )?
|
|
@@ -821,7 +826,7 @@ extendService[ art, outer ] locals[ name = {} ]
|
|
|
821
826
|
extendContext[ art, outer ] locals[ name = {} ]
|
|
822
827
|
@after { this.attachLocation( $art ); }
|
|
823
828
|
:
|
|
824
|
-
CONTEXT { $art.expectedKind =
|
|
829
|
+
CONTEXT { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
825
830
|
simplePath[ $name, 'Context' ]
|
|
826
831
|
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
827
832
|
( WITH { this.noSemicolonHere(); } )?
|
|
@@ -831,8 +836,9 @@ extendContext[ art, outer ] locals[ name = {} ]
|
|
|
831
836
|
extendEntityOrAspect[ art, outer ] locals[ name = {} ]
|
|
832
837
|
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
833
838
|
:
|
|
834
|
-
|
|
835
|
-
|
|
839
|
+
(ASPECT | ENTITY) { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
840
|
+
simplePath[ $name, 'Extend' ]
|
|
841
|
+
{ $art.name = $name;
|
|
836
842
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
837
843
|
}
|
|
838
844
|
(
|
|
@@ -858,13 +864,13 @@ extendForEntity[ art ]
|
|
|
858
864
|
elementDefOrExtend[ $art ]*
|
|
859
865
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
860
866
|
(
|
|
861
|
-
ACTIONS
|
|
867
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
862
868
|
actionFunctionDef[ $art ]*
|
|
863
869
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
864
870
|
)?
|
|
865
871
|
optionalSemi
|
|
866
872
|
|
|
|
867
|
-
ACTIONS
|
|
873
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
868
874
|
actionFunctionDef[ $art ]*
|
|
869
875
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
870
876
|
optionalSemi
|
|
@@ -875,30 +881,31 @@ extendForEntity[ art ]
|
|
|
875
881
|
extendProjection[ art, outer ] locals[ name = {} ]
|
|
876
882
|
@after { this.attachLocation( $art ); }
|
|
877
883
|
:
|
|
878
|
-
|
|
879
|
-
|
|
884
|
+
PROJECTION { $art.expectedKind = this.valueWithTokenLocation( 'entity' ); }
|
|
885
|
+
simplePath[ $name, 'Extend' ]
|
|
886
|
+
{ $art.name = $name;
|
|
880
887
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
881
888
|
}
|
|
882
889
|
( WITH { this.noSemicolonHere(); } )?
|
|
883
890
|
{ this.docComment( $art ); }
|
|
884
891
|
annotationAssignment_ll1[ $art ]*
|
|
885
892
|
(
|
|
886
|
-
'{' { $art.columns =
|
|
893
|
+
'{' { $art.columns = this.createArray(); }
|
|
887
894
|
(
|
|
888
895
|
selectItemDef[ $art.columns ]
|
|
889
896
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
890
897
|
selectItemDef[ $art.columns ]
|
|
891
898
|
)*
|
|
892
899
|
)?
|
|
893
|
-
'}'
|
|
900
|
+
'}' { this.finalizeDictOrArray( $art.columns ); }
|
|
894
901
|
(
|
|
895
|
-
ACTIONS
|
|
902
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
896
903
|
actionFunctionDef[ $art ]*
|
|
897
904
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
898
905
|
)?
|
|
899
906
|
optionalSemi
|
|
900
907
|
|
|
|
901
|
-
ACTIONS
|
|
908
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
902
909
|
actionFunctionDef[ $art ]*
|
|
903
910
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
904
911
|
optionalSemi
|
|
@@ -910,8 +917,9 @@ extendProjection[ art, outer ] locals[ name = {} ]
|
|
|
910
917
|
extendType[ art, outer ] locals[ name = {} ]
|
|
911
918
|
@after { this.attachLocation( $art ); }
|
|
912
919
|
:
|
|
913
|
-
TYPE
|
|
914
|
-
|
|
920
|
+
TYPE { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
921
|
+
simplePath[ $name, 'Extend' ]
|
|
922
|
+
{ $art.name = $name;
|
|
915
923
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
916
924
|
}
|
|
917
925
|
// extendWithOptElementsOrType + includeRef:
|
|
@@ -952,13 +960,13 @@ extendWithOptElementsOrType[ art ]
|
|
|
952
960
|
{ this.checkExtensionDict( $art.elements ); }
|
|
953
961
|
optionalSemi
|
|
954
962
|
|
|
|
955
|
-
ELEMENTS
|
|
963
|
+
ELEMENTS { $art.elements = this.createDict(); } '{'
|
|
956
964
|
elementDefOrExtend[ $art ]*
|
|
957
965
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
958
966
|
{ this.checkExtensionDict( $art.elements ); }
|
|
959
967
|
optionalSemi
|
|
960
968
|
|
|
|
961
|
-
ENUM
|
|
969
|
+
ENUM { $art.enum = this.createDict(); } '{'
|
|
962
970
|
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
963
971
|
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
964
972
|
optionalSemi
|
|
@@ -1006,7 +1014,7 @@ extendElement[ art, outer ]
|
|
|
1006
1014
|
@after{ this.attachLocation( $art ); }
|
|
1007
1015
|
:
|
|
1008
1016
|
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^([:{@=}()]|WITH)$/i ); }
|
|
1009
|
-
(
|
|
1017
|
+
( ELEMENT { $art.expectedKind = this.valueWithTokenLocation(); } )?
|
|
1010
1018
|
name=ident['Element']
|
|
1011
1019
|
{ this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
|
|
1012
1020
|
extendWithOptElementsOrType[ $art, $art ]
|
|
@@ -1016,7 +1024,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1016
1024
|
@after { this.attachLocation( $art ); }
|
|
1017
1025
|
:
|
|
1018
1026
|
simplePath[ $name, 'Annotate' ]
|
|
1019
|
-
( ':' simplePath[ $elemName, 'Element'] )?
|
|
1027
|
+
( ':' simplePath[ $elemName, 'Element'] )? // TODO: do like for extendArtifact !
|
|
1020
1028
|
{ this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
|
|
1021
1029
|
( WITH { this.noSemicolonHere(); } )?
|
|
1022
1030
|
{ this.docComment( $art ); }
|
|
@@ -1027,16 +1035,14 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1027
1035
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1028
1036
|
{ this.checkExtensionDict( $art.elements ); }
|
|
1029
1037
|
(
|
|
1030
|
-
ACTIONS
|
|
1031
|
-
'{' { $art.actions = this.createDict(); }
|
|
1038
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
1032
1039
|
annotateAction[ $art ]*
|
|
1033
1040
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1034
1041
|
{ this.checkExtensionDict( $art.actions ); }
|
|
1035
1042
|
)?
|
|
1036
1043
|
optionalSemi
|
|
1037
1044
|
|
|
|
1038
|
-
ACTIONS
|
|
1039
|
-
'{' { $art.actions = this.createDict(); }
|
|
1045
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
1040
1046
|
annotateAction[ $art ]*
|
|
1041
1047
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1042
1048
|
{ this.checkExtensionDict( $art.actions ); }
|
|
@@ -1051,8 +1057,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1051
1057
|
{ this.checkExtensionDict( $art.params ); }
|
|
1052
1058
|
(
|
|
1053
1059
|
// TODO: set proper $art.returns
|
|
1054
|
-
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
1055
|
-
'{' { $art.elements = this.createDict(); }
|
|
1060
|
+
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1056
1061
|
annotateElement[ $art ]*
|
|
1057
1062
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1058
1063
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1062,8 +1067,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1062
1067
|
)
|
|
1063
1068
|
|
|
|
1064
1069
|
// TODO: set proper $art.returns
|
|
1065
|
-
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
1066
|
-
'{' { $art.elements = this.createDict(); }
|
|
1070
|
+
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1067
1071
|
annotateElement[ $art ]*
|
|
1068
1072
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1069
1073
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1114,8 +1118,7 @@ annotateAction [ outer ] locals [ art = {} ]
|
|
|
1114
1118
|
)?
|
|
1115
1119
|
(
|
|
1116
1120
|
// TODO: set proper $art.returns
|
|
1117
|
-
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
1118
|
-
'{' { $art.elements = this.createDict(); }
|
|
1121
|
+
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1119
1122
|
annotateElement[ $art ]*
|
|
1120
1123
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1121
1124
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1209,7 +1212,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1209
1212
|
@after{ /* #ATN 3 */ }
|
|
1210
1213
|
:
|
|
1211
1214
|
typeStruct[ $art ]
|
|
1212
|
-
|
|
1215
|
+
( nullability[ $art ]
|
|
1216
|
+
requiredSemi
|
|
1217
|
+
| optionalSemi
|
|
1218
|
+
)
|
|
1213
1219
|
|
|
|
1214
1220
|
':'
|
|
1215
1221
|
// #ATN: typeRefOptArgs can start with ARRAY or MANY or ASSOCIATION or TYPE or LOCALIZED
|
|
@@ -1217,7 +1223,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1217
1223
|
{ this.setLocalToken( 'MANY', 'HelperToken1', /^[^\{]/ ); }
|
|
1218
1224
|
(
|
|
1219
1225
|
typeStruct[ $art ]
|
|
1220
|
-
|
|
1226
|
+
( nullability[ $art ]
|
|
1227
|
+
requiredSemi
|
|
1228
|
+
| optionalSemi
|
|
1229
|
+
)
|
|
1221
1230
|
|
|
|
1222
1231
|
typeAssociationBase[ $art, false ]
|
|
1223
1232
|
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
@@ -1229,7 +1238,7 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1229
1238
|
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
1230
1239
|
typeStruct[ $art.items ]
|
|
1231
1240
|
nullability[ $art.items ]?
|
|
1232
|
-
optionalSemi
|
|
1241
|
+
optionalSemi // TODO(v4): Should be requiredSemi with nullability
|
|
1233
1242
|
|
|
|
1234
1243
|
(
|
|
1235
1244
|
array=ARRAY of=OF
|
|
@@ -1240,7 +1249,7 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1240
1249
|
// #ATN: typeRefOptArgs can start with TYPE
|
|
1241
1250
|
( typeStruct[ $art.items ]
|
|
1242
1251
|
nullability[ $art.items ]?
|
|
1243
|
-
optionalSemi
|
|
1252
|
+
optionalSemi // TODO(v4): Should be requiredSemi with nullability
|
|
1244
1253
|
| ( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
|
|
1245
1254
|
nullability[ $art.items ]?
|
|
1246
1255
|
{ this.docComment( $art ); }
|
|
@@ -1261,13 +1270,14 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1261
1270
|
)
|
|
1262
1271
|
|
|
|
1263
1272
|
typeTypeOf[ $art ]
|
|
1264
|
-
|
|
1273
|
+
defaultAndNullablity[ $art ]?
|
|
1265
1274
|
{ this.docComment( $art ); }
|
|
1266
|
-
annotationAssignment_ll1[ $art ]*
|
|
1275
|
+
annotationAssignment_ll1[ $art ]*
|
|
1276
|
+
requiredSemi
|
|
1267
1277
|
|
|
|
1268
1278
|
l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1269
1279
|
typeRefOptArgs[ $art ]
|
|
1270
|
-
|
|
1280
|
+
defaultAndNullablity[ $art ]?
|
|
1271
1281
|
{ this.docComment( $art ); }
|
|
1272
1282
|
annotationAssignment_ll1[ $art ]*
|
|
1273
1283
|
requiredSemi
|
|
@@ -1291,11 +1301,11 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1291
1301
|
(
|
|
1292
1302
|
optionalSemi
|
|
1293
1303
|
|
|
|
1294
|
-
|
|
1304
|
+
defaultAndNullablity[ $art ]
|
|
1295
1305
|
requiredSemi
|
|
1296
1306
|
)
|
|
1297
1307
|
|
|
|
1298
|
-
|
|
1308
|
+
defaultAndNullablity[ $art ]?
|
|
1299
1309
|
requiredSemi
|
|
1300
1310
|
)
|
|
1301
1311
|
|
|
|
@@ -1305,7 +1315,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1305
1315
|
includeRef[ $art ]
|
|
1306
1316
|
)*
|
|
1307
1317
|
typeStruct[ $art ]
|
|
1308
|
-
optionalSemi
|
|
1318
|
+
( optionalSemi
|
|
1319
|
+
| nullability[ $art ]
|
|
1320
|
+
requiredSemi
|
|
1321
|
+
)
|
|
1309
1322
|
)
|
|
1310
1323
|
)
|
|
1311
1324
|
;
|
|
@@ -1751,7 +1764,7 @@ tableTerm returns [ table ]
|
|
|
1751
1764
|
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
|
|
1752
1765
|
| a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
|
|
1753
1766
|
// not using ident` to have a similar behavior to above
|
|
1754
|
-
)
|
|
1767
|
+
)?
|
|
1755
1768
|
|
|
|
1756
1769
|
te=tableExpression close=')'
|
|
1757
1770
|
{ $table = this.surroundByParens( $te.table, $open, $close ); }
|
|
@@ -2045,8 +2058,21 @@ conditionEOF returns [ cond ]
|
|
|
2045
2058
|
c=condition { $cond = $c.cond; } EOF
|
|
2046
2059
|
;
|
|
2047
2060
|
|
|
2048
|
-
condition returns [ cond ] locals[ args = [] ]
|
|
2049
|
-
@after{ $cond = this.argsExpression( $args, true ); }
|
|
2061
|
+
condition returns [ cond ] locals [ args = [], orl = [] ]
|
|
2062
|
+
@after{ $cond = this.argsExpression( $args, $ctx.e2 ? '?:' : true ); }
|
|
2063
|
+
:
|
|
2064
|
+
c1=conditionOr { $args.push($c1.cond); }
|
|
2065
|
+
(
|
|
2066
|
+
// '?' is not mentioned in code completion, see errorStrategy.js#intervalSetToArray
|
|
2067
|
+
q='?' { this.pushXprToken( $args ); }
|
|
2068
|
+
e2=expression { $args.push($e2.expr); }
|
|
2069
|
+
colon=':' { this.pushXprToken( $args ); }
|
|
2070
|
+
e3=expression { $args.push($e3.expr); }
|
|
2071
|
+
)?
|
|
2072
|
+
;
|
|
2073
|
+
|
|
2074
|
+
conditionOr returns [ cond ] locals [ args = [], orl = [] ]
|
|
2075
|
+
@after{ $cond = this.argsExpression( $args, 'or' ); }
|
|
2050
2076
|
:
|
|
2051
2077
|
c1=conditionAnd { $args.push($c1.cond); }
|
|
2052
2078
|
(
|
|
@@ -2349,7 +2375,7 @@ namedExpression[ pathStep, id ]
|
|
|
2349
2375
|
:
|
|
2350
2376
|
elem=expression
|
|
2351
2377
|
{ if ($pathStep && $id) {
|
|
2352
|
-
this.addDef( ($ctx.elem) ? $elem.expr : { location: $id.location },
|
|
2378
|
+
this.addDef( ($ctx.elem && $elem.expr) ? $elem.expr : { location: $id.location },
|
|
2353
2379
|
$pathStep, 'args', 0, $id );
|
|
2354
2380
|
}
|
|
2355
2381
|
}
|
|
@@ -2557,7 +2583,7 @@ annotationAssignment_fix[ art ] locals[ assignment ]
|
|
|
2557
2583
|
|
|
|
2558
2584
|
{ $assignment = { name: {} }; }
|
|
2559
2585
|
annotationPath[ $assignment.name, 'anno' ]
|
|
2560
|
-
annotationPathVariant[ $assignment.name ]?
|
|
2586
|
+
( '#' annotationPathVariant[ $assignment.name ] )?
|
|
2561
2587
|
{ this.warnIfColonFollows( $assignment ); }
|
|
2562
2588
|
)
|
|
2563
2589
|
;
|
|
@@ -2575,7 +2601,7 @@ annotationAssignment_ll1[ art ] locals[ assignment ]
|
|
|
2575
2601
|
|
|
|
2576
2602
|
{ $assignment = { name: {} }; }
|
|
2577
2603
|
annotationPath[ $assignment.name, 'anno' ]
|
|
2578
|
-
annotationPathVariant[ $assignment.name ]?
|
|
2604
|
+
( '#' annotationPathVariant[ $assignment.name ] )?
|
|
2579
2605
|
(
|
|
2580
2606
|
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2581
2607
|
val=annoValue[ $assignment ]
|
|
@@ -2601,11 +2627,7 @@ annotationAssignment_atn[ art ] locals[ assignment ]
|
|
|
2601
2627
|
// before an "expression" which can start with a '#' for an enum value
|
|
2602
2628
|
// -> used to introduce variant name if and only if in same line as previous token
|
|
2603
2629
|
{ this.setLocalToken( '#', 'HelperToken1', null, true ); }
|
|
2604
|
-
(
|
|
2605
|
-
hash=HelperToken1
|
|
2606
|
-
{ this.meltKeywordToIdentifier();; this.reportUnexpectedSpace( $hash ); }
|
|
2607
|
-
variant=ident['variant'] { $assignment.name.variant = $variant.id; }
|
|
2608
|
-
)?
|
|
2630
|
+
( hash=HelperToken1 annotationPathVariant[ $assignment.name ] )?
|
|
2609
2631
|
// ':' is in the follow set of this rule, as it is used in rule "selectItemDef"
|
|
2610
2632
|
// before an "expression" which can start with a ':' for a parameter reference
|
|
2611
2633
|
// -> used to introduce assignment value if and only if in same line as previous token
|
|
@@ -2617,11 +2639,7 @@ annotationAssignment_atn[ art ] locals[ assignment ]
|
|
|
2617
2639
|
|
|
|
2618
2640
|
atv='@'? annotationPath[ $assignment, 'ref', $atv ]
|
|
2619
2641
|
{ this.setLocalToken( '#', 'HelperToken1', null, true ); } // see above
|
|
2620
|
-
(
|
|
2621
|
-
hash=HelperToken1
|
|
2622
|
-
{ this.meltKeywordToIdentifier();; this.reportUnexpectedSpace( $hash ); }
|
|
2623
|
-
variant=ident['variant'] { $assignment.variant = $variant.id; }
|
|
2624
|
-
)?
|
|
2642
|
+
( hash=HelperToken1 annotationPathVariant[ $assignment ] )?
|
|
2625
2643
|
)
|
|
2626
2644
|
)?
|
|
2627
2645
|
)
|
|
@@ -2632,11 +2650,11 @@ annotationAssignment_paren[ art ]
|
|
|
2632
2650
|
'('
|
|
2633
2651
|
// allow completely useless `@()`; no warning anymore - who cares?
|
|
2634
2652
|
{
|
|
2635
|
-
this.meltKeywordToIdentifier();
|
|
2636
2653
|
if (this.isStraightBefore(')')) {
|
|
2637
2654
|
this.matchWildcard(); // we know it is the ')' - we do not reach the final match
|
|
2638
2655
|
return $ctx;
|
|
2639
2656
|
}
|
|
2657
|
+
this.meltKeywordToIdentifier();
|
|
2640
2658
|
}
|
|
2641
2659
|
annotationAssignment_1[ $art ]
|
|
2642
2660
|
( ','
|
|
@@ -2653,7 +2671,7 @@ annotationAssignment_1[ art ] locals[ assignment = { name: {} } ]
|
|
|
2653
2671
|
@after { this.assignAnnotation( $art, $assignment ); }
|
|
2654
2672
|
:
|
|
2655
2673
|
annotationPath[ $assignment.name, 'anno' ]
|
|
2656
|
-
annotationPathVariant[ $assignment.name ]?
|
|
2674
|
+
( '#' annotationPathVariant[ $assignment.name ] )?
|
|
2657
2675
|
(
|
|
2658
2676
|
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2659
2677
|
val=annoValue[ $assignment ]
|
|
@@ -2678,11 +2696,11 @@ annotationPath[ art, category, headat = null ] locals[ _sync = 'nop' ]
|
|
|
2678
2696
|
)*
|
|
2679
2697
|
;
|
|
2680
2698
|
|
|
2699
|
+
// Before calling this rule, match '#'
|
|
2681
2700
|
annotationPathVariant[ art ] locals[ variant = {} ]
|
|
2682
2701
|
@after { this.attachLocation($art); }
|
|
2683
2702
|
:
|
|
2684
|
-
|
|
2685
|
-
hash='#' { this.meltKeywordToIdentifier();; this.reportUnexpectedSpace( $hash ); }
|
|
2703
|
+
{ this.reportUnexpectedSpace();; this.meltKeywordToIdentifier(); }
|
|
2686
2704
|
simplePath[ $variant, 'variant' ] { $art.variant = $variant; }
|
|
2687
2705
|
;
|
|
2688
2706
|
|
|
@@ -2693,7 +2711,7 @@ annoValue[ assignment ]
|
|
|
2693
2711
|
// no docComment() here
|
|
2694
2712
|
// this alternative is done with token rewrite in rule "annotationAssignment_atn"
|
|
2695
2713
|
at='@'? annotationPath[ $assignment, 'ref', $at ]
|
|
2696
|
-
annotationPathVariant[ $assignment ]?
|
|
2714
|
+
( '#' annotationPathVariant[ $assignment ] )?
|
|
2697
2715
|
;
|
|
2698
2716
|
|
|
2699
2717
|
annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
@@ -2713,6 +2731,7 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
|
2713
2731
|
|
|
|
2714
2732
|
'[' // no need for createArray() here, $assignment.location is set
|
|
2715
2733
|
{ $assignment.val = []; $assignment.literal = 'array'; }
|
|
2734
|
+
{ this.meltKeywordToIdentifier(true); }
|
|
2716
2735
|
(
|
|
2717
2736
|
(
|
|
2718
2737
|
head=annoSubValue { $assignment.val.push( $head.val ); }
|
|
@@ -2727,6 +2746,7 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
|
2727
2746
|
)
|
|
2728
2747
|
(
|
|
2729
2748
|
',' { if (this.isStraightBefore(']')) break; } // allow ',' before ']'
|
|
2749
|
+
{ this.meltKeywordToIdentifier(true); }
|
|
2730
2750
|
(
|
|
2731
2751
|
tail=annoSubValue { $assignment.val.push( $tail.val ); }
|
|
2732
2752
|
|
|
|
@@ -2761,19 +2781,14 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
|
2761
2781
|
cond=condition // 'condition' is also used in 'expression' inside '()'.
|
|
2762
2782
|
// TODO: (1,2,3) not supported, yet, only ((1,2,3)); we could support it via:
|
|
2763
2783
|
// (',' condition)*
|
|
2764
|
-
{ $assignment
|
|
2765
|
-
if (!this.isBetaEnabled(this.options, 'annotationExpressions')) {
|
|
2766
|
-
this.error( 'syntax-unsupported-expression', [ $cond.cond.location ], {},
|
|
2767
|
-
'Expressions in annotation values are not supported' );
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2784
|
+
{ this.expressionAsAnnotationValue( $assignment, $ctx.cond ); }
|
|
2770
2785
|
')'
|
|
2771
2786
|
;
|
|
2772
2787
|
|
|
2773
2788
|
flattenedValue[ assignment ] locals[ val = { name: {} } ]
|
|
2774
2789
|
:
|
|
2775
2790
|
at='@'? annotationPath[ $val.name, 'name', $at ]
|
|
2776
|
-
( annotationPathVariant[ $val.name ] )?
|
|
2791
|
+
( '#' annotationPathVariant[ $val.name ] )?
|
|
2777
2792
|
(
|
|
2778
2793
|
':' { this.meltKeywordToIdentifier(true); } // allow path as anno value start with reserved
|
|
2779
2794
|
annoValue[ $val ]
|
|
@@ -2784,7 +2799,8 @@ flattenedValue[ assignment ] locals[ val = { name: {} } ]
|
|
|
2784
2799
|
namedValue[ struct ] locals[ val = { name: {} } ]
|
|
2785
2800
|
:
|
|
2786
2801
|
at='@'? annotationPath[ $val.name, 'name', $at ]
|
|
2787
|
-
( ':'
|
|
2802
|
+
( ':' { this.meltKeywordToIdentifier(true); }
|
|
2803
|
+
sub=annoSubValue { Object.assign( $val, $sub.val ); } )?
|
|
2788
2804
|
{
|
|
2789
2805
|
if (!$val.location) $val.location = $val.name.location;
|
|
2790
2806
|
this.addDef( $val, $struct, 'struct', null, $val.name ); // TODO: re-check name
|
|
@@ -2809,8 +2825,10 @@ annoSubValue returns[ val = {} ]
|
|
|
2809
2825
|
|
|
|
2810
2826
|
'[' // no need for createArray() here, $val.location is set
|
|
2811
2827
|
{ $val.val = []; $val.literal = 'array'; }
|
|
2828
|
+
{ this.meltKeywordToIdentifier(true); }
|
|
2812
2829
|
( head=annoSubValue { $val.val.push( $head.val ); }
|
|
2813
2830
|
( ',' { if (this.isStraightBefore(']')) break; } // allow ',' before ']'
|
|
2831
|
+
{ this.meltKeywordToIdentifier(true); }
|
|
2814
2832
|
tail=annoSubValue { $val.val.push( $tail.val ); }
|
|
2815
2833
|
)*
|
|
2816
2834
|
)?
|
|
@@ -2822,7 +2840,12 @@ annoSubValue returns[ val = {} ]
|
|
|
2822
2840
|
{ Object.assign( $val, this.numberLiteral( $num, $plus||$min ) ); }
|
|
2823
2841
|
|
|
|
2824
2842
|
at='@'? annotationPath[ $val, 'ref', $at ]
|
|
2825
|
-
( annotationPathVariant[ $val ] )?
|
|
2843
|
+
( '#' annotationPathVariant[ $val ] )?
|
|
2844
|
+
|
|
|
2845
|
+
'('
|
|
2846
|
+
cond=condition
|
|
2847
|
+
{ this.expressionAsAnnotationValue( $val, $ctx.cond ); }
|
|
2848
|
+
')'
|
|
2826
2849
|
;
|
|
2827
2850
|
|
|
2828
2851
|
// Literal values and identifiers -----------------------------------------------
|
|
@@ -2831,7 +2854,7 @@ literalValue returns[ val ] locals[ tok ]
|
|
|
2831
2854
|
@init{ $tok = this.getCurrentToken(); }
|
|
2832
2855
|
@after { this.attachLocation($val); }
|
|
2833
2856
|
:
|
|
2834
|
-
hash='#' { this.
|
|
2857
|
+
hash='#' { this.reportUnexpectedSpace( $hash );; this.meltKeywordToIdentifier(); }
|
|
2835
2858
|
name=ident['enumref'] // TODO v4: remove from this rule (not in enum!)
|
|
2836
2859
|
{ $val = { literal: 'enum', sym: $name.id } }
|
|
2837
2860
|
|
|
|
@@ -37,8 +37,21 @@ function isWhitespaceCharacterNoNewline( char ) {
|
|
|
37
37
|
return whitespaceRegEx.test(char);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Normalized CDL newlines to LF (\n). CDL newlines also contain PS and other
|
|
42
|
+
* characters, see cdlNewLineRegEx.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} str
|
|
45
|
+
* @return {string}
|
|
46
|
+
*/
|
|
47
|
+
function normalizeNewLine( str ) {
|
|
48
|
+
// Note: cdlNewLineRegEx does not have `g` flag and we can't use replaceAll in Node 14.
|
|
49
|
+
return str.replace(new RegExp(cdlNewLineRegEx, 'ug'), '\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
module.exports = {
|
|
41
53
|
isWhitespaceOrNewLineOnly,
|
|
42
54
|
isWhitespaceCharacterNoNewline,
|
|
43
55
|
cdlNewLineRegEx,
|
|
56
|
+
normalizeNewLine,
|
|
44
57
|
};
|
package/lib/main.d.ts
CHANGED
|
@@ -818,6 +818,23 @@ declare namespace compiler {
|
|
|
818
818
|
* @param dialect
|
|
819
819
|
*/
|
|
820
820
|
function delimitedId(name: string, dialect: string) : string;
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Return all non-lossy changes in artifacts between two given models.
|
|
824
|
+
* Note: Only supports changes in artifacts compiled/rendered as db-CSN/SQL.
|
|
825
|
+
*
|
|
826
|
+
* ATTENTION: This function may change without prior notice!
|
|
827
|
+
* It is still considered work-in-progress!
|
|
828
|
+
*
|
|
829
|
+
* @beta
|
|
830
|
+
*
|
|
831
|
+
* @param csn A client/inferred CSN model representing the desired "after-image"
|
|
832
|
+
* @param options SQL specific options
|
|
833
|
+
* @param beforeImage A db-transformed CSN representing the "before-image", or null in case no such image
|
|
834
|
+
* is known, i.e. for the very first migration step.
|
|
835
|
+
* @returns See {@link SqlMigrationResult} for details.
|
|
836
|
+
*/
|
|
837
|
+
function migration(csn: CSN, options: SqlOptions, beforeImage: CSN): SqlMigrationResult;
|
|
821
838
|
}
|
|
822
839
|
|
|
823
840
|
/**
|
|
@@ -884,10 +901,10 @@ declare namespace compiler {
|
|
|
884
901
|
* Return all changes in artifacts between two given models.
|
|
885
902
|
* Note: Only supports changes in entities (not views etc.) compiled/rendered as HANA-CSN/SQL.
|
|
886
903
|
*
|
|
887
|
-
* @param csn A
|
|
904
|
+
* @param csn A client/inferred CSN model representing the desired "after-image"
|
|
888
905
|
* @param options Options
|
|
889
|
-
* @param beforeImage A HANA-transformed CSN representing the "before-image", or null in case
|
|
890
|
-
*
|
|
906
|
+
* @param beforeImage A SAP HANA-transformed CSN representing the "before-image", or null in case
|
|
907
|
+
* no such image is known, i.e. for the very first migration step.
|
|
891
908
|
* @returns See {@link HdiMigrationResult} for details.
|
|
892
909
|
*/
|
|
893
910
|
function migration(csn: CSN, options: HdiOptions, beforeImage: CSN): HdiMigrationResult;
|
|
@@ -942,6 +959,29 @@ declare namespace compiler {
|
|
|
942
959
|
}>,
|
|
943
960
|
}
|
|
944
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Result type of {@link to.sql.migration}.
|
|
964
|
+
*
|
|
965
|
+
* ATTENTION: This structure may change without prior notice!
|
|
966
|
+
* It is still considered work-in-progress!
|
|
967
|
+
*
|
|
968
|
+
* @beta
|
|
969
|
+
*/
|
|
970
|
+
export type SqlMigrationResult = {
|
|
971
|
+
/**
|
|
972
|
+
* The desired after-image in db-transformed CSN format
|
|
973
|
+
*/
|
|
974
|
+
afterImage: CSN
|
|
975
|
+
/**
|
|
976
|
+
* An array of SQL statements to drop views/tables.
|
|
977
|
+
*/
|
|
978
|
+
drops: string[],
|
|
979
|
+
/**
|
|
980
|
+
* An array of SQL statements to ALTER/CREATE tables/views.
|
|
981
|
+
*/
|
|
982
|
+
createsAndAlters: string[],
|
|
983
|
+
}
|
|
984
|
+
|
|
945
985
|
/**
|
|
946
986
|
* Return the resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
947
987
|
* mode.
|