@sap/cds-compiler 5.3.0 → 5.4.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 (50) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +2 -2
  4. package/lib/api/options.js +4 -2
  5. package/lib/base/builtins.js +0 -10
  6. package/lib/base/keywords.js +3 -31
  7. package/lib/base/message-registry.js +23 -5
  8. package/lib/base/messages.js +1 -1
  9. package/lib/checks/existsMustEndInAssoc.js +7 -2
  10. package/lib/checks/foreignKeys.js +12 -7
  11. package/lib/compiler/assert-consistency.js +11 -3
  12. package/lib/compiler/builtins.js +2 -0
  13. package/lib/compiler/checks.js +88 -38
  14. package/lib/compiler/define.js +2 -2
  15. package/lib/compiler/shared.js +9 -10
  16. package/lib/compiler/xpr-rewrite.js +11 -0
  17. package/lib/compiler/xsn-model.js +1 -1
  18. package/lib/edm/csn2edm.js +2 -0
  19. package/lib/edm/edm.js +2 -1
  20. package/lib/edm/edmPreprocessor.js +14 -1
  21. package/lib/edm/edmUtils.js +17 -2
  22. package/lib/gen/BaseParser.js +291 -197
  23. package/lib/gen/CdlParser.js +1631 -1605
  24. package/lib/gen/Dictionary.json +74 -6
  25. package/lib/gen/language.checksum +1 -1
  26. package/lib/gen/language.interp +1 -1
  27. package/lib/gen/languageParser.js +1808 -1804
  28. package/lib/language/antlrParser.js +8 -4
  29. package/lib/language/genericAntlrParser.js +3 -3
  30. package/lib/model/csnUtils.js +6 -1
  31. package/lib/optionProcessor.js +4 -0
  32. package/lib/parsers/AstBuildingParser.js +172 -108
  33. package/lib/parsers/CdlGrammar.g4 +154 -134
  34. package/lib/parsers/Lexer.js +3 -3
  35. package/lib/parsers/identifiers.js +59 -0
  36. package/lib/render/toCdl.js +5 -5
  37. package/lib/render/utils/common.js +5 -0
  38. package/lib/render/utils/delta.js +23 -5
  39. package/lib/transform/db/expansion.js +2 -1
  40. package/lib/transform/db/rewriteCalculatedElements.js +11 -5
  41. package/lib/transform/db/transformExists.js +52 -26
  42. package/lib/transform/effective/annotations.js +147 -0
  43. package/lib/transform/effective/main.js +17 -3
  44. package/lib/transform/forOdata.js +53 -10
  45. package/lib/transform/forRelationalDB.js +8 -1
  46. package/lib/transform/odata/createForeignKeys.js +180 -0
  47. package/lib/transform/odata/flattening.js +135 -19
  48. package/lib/transform/odata/typesExposure.js +4 -3
  49. package/lib/transform/transformUtils.js +6 -6
  50. package/package.json +1 -1
@@ -44,22 +44,22 @@ start returns[ source = new XsnSource( 'cdl' ) ]
44
44
  (
45
45
  ( <cond=namespaceRestriction> namespaceDeclaration[ $source ]
46
46
  | usingDeclaration[ $source ]
47
- | artifactDefOrExtend[ $source ] <setCondition=namespaceRestriction>
47
+ | artifactDefOrExtend[ $source ] <prepare=namespaceRestriction>
48
48
  )
49
- ( ';' | <exitLoop> | <repeatLoop=afterBrace> { this.noAssignmentInSameLine(); } )
49
+ ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
50
50
  )*
51
51
  EOF { this.docComment( null ); }
52
52
  ;
53
53
 
54
54
  artifactsBlock[ art, start = undefined ]
55
55
  :
56
- '{'<setCondition=vocabularyRestriction>
56
+ '{'<prepare=vocabularyRestriction>
57
57
  { $art.artifacts = this.createDict( $start ); $art.extensions = []; }
58
58
  (
59
59
  artifactDefOrExtend[ $art ]
60
- ( ';' | <exitLoop> | <repeatLoop=afterBrace> { this.noAssignmentInSameLine(); } )
60
+ ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
61
61
  )*
62
- '}'<setCondition=afterBrace>
62
+ '}'<prepare=afterBrace>
63
63
  { this.finalizeDictOrArray( $art.artifacts ); }
64
64
  ;
65
65
 
@@ -125,7 +125,7 @@ usingDeclaration[ source ] locals[ decl = { kind: 'using' } ] // TODO: XsnArtifa
125
125
  ( usingProxy[ $decl, { kind: 'using' } ]
126
126
  ( ',' | <exitLoop> )
127
127
  )+
128
- '}'<setCondition=afterBrace>
128
+ '}'<prepare=afterBrace>
129
129
  { this.finalizeDictOrArray( $decl.usings ); }
130
130
  ( FROM String
131
131
  { $source.dependencies.push( $decl.fileDep = this.quotedLiteral() ); }
@@ -186,7 +186,8 @@ annotationDef[ art, outer ]
186
186
  @finally{ this.attachLocation( $art ); }
187
187
  :
188
188
  ANNOTATION name=namePath[ 'AnnoDef' ]
189
- { this.addDef( $art, $outer, 'vocabularies', 'annotation', $name ); }
189
+ // make it also work with ignored <cond=vocabularyRestriction>:
190
+ { this.addDef( $art, $outer, ($outer.kind === 'source' ? 'vocabularies' : 'artifacts'), 'annotation', $name ); }
190
191
  { this.docComment( $art ); } annoAssignMid[ $art ]*
191
192
  typeOrIncludesSpec[ $art ]
192
193
  ;
@@ -338,9 +339,9 @@ actionsBlock[ art ]
338
339
  ACTIONS { $art.actions = this.createDict(); } '{'
339
340
  (
340
341
  boundActionFunctionDef[ $art ]
341
- ( ';' | <exitLoop> | <repeatLoop=afterBrace> { this.noAssignmentInSameLine(); } )
342
+ ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
342
343
  )*
343
- '}'<setCondition=afterBrace>
344
+ '}'<prepare=afterBrace>
344
345
  { this.finalizeDictOrArray( $art.actions ); }
345
346
  ;
346
347
 
@@ -390,9 +391,9 @@ paramDef[ outer ] locals[ art = new XsnArtifact() ]
390
391
  ;
391
392
 
392
393
  returnsSpec[ outer ] locals[ art = new XsnArtifact() ]
393
- @finally{ this.attachLocation( $art ); }
394
+ @finally{ this.attachLocation( $art ); if ($ret) art.location.tokenIndex = $ret.location.tokenIndex; }
394
395
  :
395
- RETURNS<setCondition=elementRestriction>
396
+ ret=RETURNS <prepare=elementRestriction, arg=default>
396
397
  { $art.kind = 'param'; $outer.returns = $art; }
397
398
  { this.docComment( $art ); } annoAssignStd[ $art ]*
398
399
  typeExpression[ $art ]
@@ -404,10 +405,10 @@ elementsBlock[ art ]
404
405
  ( elementDef[ $art ]
405
406
  ( ';'
406
407
  | <exitLoop>
407
- | <repeatLoop=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
408
+ | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
408
409
  )
409
410
  )*
410
- '}'<setCondition=afterBrace>
411
+ '}'<prepare=afterBrace>
411
412
  { this.finalizeDictOrArray( $art.elements ); }
412
413
  ;
413
414
 
@@ -421,29 +422,27 @@ elementDef[ outer, art = undefined ]
421
422
  ( MASKED { $art.masked = this.valueWithLocation( true ); }
422
423
  { this.message( 'syntax-unsupported-masked', this.lb(), { keyword: 'masked' } ); } )?
423
424
  ( ELEMENT { $art.$syntax = 'element'; } )?
424
- Id['Element']
425
+ Id['Element'] <prepare=elementRestriction, arg=elem>
425
426
  { this.addDef( $art, $outer, 'elements', 'element', this.identAst() ); }
426
427
  { this.docComment( $art ); } annoAssignMid[ $art ]*
427
428
  (
428
429
  elementsBlock[ $art ]
429
430
  nullability[ $art ]?
430
431
  |
431
- ':'<setCondition=elementRestriction>
432
- typeExpression[ $art ] // was elementType, with NOT? NULL / DEFAULT
433
- |
434
- // <setCondition=elementRestriction> // TODO TOOL: allow this
435
- { this.elementRestriction(); }<always> // workaround
436
- )
432
+ ':' typeExpression[ $art ] // was elementType, with NOT? NULL / DEFAULT
433
+ )?
437
434
  (
438
- <cond=calcOrDefaultRestriction> '='
435
+ <cond=elementRestriction, arg=calc> '='
439
436
  // TODO TOOL: add to "expected set" if failing here? Or have some "do not
440
437
  // consider for rule exit if condition failure on `=`"?
441
438
  expr=expression { $art.value = $expr; }
442
439
  ( STORED { $art.value.stored = this.valueWithLocation( true ); } )?
443
440
  // TODO: why have `stored` as property of the value?
444
- { this.docComment( $art ); } // TODO: also restricted
445
- ( <cond=elementRestriction> annoAssignStd[ $art ] )*
446
- )?
441
+ { if (this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
442
+ ( <cond=elementRestriction, arg=anno> annoAssignStd[ $art ] )*
443
+ |
444
+ { this.docComment( $art, 'type' ); } // delayed from type expression
445
+ )
447
446
  ;
448
447
 
449
448
  enumSymbolsBlock[ art ]
@@ -452,7 +451,7 @@ enumSymbolsBlock[ art ]
452
451
  ( enumSymbolDef[ $art ]
453
452
  ( ';' | <exitLoop> )
454
453
  )*
455
- '}'<setCondition=afterBrace>
454
+ '}'<prepare=afterBrace>
456
455
  { this.finalizeDictOrArray( $art.enum ); }
457
456
  ;
458
457
 
@@ -521,17 +520,17 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
521
520
  // Annotate and Extend: main definitions ----------------------------------------
522
521
 
523
522
  annotateArtifact[ art, outer ]
524
- @finally{ this.attachLocation( $art ); }
523
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
525
524
  :
526
525
  name=namePath[ 'Ext' ]
527
526
  ( // direct element annotation:
528
527
  ':' elemName=namePath[ 'ExtElement']
529
528
  { this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
530
- WITH?
529
+ keyword=WITH?
531
530
  { this.docComment( $art ); } annoAssignStd[ $art ]*
532
531
  annotateElementsBlock[ $art ]?
533
532
  | // definition annotation
534
- WITH?
533
+ keyword=WITH?
535
534
  // <cond=noRuleExitAfterWith>), or as rule option,
536
535
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
537
536
  { this.addExtension( $art, $outer, 'annotate', $name ); }
@@ -547,13 +546,13 @@ annotateArtifact[ art, outer ]
547
546
  ;
548
547
 
549
548
  extendArtifact[ art, outer ]
550
- @finally{ this.attachLocation( $art ); }
549
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
551
550
  :
552
551
  name=namePath[ 'Ext' ]
553
552
  ( // direct element annotation:
554
553
  ':' elemName=namePath[ 'ExtElement']
555
554
  { this.addExtension( $art, $outer, 'extend', $name, $elemName.path ); }
556
- WITH?
555
+ keyword=WITH?
557
556
  { this.docComment( $art ); } annoAssignStd[ $art ]*
558
557
  (
559
558
  elements=ELEMENTS? extendElementsBlock[ $art, $elements ]
@@ -569,7 +568,7 @@ extendArtifact[ art, outer ]
569
568
  actionsBlock[ $art ]?
570
569
  )?
571
570
  |
572
- WITH
571
+ keyword=WITH
573
572
  { this.addExtension( $art, $outer, 'extend', $name ); }
574
573
  { this.docComment( $art ); } annoAssignStd[ $art ]*
575
574
  (
@@ -595,12 +594,12 @@ extendArtifact[ art, outer ]
595
594
  ;
596
595
 
597
596
  extendService[ art, outer ]
598
- @finally{ this.attachLocation( $art ); }
597
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
599
598
  :
600
599
  SERVICE { $art.expectedKind = this.valueWithLocation(); }
601
600
  name=namePath[ 'ExtService' ]
602
601
  { $art.name = $name; $outer.extensions.push( $art ); }
603
- WITH?
602
+ keyword=WITH?
604
603
  // <cond=noRuleExitAfterWith>), or as rule option,
605
604
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
606
605
  { this.docComment( $art ); } annoAssignStd[ $art ]*
@@ -608,12 +607,12 @@ extendService[ art, outer ]
608
607
  ;
609
608
 
610
609
  extendContext[ art, outer ]
611
- @finally{ this.attachLocation( $art ); }
610
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
612
611
  :
613
612
  CONTEXT { $art.expectedKind = this.valueWithLocation(); }
614
613
  name=namePath[ 'ExtContext' ]
615
614
  { $art.name = $name; $outer.extensions.push( $art ); }
616
- WITH?
615
+ keyword=WITH?
617
616
  // <cond=noRuleExitAfterWith>), or as rule option,
618
617
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
619
618
  { this.docComment( $art ); } annoAssignStd[ $art ]*
@@ -621,7 +620,7 @@ extendContext[ art, outer ]
621
620
  ;
622
621
 
623
622
  extendType[ art, outer ]
624
- @finally{ this.attachLocation( $art ); }
623
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
625
624
  :
626
625
  TYPE { $art.expectedKind = this.valueWithLocation(); }
627
626
  name=namePath[ 'Ext' ]
@@ -630,7 +629,7 @@ extendType[ art, outer ]
630
629
  { this.docComment( $art ); } annoAssignStd[ $art ]*
631
630
  extendElementsBlock[ $art ]?
632
631
  |
633
- WITH
632
+ keyword=WITH
634
633
  { this.docComment( $art ); } annoAssignStd[ $art ]*
635
634
  (
636
635
  incl=simplePath { $art.includes = [ $incl ]; }
@@ -647,7 +646,7 @@ extendType[ art, outer ]
647
646
  ;
648
647
 
649
648
  extendEntityOrAspect[ art, outer ]
650
- @finally{ this.attachLocation( $art ); }
649
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
651
650
  :
652
651
  ASPECT/ENTITY { $art.expectedKind = this.valueWithLocation(); }
653
652
  name=namePath[ 'Ext' ]
@@ -655,7 +654,7 @@ extendEntityOrAspect[ art, outer ]
655
654
  (
656
655
  { this.docComment( $art ); } annoAssignStd[ $art ]*
657
656
  |
658
- WITH
657
+ keyword=WITH
659
658
  { this.docComment( $art ); } annoAssignStd[ $art ]*
660
659
  (
661
660
  incl=simplePath { $art.includes = [ $incl ]; }
@@ -667,12 +666,12 @@ extendEntityOrAspect[ art, outer ]
667
666
  ;
668
667
 
669
668
  extendProjection[ art, outer ]
670
- @finally{ this.attachLocation( $art ); }
669
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
671
670
  :
672
671
  PROJECTION { $art.expectedKind = this.valueWithLocation(); }
673
672
  name=namePath[ 'Ext' ]
674
673
  { $art.name = $name; $outer.extensions.push( $art ); }
675
- WITH ?
674
+ keyword=WITH ?
676
675
  { this.docComment( $art ); } annoAssignStd[ $art ]*
677
676
  selectItemsList[ $art ]?
678
677
  actionsBlock[ $art ]?
@@ -684,9 +683,9 @@ annotateActionsBlock[ art ]
684
683
  :
685
684
  ACTIONS { $art.actions = this.createDict(); } '{'
686
685
  ( annotateBoundAction[ $art ]
687
- ( ';' | <exitLoop> | <repeatLoop=afterBrace> { this.noAssignmentInSameLine(); } )
686
+ ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
688
687
  )*
689
- '}'<setCondition=afterBrace>
688
+ '}'<prepare=afterBrace>
690
689
  { this.finalizeExtensionsDict( $art.actions ); }
691
690
  ;
692
691
 
@@ -722,9 +721,9 @@ annotateParam[ outer ] locals[ art = new XsnArtifact() ]
722
721
  ;
723
722
 
724
723
  annotateReturns[ outer ] locals[ art = new XsnArtifact() ]
725
- @finally{ this.attachLocation( $art ); }
724
+ @finally{ this.attachLocation( $art ); if ($ret) art.location.tokenIndex = $ret.location.tokenIndex; }
726
725
  :
727
- RETURNS { $outer.returns = $art; $art.kind = 'annotate'; } // TODO: tokenIndex necessary?
726
+ ret=RETURNS { $outer.returns = $art; $art.kind = 'annotate'; }
728
727
  { this.docComment( $art ); } annoAssignStd[ $art ]*
729
728
  annotateElementsBlock[ $art ]?
730
729
  ;
@@ -735,10 +734,10 @@ annotateElementsBlock[ art ]
735
734
  ( annotateElement[ $art ]
736
735
  ( ';'
737
736
  | <exitLoop>
738
- | <repeatLoop=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
737
+ | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
739
738
  )
740
739
  )*
741
- '}'<setCondition=afterBrace>
740
+ '}'<prepare=afterBrace>
742
741
  { this.finalizeExtensionsDict( $art.elements ); }
743
742
  ;
744
743
 
@@ -758,13 +757,14 @@ extendElementsBlock[ art, start = undefined ]
758
757
  ( elementDefOrExtend[ $art ]
759
758
  ( ';'
760
759
  | <exitLoop>
761
- | <repeatLoop=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
760
+ | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
762
761
  )*
763
- '}'<setCondition=afterBrace>
762
+ '}'<prepare=afterBrace>
764
763
  { this.finalizeExtensionsDict( $art.elements ); }
765
764
  ;
766
765
 
767
766
  elementDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
767
+ @finally{ this.checkWith( $keyword ); this.attachLocation( $art ); }
768
768
  :
769
769
  { this.docComment( $art ); } annoAssignStd[ $art ]*
770
770
  (
@@ -778,7 +778,7 @@ elementDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
778
778
  { this.docComment( $art ); } annoAssignStd[ $art ]*
779
779
  extendElementsBlock[ $art ]?
780
780
  |
781
- WITH
781
+ keyword=WITH
782
782
  { this.docComment( $art ); } annoAssignStd[ $art ]*
783
783
  (
784
784
  elements=ELEMENTS? extendElementsBlock[ art, $elements ]
@@ -848,71 +848,84 @@ typeOrIncludesSpec[ art ]
848
848
  typeExpression[ art ]
849
849
  // TODO: really introduce <exitRule>
850
850
  :
851
- elementsBlock[ $art ]
851
+ elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
852
852
  nullability[ $art ]?
853
853
  |
854
854
  ( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
855
855
  (<altRuleStart>)
856
856
  nullability[ $art ]?
857
- // <setCondition=calcOrDefaultRestriction> // TODO TOOL: allow this
858
- { this.calcOrDefaultRestriction(); }<always> // workaround
859
857
  (
860
858
  nullabilityAndDefault[ $art ]
861
- { this.docComment( $art ); } annoAssignStd[ $art ]*
859
+ // TODO TOOL: with <exitRule>, we could move the following to the end
860
+ (
861
+ { this.elementRestriction( false, 'calc' ); }
862
+ // <prepare=elementRestriction, arg=calc>
863
+ { this.docComment( $art ); } annoAssignStd[ $art ]+
864
+ |
865
+ // We could still have the calc expression after this → delay
866
+ { this.docComment( $art, 'elem' ); }
867
+ )
862
868
  |
863
- enumSymbolsBlock[ $art ]
864
- nullabilityAndDefault[ $art ]?
869
+ { this.docComment( $art, 'elem' ); }
865
870
  |
866
- { this.docComment( $art ); } annoAssignStd[ $art ]+
867
- ( enumSymbolsBlock[ $art ]
868
- // <setCondition=calcOrDefaultRestriction> // TODO TOOL: allow this
869
- { this.calcOrDefaultRestriction(); }<always> // workaround
871
+ { this.docComment( $art ); } annoAssignStd[ $art ]+ // +!
872
+ ( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
873
+ // TODO TOOL: written?
870
874
  nullabilityAndDefault[ $art ]?
871
875
  )?
872
876
  |
873
877
  { this.docComment( $art ); }
878
+ enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
879
+ nullabilityAndDefault[ $art ]?
874
880
  )
875
881
  |
876
882
  LOCALIZED
877
883
  { $art.localized = this.valueWithLocation( true ); }
878
884
  typeRefOptArgs[ $art ] // TODO: why no TYPE OF ?
879
- // <setCondition=calcOrDefaultRestriction> // TODO TOOL: allow this
880
- { this.calcOrDefaultRestriction(); }<always> // workaround
881
885
  nullabilityAndDefault[ $art ]?
882
- { this.docComment( $art ); } annoAssignStd[ $art ]*
886
+ // TODO TOOL: with <exitRule>, we could move the following to the end
887
+ (
888
+ { this.elementRestriction( false, 'calc' ); }
889
+ // <prepare=elementRestriction, arg=calc>
890
+ { this.docComment( $art ); } annoAssignStd[ $art ]+
891
+ |
892
+ { this.docComment( $art, 'elem' ); }
893
+ )
883
894
  |
884
- assoc=ASSOCIATION cardinality[ $art ]? TO card=ONE/MANY?
895
+ assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
896
+ cardinality[ $art ]? TO card=ONE/MANY?
885
897
  target=simplePath { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
886
- // final anno assignments allowed also with fkeys (also in ANTLR-based parser)
887
- // <prepare=calcOrDefaultRestriction,arg=true> // TODO TOOL: allow this
888
- { this.calcOrDefaultRestriction(false,true); }<always> // workaround
889
-
890
898
  ( ON cond=condition { $art.on = $cond; }
891
899
  | foreignKeysBlock[ $art ]?
892
900
  nullabilityAndDefault[ $art ]?
893
- // scalar default - hm..., what about calc expression?
901
+ // scalar default ? - hm..., what about calc expression?
894
902
  )
903
+ // final anno assignments allowed also with fkeys (no auto-`;` after them):
904
+ // (the "TODO TOOL" code for doc/annos would also be fine)
895
905
  { this.docComment( $art ); } annoAssignStd[ $art ]*
896
906
  |
897
- assoc=COMPOSITION cardinality[ $art ]? OF card=ONE/MANY?
907
+ assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
908
+ cardinality[ $art ]? OF card=ONE/MANY?
898
909
  (
899
910
  target=simplePath { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
900
- // final anno assignments allowed also with fkeys (also in ANTLR-based parser) - TODO: really?
901
- // <prepare=calcOrDefaultRestriction,arg=true> // TODO TOOL: allow this
902
- { this.calcOrDefaultRestriction(false,true); }<always> // workaround
903
911
  ( ON cond=condition { $art.on = $cond; }
904
912
  | foreignKeysBlock[ $art ]?
905
913
  nullabilityAndDefault[ $art ]?
906
914
  )
915
+ // final anno assignments allowed also with fkeys (no auto-`;` after them):
916
+ // (the "TODO TOOL" code for doc/annos would also be fine)
907
917
  { this.docComment( $art ); } annoAssignStd[ $art ]*
908
918
  |
909
- { $target = {}; this.setAssocAndComposition( $art, $assoc, $card, $target ); }
919
+ { $target = { location: this.startLocation( this.la() ) };
920
+ this.setAssocAndComposition( $art, $assoc, $card, $target ); }
910
921
  elementsBlock[ $target ]
911
922
  { $target.location = $target.elements[Symbol.for('cds.$location')]; }
912
923
  )
913
924
  |
914
- ( ARRAY OF { $art.items = { location: this.locationOfPrevTokens( 2 ) }; }
915
- | MANY { $art.items = { location: this.lb().location }; }
925
+ ( ARRAY <prepare=elementRestriction, arg=calc>
926
+ OF { $art.items = { location: this.locationOfPrevTokens( 2 ) }; }
927
+ | MANY <prepare=elementRestriction, arg=calc>
928
+ { $art.items = { location: this.lb().location }; }
916
929
  )
917
930
  (
918
931
  elementsBlock[ $art.items ]
@@ -921,9 +934,14 @@ typeExpression[ art ]
921
934
  ( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
922
935
  nullability[ $art.items ]?
923
936
  ( enumSymbolsBlock[ $art.items ]
924
- nullability[ $art.items ]? // TODO: only with enum
937
+ nullability[ $art.items ]?
925
938
  |
926
- { this.docComment( $art ); } annoAssignStd[ $art ]*
939
+ // TODO TOOL: with <exitRule>, we could move the following to the end
940
+ { this.elementRestriction( false, 'calc' ); }
941
+ // <prepare=elementRestriction, arg=calc>
942
+ { this.docComment( $art ); } annoAssignStd[ $art ]+
943
+ |
944
+ { this.docComment( $art, 'elem' ); }
927
945
  )
928
946
  )
929
947
  ;
@@ -969,6 +987,7 @@ typeRefOptArgs[ art ] locals[ type = $art.type ]
969
987
  | <exitLoop>
970
988
  )
971
989
  )* // TODO: really as loop?
990
+ { this.checkTypeArgs( $art ); } // might reset $art.$typeArgs
972
991
  |
973
992
  { $art.$typeArgs = this.createDict( $open ); }
974
993
  (
@@ -976,7 +995,7 @@ typeRefOptArgs[ art ] locals[ type = $art.type ]
976
995
  ( ',' | <exitLoop> )
977
996
  )+ // TODO: really as loop?
978
997
  )
979
- ')' { this.finalizeDictOrArray( $art.$typeArgs ); }
998
+ ')' { if ($art.$typeArgs) this.finalizeDictOrArray( $art.$typeArgs ); }
980
999
  )?
981
1000
  ;
982
1001
 
@@ -1043,11 +1062,11 @@ targetCardinality[ card, atAlt = false ]
1043
1062
  nullabilityAndDefault[ art ]
1044
1063
  :
1045
1064
  nullability[ $art ]
1046
- ( <cond=calcOrDefaultRestriction> DEFAULT expr=expression
1065
+ ( <cond=elementRestriction, arg=default> DEFAULT expr=expression
1047
1066
  { $art.default = $expr; }
1048
1067
  )?
1049
1068
  |
1050
- <cond=calcOrDefaultRestriction> DEFAULT expr=expression
1069
+ <cond=elementRestriction, arg=default> DEFAULT expr=expression
1051
1070
  { $art.default = $expr; }
1052
1071
  nullability[ $art ]?
1053
1072
  // TODO TOOL: when `followUnion` does not contain `Id`, `RuleEnd_` does not
@@ -1074,9 +1093,10 @@ projectionSpec returns[ default query = {} ]
1074
1093
  // TODO, currently just with simple ref
1075
1094
  PROJECTION { $query = { op: this.valueWithLocation( 'SELECT' ) }; }
1076
1095
  ON
1077
- // TODO: this <setCondition=afterBrace> is extremely strange... v6 forbid.
1078
- // Deliberately set this via action.
1079
- tab=fromRefWithOptAlias { this.afterBrace(); }<always>
1096
+ tab=fromRefWithOptAlias
1097
+ // TODO: this <prepare=afterBrace> is extremely strange... v6 forbid.
1098
+ // Deliberately set this via action (→ interpreter will not accept this)
1099
+ { this.afterBrace(); }<always>
1080
1100
  { $query.from = tab; }
1081
1101
  selectItemsList[ $query ]?
1082
1102
  excludingClause[ $query ]?
@@ -1158,7 +1178,7 @@ querySource[ query ]
1158
1178
  )?
1159
1179
  ;
1160
1180
 
1161
- tableExpression returns[ default expr ] // TableOrJoin
1181
+ tableExpression returns[ default expr = {} ] // TableOrJoin
1162
1182
  :
1163
1183
  ( tableOrQueryParens[ ...$ ]
1164
1184
  (<altRuleStart> { $expr = this.taggedIfQuery( $expr ); } )
@@ -1174,12 +1194,11 @@ tableExpression returns[ default expr ] // TableOrJoin
1174
1194
  )
1175
1195
  |
1176
1196
  ( ( join=INNER | join=LEFT/RIGHT/FULL OUTER? )
1177
- card=joinCardinality?
1178
- | { $join = undefined; } // TODO TOOL: action is lost
1197
+ card=joinCardinality? JOIN
1198
+ | JOIN { $join = undefined; }
1179
1199
  )
1180
- JOIN
1200
+ // TODO TOOL: allow zero-alt in choice in outer alt → JOIN can be moved outside
1181
1201
  { $expr = { op: this.valueWithLocation(), join: this.valueWithLocation( $join?.keyword || 'inner', $join ), args: [ $expr ] }; if ($card) $expr.cardinality = $card; $card = undefined; }
1182
- { $join = undefined; } // TODO TOOL bug workaround, see above
1183
1202
  tab=tableExpression
1184
1203
  { $expr.args.push( $tab ); this.attachLocation( $expr ); }
1185
1204
  ON cond=condition { $expr.on = $cond; }
@@ -1189,26 +1208,22 @@ tableExpression returns[ default expr ] // TableOrJoin
1189
1208
 
1190
1209
  tableOrQueryParens returns[ default expr ]
1191
1210
  :
1192
- '('
1211
+ '(' <prepare=queryOnLeft>
1193
1212
  ( <priority> tableOrQueryParens[ ...$ ]
1194
- ( <prec=-2, postfix> tableExpression[ ...$ ]<atAltStart>
1195
- | <prec=-1, postfix> queryExpression[ ...$ ]<atAltStart>
1213
+ ( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
1214
+ | <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1196
1215
  )?
1197
- | <prec=-2> tableExpression[ ...$ ]
1198
- | <prec=-1> queryExpression[ ...$ ]
1216
+ | tableExpression[ ...$ ] <prepare=queryOnLeft, arg=table>
1217
+ | queryExpression[ ...$ ]
1199
1218
  )
1200
1219
  ')'
1201
1220
  { this.surroundByParens( $expr ); }
1202
- ( <prec=-2, postfix=once> AS Id['FromAlias']
1203
- <setCondition=setPrecInCallingRule>
1221
+ ( <cond=queryOnLeft, arg=table> AS Id['FromAlias']
1204
1222
  { $expr = this.taggedIfQuery( $expr ); $expr.name = this.identAst(); }
1205
- | <hide, cond=tableAlias> Id_restricted['FromAlias']
1206
- <setCondition=setPrecInCallingRule>
1223
+ | <cond=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
1224
+ // TODO TOOL: shouldn't we have generated `default: this.giR()`?
1207
1225
  { $expr = this.taggedIfQuery( $expr ); $expr.name = this.fragileAlias(); }
1208
- |
1209
- // <setCondition=setPrecInCallingRule> // TODO TOOL: allow this
1210
- { this.setPrecInCallingRule(); }<always> // workaround
1211
- )
1226
+ )?
1212
1227
  ; // change #10799 for ANTLR-based parser
1213
1228
 
1214
1229
  joinCardinality returns[ sourceMax, targetMax ]
@@ -1229,9 +1244,10 @@ joinCardinality returns[ sourceMax, targetMax ]
1229
1244
  )
1230
1245
  ;
1231
1246
 
1232
- fromRefWithOptAlias returns[ default expr = { path: [] } ]
1247
+ fromRefWithOptAlias returns[ default expr = {} ]
1233
1248
  @finally{ this.attachLocation( $expr ); }
1234
1249
  :
1250
+ { $expr.path = []; }
1235
1251
  fromPath[ $expr, 'artref' ]
1236
1252
  (
1237
1253
  ':' { if (!$expr.scope) $expr.scope = $expr.path.length; else {
@@ -1243,7 +1259,8 @@ fromRefWithOptAlias returns[ default expr = { path: [] } ]
1243
1259
  (
1244
1260
  AS Id['FromAlias'] { $expr.name = this.identAst(); }
1245
1261
  |
1246
- <hide, cond=tableAlias>
1262
+ <cond=tableWithoutAs>
1263
+ // TODO: probably not necessary, TOOL already uses `default: this.giR()`
1247
1264
  Id_restricted['FromAlias']
1248
1265
  { $expr.name = this.fragileAlias(); }
1249
1266
  |
@@ -1335,13 +1352,13 @@ excludingClause[ query ]
1335
1352
  { this.addDef( { location: this.lb().location }, $query, 'excludingDict', '', this.identAst() ); }
1336
1353
  ( ',' | <exitLoop> )
1337
1354
  )+
1338
- '}'<setCondition=afterBrace>
1355
+ '}'<prepare=afterBrace>
1339
1356
  { this.finalizeDictOrArray( $query.excludingDict ); }
1340
1357
  ;
1341
1358
 
1342
1359
  selectItemsList[ query, start = undefined ]
1343
1360
  :
1344
- '{'<setCondition=notInExpandInline>
1361
+ '{'<prepare=inSelectItem, arg=top>
1345
1362
  { $query.columns = this.createArray( $start ); }
1346
1363
  (
1347
1364
  ( '*' { $query.columns.push( this.valueWithLocation() ); }
@@ -1349,13 +1366,13 @@ selectItemsList[ query, start = undefined ]
1349
1366
  )
1350
1367
  ( ',' | <exitLoop> )
1351
1368
  )*
1352
- '}'<setCondition=afterBrace>
1369
+ '}'<prepare=afterBrace>
1353
1370
  { this.finalizeDictOrArray( $query.columns ); }
1354
1371
  ;
1355
1372
 
1356
1373
  nestedSelectItemsList[ query, clause ]
1357
1374
  :
1358
- '{'<setCondition=inExpandInline>
1375
+ '{'<prepare=inSelectItem, arg=nested>
1359
1376
  { $query[$clause] = this.createArray(); }
1360
1377
  (
1361
1378
  ( '*' { $query[$clause].push( this.valueWithLocation() ); }
@@ -1363,7 +1380,7 @@ nestedSelectItemsList[ query, clause ]
1363
1380
  )
1364
1381
  ( ',' | <exitLoop> )
1365
1382
  )*
1366
- '}'<setCondition=afterBrace>
1383
+ '}'<prepare=afterBrace>
1367
1384
  { this.finalizeDictOrArray( $query[$clause] ); }
1368
1385
  ;
1369
1386
 
@@ -1372,9 +1389,9 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1372
1389
  :
1373
1390
  { $columns.push( $art ); } // TODO: probably too early
1374
1391
  { this.docComment( $art ); } annoAssignCol[ $art ]*
1375
- ( <cond=notInExpandInline> VIRTUAL
1392
+ ( <cond=modifierRestriction> VIRTUAL
1376
1393
  { $art.virtual = this.valueWithLocation( true ); } )?
1377
- ( <cond=notInExpandInline> KEY
1394
+ ( <cond=modifierRestriction> KEY
1378
1395
  { $art.key = this.valueWithLocation( true ); } )?
1379
1396
  (
1380
1397
  expr=expression { $art.value = $expr; }
@@ -1389,6 +1406,7 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1389
1406
  excludingClause[ $art ]?
1390
1407
  |
1391
1408
  '.'
1409
+ { this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
1392
1410
  { this.reportExpandInline( $art, $as || true ); }
1393
1411
  { if ($alias) $alias.token.parsedAs = $alias.parsedAs; }
1394
1412
  (
@@ -1423,7 +1441,6 @@ selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1423
1441
  | assoc=COMPOSITION { this.associationInSelectItem( $art ); }
1424
1442
  cardinality[ $art ]? OF
1425
1443
  )
1426
- // { this.classifyImplicitName( 'ItemAssoc', $art.value ); } TODO: do we need this?
1427
1444
  card=ONE/MANY? target=simplePath
1428
1445
  { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
1429
1446
  ON expr=condition { $art.on = $expr; }
@@ -1497,7 +1514,7 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
1497
1514
  ;
1498
1515
 
1499
1516
  // TODO: ? params
1500
- expression returns[ default expr ]
1517
+ expression returns[ default expr = {} ]
1501
1518
  //@finally{ if (!$expr?.$parens) this.attachLocation( $expr ); }
1502
1519
  :
1503
1520
  (
@@ -1576,7 +1593,7 @@ expression returns[ default expr ]
1576
1593
  | <prec=10, postfix=once> IS { $expr = this.applyOpToken( $expr ); }
1577
1594
  ( NOT { this.pushXprToken( $expr ); } )?
1578
1595
  NULL { this.pushXprToken( $expr ); }
1579
- | ( <cond=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1596
+ | ( <arg=10, cond=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1580
1597
  // TODO: condition, because there might be NOT NULL after DEFAULT expression
1581
1598
  | <prec=10, postfix=once>
1582
1599
  { $expr = { op: { val: 'ixpr', location: this.la().location }, args: [ $expr ] }; }
@@ -1601,19 +1618,18 @@ expression returns[ default expr ]
1601
1618
 
1602
1619
  expressionOrQueryParens returns[ default expr ]
1603
1620
  :
1604
- '('
1621
+ '(' <prepare=queryOnLeft>
1605
1622
  ( <priority> expressionOrQueryParens[ ...$ ]
1606
- ( <prec=-2, postfix> expression[ ...$ ]<atAltStart>
1623
+ ( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
1607
1624
  continueExpressionslist[ ...$ ]?
1608
- | <prec=-2, postfix>
1609
- continueExpressionslist[ ...$ ]
1610
- | <prec=-1, postfix> queryExpression[ ...$ ]<atAltStart>
1625
+ | continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
1626
+ | <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1611
1627
  )?
1612
- | <prec=-2> expression[ ...$ ]
1628
+ | expression[ ...$ ] <prepare=queryOnLeft, arg=expr>
1613
1629
  continueExpressionslist[ ...$ ]?
1614
- | <prec=-1> queryExpression[ ...$ ]
1630
+ | queryExpression[ ...$ ]
1615
1631
  )
1616
- ')' <setCondition=setPrecInCallingRule>
1632
+ ')'
1617
1633
  { this.surroundByParens( $expr ); }
1618
1634
  ;
1619
1635
 
@@ -1636,10 +1652,10 @@ newAndValuePath returns[ default expr ]
1636
1652
  { if ($e.op?.val !== 'ixpr') $expr.args.push( $e ); else $expr.args.push( ...e.args ); }
1637
1653
  ;
1638
1654
 
1639
- caseExpression returns[ default expr = { op: { val: 'ixpr' }, args: [] } ]
1655
+ caseExpression returns[ default expr ]
1640
1656
  @finally{ this.attachLocation( $expr ); }
1641
1657
  :
1642
- CASE { this.pushXprToken( $expr ); $expr.op.location = $expr.args[0].location; }
1658
+ CASE { $expr.op = { val: 'ixpr', location: this.lb().location }; $expr.args = []; this.pushXprToken( $expr ); }
1643
1659
  ( e=expression { $expr.args.push( $e ); } )?
1644
1660
  (
1645
1661
  WHEN { this.pushXprToken( $expr ); }
@@ -1654,7 +1670,7 @@ caseExpression returns[ default expr = { op: { val: 'ixpr' }, args: [] } ]
1654
1670
  END { this.pushXprToken( $expr ); }
1655
1671
  ;
1656
1672
 
1657
- castFunction returns[ default expr = {} ]
1673
+ castFunction returns[ default expr ]
1658
1674
  @finally{ this.attachLocation( $expr ); }
1659
1675
  :
1660
1676
  CAST { $expr.op = this.valueWithLocation(); }
@@ -1669,7 +1685,7 @@ argumentsAndFilter[ pathStep ]
1669
1685
  options{ minTokensMatched = 1 }
1670
1686
  :
1671
1687
  (
1672
- open='(' <setCondition=prepareSpecialFunction>
1688
+ open='(' <prepare=prepareSpecialFunction>
1673
1689
  { $pathStep.args = this.createArray(); }
1674
1690
  // action here, default action won't be executed with failed condition (TODO
1675
1691
  // TOOL? at least msg)
@@ -1678,7 +1694,7 @@ options{ minTokensMatched = 1 }
1678
1694
  (
1679
1695
  expr=funcExpression { $pathStep.args.push( $expr ); }
1680
1696
  (
1681
- ','<setCondition=nextFunctionArgument>
1697
+ ','<prepare=nextFunctionArgument>
1682
1698
  ( expr=funcExpression { $pathStep.args.push( $expr ); }
1683
1699
  | <exitLoop> // <cond>: only before `)`
1684
1700
  )
@@ -1750,12 +1766,14 @@ funcExpression returns[ default expr ] locals[ args ]
1750
1766
  )*
1751
1767
  ;
1752
1768
 
1769
+ // TODO: check Id_all - necessary if generic token is a reserved word?
1753
1770
  GenericExpr
1754
1771
  : Id_all | '*' ;
1755
1772
  GenericIntro
1756
1773
  : Id_all ;
1757
1774
  GenericSeparator
1758
- : Id_all ;
1775
+ : Id_restricted ; // otherwise expression ops use keyword prediction
1776
+ // TODO TOOL: use `<restrict=Id> GenericSeparator` instead and back to Id_all or Id
1759
1777
 
1760
1778
  overClause[ outer ] locals[ over = [] ]
1761
1779
  @finally{ $outer.push( this.surroundByParens( this.ixprAst( $over ) ) ); }
@@ -1862,7 +1880,7 @@ literalValue returns[ default expr = {} ]
1862
1880
  annoAssignStd[ art ]
1863
1881
  @finally{ this.docComment( $art ); }
1864
1882
  :
1865
- '@'<setCondition=annoInSameLine> { this.reportUnexpectedSpace(); }
1883
+ '@'<prepare=annoInSameLine> { this.reportUnexpectedSpace(); }
1866
1884
  ( annoAssignParen[ ...$ ]
1867
1885
  | annoAssignBase[ ...$ ]
1868
1886
  )
@@ -1880,7 +1898,7 @@ annoAssignCol[ art ]
1880
1898
  annoAssignMid[ art ]
1881
1899
  @finally{ this.docComment( $art ); }
1882
1900
  :
1883
- '@'<setCondition=annoInSameLine> { this.reportUnexpectedSpace(); }
1901
+ '@'<prepare=annoInSameLine> { this.reportUnexpectedSpace(); }
1884
1902
  ( annoAssignParen[ ...$ ]
1885
1903
  | name=annoNamePath // !
1886
1904
  { this.assignAnnotation( $art, {}, $name ); this.warnIfColonFollows( $name ); }
@@ -1889,7 +1907,7 @@ annoAssignMid[ art ]
1889
1907
 
1890
1908
  annoAssignParen[ art ]
1891
1909
  :
1892
- '('<setCondition=annoInSameLine>
1910
+ '('<prepare=annoInSameLine>
1893
1911
  ( annoAssignBase[ $art ]
1894
1912
  ( ',' | <exitLoop> )
1895
1913
  )*
@@ -1982,15 +2000,17 @@ annoValue returns[ default value = {} ]
1982
2000
  }
1983
2001
  ( ',' | <exitLoop> )
1984
2002
  )*
1985
- '}' // Do NOT use <setCondition=afterBrace> here!
2003
+ // ( <cond=TODO> '}' ) // TODO TOOL - workaround:
2004
+ { this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
2005
+ // Do NOT use <prepare=afterBrace> here!
1986
2006
  |
1987
- '['<setCondition=ellipsisRestriction>
2007
+ '['<prepare=arrayAnno>
1988
2008
  { $value.val = []; $value.literal = 'array' }
1989
2009
  // no need for createArray() here, $value.location is set above
1990
2010
  (
1991
2011
  ( sub=annoValue { $value.val.push( $sub ) }
1992
2012
  |
1993
- <cond=ellipsisRestriction> ellipsis='...'
2013
+ <cond=arrayAnno, arg=ellipsis> ellipsis='...'
1994
2014
  ( UP TO upTo=annoValue | { $upTo = undefined; } )
1995
2015
  { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
1996
2016
  )