@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.
Files changed (70) hide show
  1. package/CHANGELOG.md +71 -4
  2. package/bin/cdsc.js +3 -0
  3. package/doc/CHANGELOG_ARCHIVE.md +6 -6
  4. package/doc/CHANGELOG_BETA.md +15 -0
  5. package/doc/DeprecatedOptions_v2.md +1 -1
  6. package/doc/NameResolution.md +1 -1
  7. package/lib/api/main.js +61 -22
  8. package/lib/api/options.js +1 -0
  9. package/lib/api/validate.js +5 -0
  10. package/lib/base/dictionaries.js +5 -3
  11. package/lib/base/keywords.js +2 -0
  12. package/lib/base/message-registry.js +64 -22
  13. package/lib/base/messages.js +12 -7
  14. package/lib/base/model.js +3 -2
  15. package/lib/checks/arrayOfs.js +1 -1
  16. package/lib/checks/defaultValues.js +1 -1
  17. package/lib/checks/hasPersistedElements.js +1 -1
  18. package/lib/checks/invalidTarget.js +1 -1
  19. package/lib/checks/onConditions.js +9 -6
  20. package/lib/checks/sql-snippets.js +2 -2
  21. package/lib/checks/types.js +1 -2
  22. package/lib/compiler/assert-consistency.js +25 -6
  23. package/lib/compiler/base.js +51 -2
  24. package/lib/compiler/builtins.js +15 -6
  25. package/lib/compiler/checks.js +4 -4
  26. package/lib/compiler/define.js +59 -80
  27. package/lib/compiler/extend.js +717 -498
  28. package/lib/compiler/finalize-parse-cdl.js +4 -3
  29. package/lib/compiler/index.js +1 -1
  30. package/lib/compiler/kick-start.js +2 -2
  31. package/lib/compiler/populate.js +17 -9
  32. package/lib/compiler/propagator.js +12 -5
  33. package/lib/compiler/resolve.js +26 -173
  34. package/lib/compiler/shared.js +20 -58
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +2 -2
  37. package/lib/edm/annotations/genericTranslation.js +124 -46
  38. package/lib/edm/csn2edm.js +22 -1
  39. package/lib/edm/edmPreprocessor.js +41 -21
  40. package/lib/gen/Dictionary.json +4 -0
  41. package/lib/gen/language.checksum +1 -1
  42. package/lib/gen/language.interp +3 -1
  43. package/lib/gen/languageLexer.js +1 -1
  44. package/lib/gen/languageParser.js +4844 -4508
  45. package/lib/inspect/inspectPropagation.js +20 -36
  46. package/lib/json/from-csn.js +56 -7
  47. package/lib/json/to-csn.js +71 -110
  48. package/lib/language/errorStrategy.js +1 -0
  49. package/lib/language/genericAntlrParser.js +49 -9
  50. package/lib/language/language.g4 +106 -83
  51. package/lib/language/textUtils.js +13 -0
  52. package/lib/main.d.ts +43 -3
  53. package/lib/main.js +4 -2
  54. package/lib/model/csnRefs.js +19 -4
  55. package/lib/model/csnUtils.js +11 -74
  56. package/lib/model/revealInternalProperties.js +3 -0
  57. package/lib/optionProcessor.js +3 -0
  58. package/lib/render/toCdl.js +203 -104
  59. package/lib/render/toHdbcds.js +0 -1
  60. package/lib/render/toRename.js +14 -51
  61. package/lib/transform/braceExpression.js +6 -0
  62. package/lib/transform/db/rewriteCalculatedElements.js +55 -14
  63. package/lib/transform/forOdataNew.js +20 -15
  64. package/lib/transform/forRelationalDB.js +21 -14
  65. package/lib/transform/parseExpr.js +2 -0
  66. package/lib/transform/transformUtilsNew.js +36 -9
  67. package/lib/transform/translateAssocsToJoins.js +11 -4
  68. package/lib/transform/universalCsn/coreComputed.js +15 -7
  69. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  70. package/package.json +2 -1
@@ -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.val !== undefined || $e.expr.sym !== undefined) &&
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 '{' { $art.elements = this.createDict(); }
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 '{' { $art.enum = this.createDict(); }
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 '{' { $art.actions = this.createDict(); }
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 = 'service'; }
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 = 'context'; }
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
- kind=(ASPECT | ENTITY) simplePath[ $name, 'Extend' ]
835
- { $art.expectedKind = $kind.text.toLowerCase(); $art.name = $name;
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 '{' { $art.actions = this.createDict(); }
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 '{' { $art.actions = this.createDict(); }
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
- expected=PROJECTION simplePath[ $name, 'Extend' ]
879
- { $art.expectedKind = 'entity'; $art.name = $name;
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 '{' { $art.actions = this.createDict(); }
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 '{' { $art.actions = this.createDict(); }
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 simplePath[ $name, 'Extend' ]
914
- { $art.expectedKind = 'type'; $art.name = $name;
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 '{' { $art.elements = this.createDict(); }
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 '{' { $art.enum = this.createDict(); }
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
- ( expected=ELEMENT { $art.expectedKind = 'element'; } )?
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
- optionalSemi
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
- optionalSemi
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
- defaultValue[ $art ]?
1273
+ defaultAndNullablity[ $art ]?
1265
1274
  { this.docComment( $art ); }
1266
- annotationAssignment_ll1[ $art ]* requiredSemi
1275
+ annotationAssignment_ll1[ $art ]*
1276
+ requiredSemi
1267
1277
  |
1268
1278
  l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
1269
1279
  typeRefOptArgs[ $art ]
1270
- defaultValue[ $art ]?
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
- defaultValue[ $art ]
1304
+ defaultAndNullablity[ $art ]
1295
1305
  requiredSemi
1296
1306
  )
1297
1307
  |
1298
- defaultValue[ $art ]?
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
- // TODO: warning for space after '#'
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.value = $cond.cond;
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
- ( ':' sub=annoSubValue { Object.assign( $val, $sub.val ); } )?
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.meltKeywordToIdentifier();; this.reportUnexpectedSpace( $hash ); }
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 clean input CSN representing the desired "after-image"
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 no such image
890
- * is known, i.e. for the very first migration step
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.