@sap/cds-compiler 3.7.2 → 3.8.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +63 -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 +24 -5
  23. package/lib/compiler/base.js +49 -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 +701 -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 +12 -53
  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 +4810 -4482
  45. package/lib/inspect/inspectPropagation.js +20 -36
  46. package/lib/json/from-csn.js +55 -5
  47. package/lib/json/to-csn.js +71 -110
  48. package/lib/language/errorStrategy.js +1 -0
  49. package/lib/language/genericAntlrParser.js +47 -8
  50. package/lib/language/language.g4 +88 -62
  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 +14 -2
  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
  }
@@ -2761,12 +2787,7 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
2761
2787
  cond=condition // 'condition' is also used in 'expression' inside '()'.
2762
2788
  // TODO: (1,2,3) not supported, yet, only ((1,2,3)); we could support it via:
2763
2789
  // (',' 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
- }
2790
+ { this.expressionAsAnnotationValue( $assignment, $ctx.cond ); }
2770
2791
  ')'
2771
2792
  ;
2772
2793
 
@@ -2823,6 +2844,11 @@ annoSubValue returns[ val = {} ]
2823
2844
  |
2824
2845
  at='@'? annotationPath[ $val, 'ref', $at ]
2825
2846
  ( annotationPathVariant[ $val ] )?
2847
+ |
2848
+ '('
2849
+ cond=condition
2850
+ { this.expressionAsAnnotationValue( $val, $ctx.cond ); }
2851
+ ')'
2826
2852
  ;
2827
2853
 
2828
2854
  // Literal values and identifiers -----------------------------------------------
@@ -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.
package/lib/main.js CHANGED
@@ -26,6 +26,7 @@ const compiler = lazyload('./compiler');
26
26
  const shared = lazyload('./compiler/shared');
27
27
  const define = lazyload('./compiler/define');
28
28
  const builtins = lazyload('./compiler/builtins');
29
+ const base = lazyload('./compiler/base');
29
30
  const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
30
31
 
31
32
  // The compiler version (taken from package.json)
@@ -157,9 +158,10 @@ module.exports = {
157
158
  // INTERNAL functions for the cds-lsp package and friends - before you use
158
159
  // it, you MUST talk with us - there can be potential incompatibilities with
159
160
  // new releases (even having the same major version):
160
- $lsp: { parse: (...args) => compiler.parseX(...args),
161
+ $lsp: {
162
+ parse: (...args) => compiler.parseX(...args),
161
163
  compile: (...args) => compiler.compileX(...args),
162
- getArtifactName: a => a.name
164
+ getArtifactName: (...args) => base.getArtifactName(...args),
163
165
  },
164
166
 
165
167
  // CSN Model related functionality
@@ -637,10 +637,18 @@ function csnRefs( csn, universalReady ) {
637
637
  /**
638
638
  * @param {CSN.Path} path
639
639
  * @param {CSN.Artifact} art
640
+ * @param {CSN.Artifact} parent
640
641
  * @param {string} [scope]
641
642
  * @param [extraInfo]
642
643
  */
643
644
  function resolvePath( path, art, parent, scope, extraInfo ) {
645
+ if (!art && path.length > 1) {
646
+ // TODO: For path.length===1, it may be that `art` is undefined, e.g. for CSN paths such
647
+ // as `[…, 'on', 1]` where the path segment refers to `=`.
648
+ // TODO: Check the call-side.
649
+ const loc = locationString(parent?.$location);
650
+ throw new ModelError(`Path item 0='${ pathId(path[0]) }' refers to nothing; in ${ loc }; path=${ JSON.stringify(path) }`);
651
+ }
644
652
  const staticAssoc = extraInfo === 'static' && scope === 'global';
645
653
  /** @type {{idx, art?, env?}[]} */
646
654
  const links = path.map( (_v, idx) => ({ idx }) );
@@ -706,8 +714,12 @@ function csnRefs( csn, universalReady ) {
706
714
  if (query !== main)
707
715
  cache.set( query, qcache );
708
716
 
709
- if (fromSelect)
710
- getCache( fromSelect, '$aliases' )[query.as] = qcache;
717
+ if (fromSelect) {
718
+ const $queryNumber = all.length + 1;
719
+ const alias = query.as || `$_select_${ $queryNumber }__`;
720
+ getCache(fromSelect, '$aliases')[alias] = qcache;
721
+ }
722
+
711
723
  const select = query.SELECT || query.projection;
712
724
  if (select) {
713
725
  cache.set( select, qcache ); // query and query.SELECT have the same cache qcache