@sap/cds-compiler 5.5.2 → 5.7.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 (57) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/bin/cdsse.js +4 -0
  3. package/bin/cdsv2m.js +2 -1
  4. package/doc/Versioning.md +4 -4
  5. package/lib/api/options.js +1 -0
  6. package/lib/base/builtins.js +2 -2
  7. package/lib/base/dictionaries.js +1 -2
  8. package/lib/base/keywords.js +3 -1
  9. package/lib/base/lazyload.js +1 -1
  10. package/lib/base/message-registry.js +170 -143
  11. package/lib/base/messages.js +69 -59
  12. package/lib/base/model.js +3 -3
  13. package/lib/base/node-helpers.js +17 -16
  14. package/lib/base/optionProcessorHelper.js +13 -14
  15. package/lib/base/shuffle.js +4 -1
  16. package/lib/checks/structuredAnnoExpressions.js +1 -1
  17. package/lib/compiler/assert-consistency.js +1 -1
  18. package/lib/compiler/builtins.js +4 -1
  19. package/lib/compiler/extend.js +20 -5
  20. package/lib/compiler/resolve.js +45 -9
  21. package/lib/compiler/shared.js +1 -0
  22. package/lib/edm/annotations/edmJson.js +3 -3
  23. package/lib/edm/annotations/genericTranslation.js +5 -1
  24. package/lib/edm/annotations/vocabularyDefinitions.js +8 -2
  25. package/lib/edm/edmUtils.js +2 -1
  26. package/lib/gen/BaseParser.js +142 -103
  27. package/lib/gen/CdlParser.js +2240 -2201
  28. package/lib/gen/Dictionary.json +185 -6
  29. package/lib/json/from-csn.js +2 -0
  30. package/lib/json/to-csn.js +13 -4
  31. package/lib/language/docCommentParser.js +11 -5
  32. package/lib/language/errorStrategy.js +3 -3
  33. package/lib/language/genericAntlrParser.js +2 -0
  34. package/lib/model/csnUtils.js +6 -1
  35. package/lib/optionProcessor.js +5 -1
  36. package/lib/parsers/AstBuildingParser.js +200 -86
  37. package/lib/parsers/CdlGrammar.g4 +142 -86
  38. package/lib/parsers/Lexer.js +5 -3
  39. package/lib/parsers/index.js +1 -1
  40. package/lib/render/toCdl.js +6 -5
  41. package/lib/render/toHdbcds.js +1 -1
  42. package/lib/render/toSql.js +5 -3
  43. package/lib/render/utils/common.js +19 -6
  44. package/lib/render/utils/standardDatabaseFunctions.js +576 -0
  45. package/lib/transform/addTenantFields.js +2 -1
  46. package/lib/transform/db/expansion.js +3 -0
  47. package/lib/transform/db/flattening.js +18 -77
  48. package/lib/transform/db/groupByOrderBy.js +2 -2
  49. package/lib/transform/db/rewriteCalculatedElements.js +14 -19
  50. package/lib/transform/db/temporal.js +2 -1
  51. package/lib/transform/forOdata.js +1 -1
  52. package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
  53. package/lib/transform/odata/createForeignKeys.js +25 -9
  54. package/lib/transform/odata/flattening.js +11 -1
  55. package/lib/transform/transformUtils.js +20 -85
  56. package/package.json +2 -1
  57. package/bin/cds_update_annotations.js +0 -180
@@ -29,6 +29,7 @@ tokens{ // reserved words
29
29
  ALL, ANY, AS,
30
30
  BY,
31
31
  CASE, CAST,
32
+ DISTINCT, // not entirely necessary
32
33
  EXISTS,
33
34
  FALSE, FROM,
34
35
  IN,
@@ -37,7 +38,7 @@ tokens{ // reserved words
37
38
  OF, ON,
38
39
  SELECT, SOME,
39
40
  TRUE,
40
- WHERE, WITH,
41
+ WHEN, WHERE, WITH,
41
42
  }
42
43
 
43
44
  // Top-level: USING, NAMESPACE, artifactDefOrExtend (start rule: start) ---------
@@ -45,22 +46,22 @@ tokens{ // reserved words
45
46
  start returns[ source = new XsnSource( 'cdl' ) ]
46
47
  :
47
48
  (
48
- ( <cond=namespaceRestriction> namespaceDeclaration[ $source ]
49
+ ( <guard=namespaceRestriction> namespaceDeclaration[ $source ]
49
50
  | usingDeclaration[ $source ]
50
51
  | artifactDefOrExtend[ $source ] <prepare=namespaceRestriction>
51
52
  )
52
- ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
53
+ ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
53
54
  )*
54
55
  EOF { this.docComment( null ); }
55
56
  ;
56
57
 
57
58
  artifactsBlock[ art, start = undefined ]
58
59
  :
59
- '{'<prepare=vocabularyRestriction>
60
+ '{'
60
61
  { $art.artifacts = this.createDict( $start ); $art.extensions = []; }
61
62
  (
62
63
  artifactDefOrExtend[ $art ]
63
- ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
64
+ ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
64
65
  )*
65
66
  '}'<prepare=afterBrace>
66
67
  { this.finalizeDictOrArray( $art.artifacts ); }
@@ -74,7 +75,7 @@ artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
74
75
  DEFINE?
75
76
  ( serviceDef[ $art, $outer ]
76
77
  | contextDef[ $art, $outer ]
77
- | <cond=vocabularyRestriction>annotationDef[ $art, $outer ]
78
+ | <guard=vocabularyRestriction> annotationDef[ $art, $outer ]
78
79
  | typeDef[ $art, $outer ]
79
80
  | aspectDef[ $art, $outer ]
80
81
  | entityDef[ $art, $outer ]
@@ -84,7 +85,7 @@ artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
84
85
  | functionMainDef[ $art, $outer ]
85
86
  )
86
87
  |
87
- <cond=extensionRestriction> EXTEND { $art.kind = 'extend'; }
88
+ <guard=extensionRestriction> EXTEND { $art.kind = 'extend'; }
88
89
  ( extendArtifact[ $art, $outer ]
89
90
  | extendService[ $art, $outer ]
90
91
  // Non-streamlined Syntax; we would neither add new clauses to them, nor
@@ -95,7 +96,7 @@ artifactDefOrExtend[ outer ] locals[ art = new XsnArtifact() ]
95
96
  | <hide> extendProjection[ $art, $outer ]
96
97
  )
97
98
  |
98
- <cond=extensionRestriction> ANNOTATE annotateArtifact[ $art, $outer ]
99
+ <guard=extensionRestriction> ANNOTATE annotateArtifact[ $art, $outer ]
99
100
  )
100
101
  ;
101
102
 
@@ -170,7 +171,8 @@ simplePath[ category = 'artref' ] returns[ default ref = {} ]
170
171
  serviceDef[ art, outer ]
171
172
  @finally{ this.attachLocation( $art ); }
172
173
  :
173
- SERVICE name=namePath[ 'Service' ]
174
+ SERVICE <prepare=vocabularyRestriction>
175
+ name=namePath[ 'Service' ]
174
176
  { this.addDef( $art, $outer, 'artifacts', 'service', $name ); }
175
177
  { this.docComment( $art ); } annoAssignMid[ $art ]*
176
178
  artifactsBlock[ $art ]?
@@ -179,7 +181,8 @@ serviceDef[ art, outer ]
179
181
  contextDef[ art, outer ]
180
182
  @finally{ this.attachLocation( $art ); }
181
183
  :
182
- CONTEXT name=namePath[ 'Context' ]
184
+ CONTEXT <prepare=vocabularyRestriction>
185
+ name=namePath[ 'Context' ]
183
186
  { this.addDef( $art, $outer, 'artifacts', 'context', $name ); }
184
187
  { this.docComment( $art ); } annoAssignMid[ $art ]*
185
188
  artifactsBlock[ $art ]?
@@ -189,7 +192,7 @@ annotationDef[ art, outer ]
189
192
  @finally{ this.attachLocation( $art ); }
190
193
  :
191
194
  ANNOTATION name=namePath[ 'AnnoDef' ]
192
- // make it also work with ignored <cond=vocabularyRestriction>:
195
+ // make it also work with ignored <guard=vocabularyRestriction>:
193
196
  { this.addDef( $art, $outer, ($outer.kind === 'source' ? 'vocabularies' : 'artifacts'), 'annotation', $name ); }
194
197
  { this.docComment( $art ); } annoAssignMid[ $art ]*
195
198
  typeOrIncludesSpec[ $art ]
@@ -232,7 +235,8 @@ aspectDef[ art, outer ]
232
235
  entityDef[ art, outer ]
233
236
  @finally{ this.attachLocation( $art ); }
234
237
  :
235
- ENTITY name=namePath[ 'Entity' ]
238
+ ENTITY<prepare=afterBrace> // enable special
239
+ name=namePath[ 'Entity' ]
236
240
  { this.addDef( $art, $outer, 'artifacts', 'entity', $name ); }
237
241
  { this.docComment( $art ); } annoAssignMid[ $art ]*
238
242
  paramsList[ $art ]?
@@ -254,6 +258,14 @@ entityDef[ art, outer ]
254
258
  { $art.query = $query; $art.$syntax = 'projection'; }
255
259
  whereGroupByHaving[ $query ]?
256
260
  orderByLimitOffset[ $query ]?
261
+ { if (this.lb().type !== '}' && ![ ';', '}', 'EOF' ].includes( this.l() ) && this.la().keyword !== 'actions') {
262
+ // not worth to nicer location here - is standard error in v6
263
+ this.warning( 'syntax-missing-proj-semicolon', this.la(),
264
+ { expecting: [ "';'" ], offending: this.antlrName( this.la() ) },
265
+ 'Missing $(EXPECTING) before $(OFFENDING)');
266
+ } }
267
+ <prepare=afterBrace> // disable special loop-exit, allow no `;`
268
+ // TODO v6: these <prepare=afterBrace>s are extremely strange
257
269
  )
258
270
  )
259
271
  actionsBlock[ $art ]?
@@ -336,7 +348,7 @@ actionsBlock[ art ]
336
348
  ACTIONS { $art.actions = this.createDict(); } '{'
337
349
  (
338
350
  boundActionFunctionDef[ $art ]
339
- ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
351
+ ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
340
352
  )*
341
353
  '}'<prepare=afterBrace>
342
354
  { this.finalizeDictOrArray( $art.actions ); }
@@ -402,7 +414,7 @@ elementsBlock[ art ]
402
414
  ( elementDef[ $art ]
403
415
  ( ';'
404
416
  | <exitLoop>
405
- | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
417
+ | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
406
418
  )
407
419
  )*
408
420
  '}'<prepare=afterBrace>
@@ -429,14 +441,14 @@ elementDef[ outer, art = undefined ]
429
441
  ':' typeExpression[ $art ] // includes DEFAULT
430
442
  )?
431
443
  (
432
- <cond=elementRestriction, arg=calc> '='
444
+ <guard=elementRestriction, arg=calc> '='
433
445
  // TODO TOOL: add to "expected set" if failing here? Or have some "do not
434
446
  // consider for rule exit if condition failure on `=`"?
435
447
  expr=expression { $art.value = $expr; }
436
448
  ( STORED { $art.value.stored = this.valueWithLocation( true ); } )?
437
449
  // TODO: why have `stored` as property of the value?
438
- { if (this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
439
- ( <cond=elementRestriction, arg=anno> annoAssignStd[ $art ] )*
450
+ { if (!this.elementRestriction( true, 'anno' )) this.docComment( $art ); }
451
+ ( <guard=elementRestriction, arg=anno> annoAssignStd[ $art ] )*
440
452
  )?
441
453
  ;
442
454
 
@@ -507,7 +519,8 @@ mixinElementDef[ outer ] locals[ art = new XsnArtifact() ]
507
519
  ( assoc=ASSOCIATION cardinality[ $art ]? TO
508
520
  | assoc=COMPOSITION cardinality[ $art ]? OF
509
521
  )
510
- card=ONE/MANY? target=simplePath
522
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
523
+ target=simplePath
511
524
  { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
512
525
  ON expr=condition { $art.on = $expr; }
513
526
  ;
@@ -526,7 +539,7 @@ annotateArtifact[ art, outer ]
526
539
  annotateElementsBlock[ $art ]?
527
540
  | // definition annotation
528
541
  keyword=WITH?
529
- // <cond=noRuleExitAfterWith>), or as rule option,
542
+ // <guard=noRuleExitAfterWith>), or as rule option,
530
543
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
531
544
  { this.addExtension( $art, $outer, 'annotate', $name ); }
532
545
  { this.docComment( $art ); } annoAssignStd[ $art ]*
@@ -597,7 +610,7 @@ extendService[ art, outer ]
597
610
  name=namePath[ 'ExtService' ]
598
611
  { $art.name = $name; $outer.extensions.push( $art ); }
599
612
  keyword=WITH?
600
- // <cond=noRuleExitAfterWith>), or as rule option,
613
+ // <guard=noRuleExitAfterWith>), or as rule option,
601
614
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
602
615
  { this.docComment( $art ); } annoAssignStd[ $art ]*
603
616
  artifactsBlock[ $art ]?
@@ -610,7 +623,7 @@ extendContext[ art, outer ]
610
623
  name=namePath[ 'ExtContext' ]
611
624
  { $art.name = $name; $outer.extensions.push( $art ); }
612
625
  keyword=WITH?
613
- // <cond=noRuleExitAfterWith>), or as rule option,
626
+ // <guard=noRuleExitAfterWith>), or as rule option,
614
627
  // this.noSemicolonHere() had the issues: DocComment, before `}`/EOF
615
628
  { this.docComment( $art ); } annoAssignStd[ $art ]*
616
629
  artifactsBlock[ $art ]?
@@ -680,7 +693,7 @@ annotateActionsBlock[ art ]
680
693
  :
681
694
  ACTIONS { $art.actions = this.createDict(); } '{'
682
695
  ( annotateBoundAction[ $art ]
683
- ( ';' | <exitLoop> | <repeatLoop, cond=afterBrace> { this.noAssignmentInSameLine(); } )
696
+ ( ';' | <exitLoop> | <repeatLoop, guard=afterBrace> { this.noAssignmentInSameLine(); } )
684
697
  )*
685
698
  '}'<prepare=afterBrace>
686
699
  { this.finalizeExtensionsDict( $art.actions ); }
@@ -731,7 +744,7 @@ annotateElementsBlock[ art ]
731
744
  ( annotateElement[ $art ]
732
745
  ( ';'
733
746
  | <exitLoop>
734
- | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
747
+ | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); }
735
748
  )
736
749
  )*
737
750
  '}'<prepare=afterBrace>
@@ -754,7 +767,7 @@ extendElementsBlock[ art, start = undefined ]
754
767
  ( elementDefOrExtend[ $art ]
755
768
  ( ';'
756
769
  | <exitLoop>
757
- | <repeatLoop, cond=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
770
+ | <repeatLoop, guard=afterBrace, restrict=Id> { this.noAssignmentInSameLine(); } )
758
771
  )*
759
772
  '}'<prepare=afterBrace>
760
773
  { this.finalizeExtensionsDict( $art.elements ); }
@@ -837,7 +850,7 @@ typeOrIncludesSpec[ art ]
837
850
  // annotations are attached to the element, the type properties to the line type
838
851
  //
839
852
  // If used in a definition with additional clauses (currently just `= expr` for
840
- // elements), these clauses must be guarded with <cond=…>.
853
+ // elements), these clauses must be guarded with <guard=…>.
841
854
  //
842
855
  // This rule is for element, type, (input and `returns`) parameter and annotation
843
856
  // definitions. It is not used when the type expression is restricted: CDL-style
@@ -848,15 +861,15 @@ typeExpression[ art ]
848
861
  ( typeRefOptArgs[ $art ] | typeTypeOf[ $art ] )
849
862
  (<altRuleStart>)
850
863
  { this.docComment( $art ); } annoAssignStd[ $art ]*
851
- ( <cond=elementRestriction, arg=notNull> nullability[ $art ]
864
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art ]
852
865
  { this.docComment( $art ); } annoAssignStd[ $art ]*
853
866
  )?
854
867
  ( enumSymbolsBlock[ $art ] <prepare=elementRestriction, arg=anno>
855
- ( <cond=elementRestriction, arg=notNull> nullability[ $art ] )?
856
- ( <cond=elementRestriction, arg=default>
868
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
869
+ ( <guard=elementRestriction, arg=default>
857
870
  DEFAULT expr=expression { $art.default = $expr; }
858
871
  )?
859
- ( <cond=elementRestriction, arg=notNull> nullability[ $art ] )?
872
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art ] )?
860
873
  | typeProperties[ $art ]
861
874
  )?
862
875
  |
@@ -866,11 +879,13 @@ typeExpression[ art ]
866
879
  typeProperties[ $art ]?
867
880
  |
868
881
  assoc=ASSOCIATION <prepare=elementRestriction, arg=calc>
869
- cardinality[ $art ]? TO card=ONE/MANY?
882
+ cardinality[ $art ]? TO
883
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
870
884
  typeAssocProperties[ $art, $assoc, $card ]
871
885
  |
872
886
  assoc=COMPOSITION <prepare=elementRestriction, arg=calc>
873
- cardinality[ $art ]? OF card=ONE/MANY?
887
+ cardinality[ $art ]? OF
888
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
874
889
  ( typeAssocProperties[ $art, $assoc, $card ]
875
890
  | elementsBlock[ this.setAssocAndComposition( $art, $assoc, $card ) ]
876
891
  { $art.target.location = $art.target.elements[Symbol.for('cds.$location')]; }
@@ -883,7 +898,7 @@ typeExpression[ art ]
883
898
  ) // no anno assignments, except to end type expression
884
899
  (
885
900
  ( typeRefOptArgs[ $art.items ] | typeTypeOf[ $art.items ] )
886
- ( <cond=elementRestriction, arg=notNull> nullability[ $art.items ] )?
901
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
887
902
  ( { this.docComment( $art ); } annoAssignStd[ $art ]*
888
903
  { ; } <exitRule> // TODO TOOL: make it work without workaround { ; }
889
904
  // TODO TOOL: investigate why simply `{} <exitRule>` is ignored
@@ -892,7 +907,7 @@ typeExpression[ art ]
892
907
  |
893
908
  elementsBlock[ $art.items ]
894
909
  )
895
- ( <cond=elementRestriction, arg=notNull> nullability[ $art.items ] )?
910
+ ( <guard=elementRestriction, arg=notNull> nullability[ $art.items ] )?
896
911
  |
897
912
  elementsBlock[ $art ] <prepare=elementRestriction, arg=calc>
898
913
  nullability[ $art ]?
@@ -915,16 +930,17 @@ typeProperties[ art ]
915
930
  (
916
931
  annoAssignStd[ $art ]
917
932
  |
918
- <cond=elementRestriction, arg=notNull>
933
+ <guard=elementRestriction, arg=notNull>
919
934
  nullability[ $art ] { this.docComment( $art ); }
920
935
  |
921
- <cond=elementRestriction, arg=default>
936
+ <guard=elementRestriction, arg=default>
922
937
  DEFAULT expr=expression { $art.default = $expr; this.docComment( $art ); }
923
938
  )+
924
939
  ;
925
940
 
926
941
 
927
942
  typeTypeOf[ art ] locals[ location ]
943
+ @after{ this.attachLocation( $art.type ); }
928
944
  :
929
945
  TYPE OF { location = this.locationOfPrevTokens( 2 ); }
930
946
  type=simplePath[ 'ref' ] { $art.type = $type; }
@@ -951,6 +967,7 @@ typeRefOptArgs[ art ] locals[ type = $art.type ]
951
967
  (
952
968
  '.' Id_all['ref'] { $type.path.push( this.identAst() ); }
953
969
  )*
970
+ { this.attachLocation( $art.type ); }
954
971
  |
955
972
  open='('
956
973
  (
@@ -989,6 +1006,7 @@ typeNamedArgsList[ art ]
989
1006
 
990
1007
  typeNamedArg[ art ]
991
1008
  :
1009
+ // TODO: or keywords with guards for better code completion?
992
1010
  name=Id['typeparamname']
993
1011
  ':'
994
1012
  ( Number
@@ -1054,13 +1072,11 @@ queryEOF returns[ query ]
1054
1072
  projectionSpec returns[ default query = {} ]
1055
1073
  @finally{ this.attachLocation($query); }
1056
1074
  :
1057
- // TODO, currently just with simple ref
1058
- PROJECTION { $query = { op: this.valueWithLocation( 'SELECT' ) }; }
1075
+ PROJECTION
1076
+ <prepare=afterBrace, arg=entity> // enable special loop-exit, TODO v6 delete
1077
+ { $query = { op: this.valueWithLocation( 'SELECT' ) }; }
1059
1078
  ON
1060
1079
  tab=fromRefWithOptAlias
1061
- // TODO: this <prepare=afterBrace> is extremely strange... v6 forbid.
1062
- // Deliberately set this via action (→ interpreter will not accept this)
1063
- { this.afterBrace(); }<always>
1064
1080
  { $query.from = tab; }
1065
1081
  selectItemsList[ $query ]?
1066
1082
  excludingClause[ $query ]?
@@ -1084,7 +1100,7 @@ queryExpression returns[ default expr = {} ] locals[ op, quantifier ]
1084
1100
  )
1085
1101
  query=queryExpression
1086
1102
  // with same op/quantifier: make left-assoc binary to nary:
1087
- { if ($expr.$parens || $op.val !== $expr.op.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier }; }
1103
+ { if ($expr.$parens || $op.val !== $expr.op.val || $quantifier?.val !== $expr.quantifier?.val) $expr = { op, args: [$expr], quantifier, location: { ...$.expr.location } }; } // TODO: ...$
1088
1104
  { $quantifier = undefined; }
1089
1105
  { $expr.args.push( $query ); this.attachLocation( $expr ); }
1090
1106
  )*
@@ -1177,16 +1193,16 @@ tableOrQueryParens returns[ default expr ]
1177
1193
  '(' <prepare=queryOnLeft>
1178
1194
  ( <prefer> tableOrQueryParens[ ...$ ]
1179
1195
  ( tableExpression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=table>
1180
- | <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1196
+ | <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1181
1197
  )?
1182
1198
  | tableExpression[ ...$ ] <prepare=queryOnLeft, arg=table>
1183
1199
  | queryExpression[ ...$ ]
1184
1200
  )
1185
1201
  ')'
1186
1202
  { this.surroundByParens( $expr ); }
1187
- ( <cond=queryOnLeft, arg=table> AS Id['FromAlias']
1203
+ ( <guard=queryOnLeft, arg=table> AS Id['FromAlias']
1188
1204
  { $expr = this.taggedIfQuery( $expr ); $expr.name = this.identAst(); }
1189
- | <cond=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
1205
+ | <guard=queryOnLeft, arg=tableWithoutAs> Id_restricted['FromAlias']
1190
1206
  // TODO TOOL: shouldn't we have generated `default: this.giR()`?
1191
1207
  { $expr = this.taggedIfQuery( $expr ); $expr.name = this.fragileAlias(); }
1192
1208
  )?
@@ -1225,8 +1241,7 @@ fromRefWithOptAlias returns[ default expr = {} ]
1225
1241
  (
1226
1242
  AS Id['FromAlias'] { $expr.name = this.identAst(); }
1227
1243
  |
1228
- <cond=tableWithoutAs>
1229
- // TODO: probably not necessary, TOOL already uses `default: this.giR()`
1244
+ // <guard=tableWithoutAs> not necessary, tool uses `default: this.giR()`
1230
1245
  Id_restricted['FromAlias']
1231
1246
  { $expr.name = this.fragileAlias(); }
1232
1247
  |
@@ -1241,6 +1256,7 @@ fromPath[ table, category ] locals[ pathItem ]
1241
1256
  Id[ $category ] { $table.path.push( $pathItem = this.identAst() ); }
1242
1257
  ( fromArgumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1243
1258
  (
1259
+ <guard=notAfterEntityArgOrFilter> // TODO TOOL: allow <hide=method>
1244
1260
  '.' { if (!$pathItem && !$table.scope) {
1245
1261
  $table.scope = $table.path.length; $category = 'ref';
1246
1262
  this.warning( 'syntax-invalid-path-separator', this.lb(),
@@ -1274,7 +1290,7 @@ fromNamedArgument[ pathStep ]
1274
1290
  cardinalityAndFilter[ pathStep ]
1275
1291
  :
1276
1292
  '['
1277
- ( <cond=beforeColon> Number // TODO: only allow `1`?
1293
+ ( <guard=beforeColon> Number // TODO: only allow `1`?
1278
1294
  { $pathStep.cardinality = { targetMax: this.unsignedIntegerLiteral(), location: this.lb().location }; }
1279
1295
  ':'
1280
1296
  )?
@@ -1338,7 +1354,7 @@ selectItemsList[ query, start = undefined ]
1338
1354
 
1339
1355
  nestedSelectItemsList[ query, clause ]
1340
1356
  :
1341
- '{'<prepare=inSelectItem, arg=nested>
1357
+ '{'<prepare=inSelectItem>
1342
1358
  { $query[$clause] = this.createArray(); }
1343
1359
  (
1344
1360
  ( '*' { $query[$clause].push( this.valueWithLocation() ); }
@@ -1350,64 +1366,104 @@ nestedSelectItemsList[ query, clause ]
1350
1366
  { this.finalizeDictOrArray( $query[$clause] ); }
1351
1367
  ;
1352
1368
 
1353
- selectItemDef[ columns ] locals[ art = new XsnArtifact(), alias ]
1369
+ selectItemDef[ columns ] locals[ art = new XsnArtifact() ]
1354
1370
  @finally{ this.attachLocation( $art ); }
1355
1371
  :
1356
1372
  { $columns.push( $art ); } // TODO: probably too early
1357
1373
  { this.docComment( $art ); } annoAssignCol[ $art ]*
1358
- ( <cond=modifierRestriction> VIRTUAL
1359
- { $art.virtual = this.valueWithLocation( true ); } )?
1360
- ( <cond=modifierRestriction> KEY
1361
- { $art.key = this.valueWithLocation( true ); } )?
1374
+ ( <guard=modifierRestriction> VIRTUAL
1375
+ { $art.virtual = this.valueWithLocation( true ); }
1376
+ )?
1377
+ {;} <prepare=columnExpr, arg=key> // TOOL TODO: disappears without {;}
1378
+ ( <guard=modifierRestriction> KEY
1379
+ { $art.key = this.valueWithLocation( true ); }
1380
+ )?
1381
+ // TODO: we might have an extra rule for column expression...
1362
1382
  (
1363
- expr=expression { $art.value = $expr; }
1364
- ( as=AS Id['ItemAlias'] { $art.name = this.identAst(); }
1365
- | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
1366
- | { $alias = this.classifyImplicitName( 'ItemImplicit', $expr ); }
1367
- )
1368
- // TODO: <cond> instead `reportExpandInline` for "expand/inline only w/ ref"
1369
1383
  (
1370
- { this.reportExpandInline( $art, false ); }
1371
- nestedSelectItemsList[ $art, 'expand' ]
1372
- excludingClause[ $art ]?
1384
+ expr=expression { $art.value = $expr; }
1385
+ ( AS Id['ItemAlias'] { $art.name = this.identAst(); }
1386
+ | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
1387
+ )?
1373
1388
  |
1374
- '.'
1375
- { this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
1376
- { this.reportExpandInline( $art, $as || true ); }
1377
- { if ($alias) $alias.token.parsedAs = $alias.parsedAs; }
1389
+ <prefer>
1390
+ expr=valuePath { $expr = this.valuePathAst( $expr ); $art.value = $expr; }
1378
1391
  (
1379
- nestedSelectItemsList[ $art, 'inline' ]
1380
- excludingClause[ $art ]?
1392
+ (
1393
+ OVER { this.pushXprToken( $expr.suffix = [] ); }
1394
+ overClause[ $expr.suffix ]
1395
+ ( e=expression[ ...{ expr: $expr } ]<atAltStart>
1396
+ { Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
1397
+ )?
1398
+ |
1399
+ e=expression[ ...{ expr: $expr } ]<atAltStart>
1400
+ { Object.assign( $e.location, $expr.location ); $art.value = this.attachLocation( $e )}
1401
+ )
1402
+ ( AS Id['ItemAlias'] { $art.name = this.identAst(); }
1403
+ | Id_restricted['ItemAlias'] { $art.name = this.fragileAlias( true ); }
1404
+ )?
1405
+ | // includes empty
1406
+ AS Id['ItemAlias'] <prepare=nestedExpand> { $art.name = this.identAst(); }
1381
1407
  |
1382
- '*' { $art.inline = [ this.valueWithLocation() ]; }
1408
+ Id_restricted['ItemAlias'] <prepare=nestedExpand>
1409
+ { $art.name = this.fragileAlias( true ); }
1410
+ |
1411
+ { this.classifyImplicitName( 'ItemImplicit', $expr ); } <prepare=nestedExpand>
1412
+ // TODO TOOL: action in default does not work in embedded action
1413
+ |
1414
+ // TODO: guard instead reportExpandInline if valuePath is function?
1415
+ '.'
1416
+ { this.reportUnexpectedSpace( this.lb(), this.la().location, true ); } // TODO: no ERR
1417
+ { this.reportExpandInline( $art, true ); }
1418
+ // no extra 'syntax-unexpected-alias' anymore,
1419
+ // 'syntax-unexpected-anno' reported in define.js
1420
+ (
1421
+ nestedSelectItemsList[ $art, 'inline' ]
1422
+ excludingClause[ $art ]?
1423
+ |
1424
+ '*' { $art.inline = [ this.valueWithLocation() ]; }
1425
+ )
1426
+ <exitRule>
1383
1427
  )
1428
+ )
1429
+ // expand is only allowed only after valuePath, but error recovery is poor
1430
+ // if the following is moved up to <prepare=nestedExpand>
1431
+ (
1432
+ // TODO: make guard handle that if valuePath might be function?
1433
+ <guard=nestedExpand>
1434
+ { if (!this.nestedExpand(true)) this.reportExpandInline( $art, false ); }
1435
+ nestedSelectItemsList[ $art, 'expand' ]
1436
+ excludingClause[ $art ]?
1384
1437
  )?
1385
1438
  |
1386
1439
  nestedSelectItemsList[ $art, 'expand' ]
1387
1440
  excludingClause[ $art ]?
1388
1441
  AS Id['ItemAlias'] { $art.name = this.identAst(); }
1389
1442
  )
1390
- { this.docComment( $art ); } annoAssignMid[ $art ]*
1443
+ { this.docComment( $art ); } <prepare=columnExpr> annoAssignMid[ $art ]*
1391
1444
  (
1392
- ':'
1445
+ ':' // TODO: guard? currently not with expand ?
1393
1446
  (
1394
1447
  ( typeTypeOf[ $art ]
1395
1448
  | ( LOCALIZED { $art.localized = this.valueWithLocation( true ); } )?
1396
1449
  typeRefOptArgs[ $art ]
1397
1450
  )
1398
1451
  |
1452
+ // TODO: guard for ref-only expression ?
1399
1453
  REDIRECTED TO target=simplePath { $art.target = $target; }
1400
1454
  ( ON cond=condition { $art.on = $cond; }
1401
1455
  | foreignKeysBlock[ $art ]
1402
1456
  )?
1403
1457
  |
1404
- // TODO: condition for this
1405
- ( assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
1458
+ ( <guard=columnExpr> // arg=singleId
1459
+ assoc=ASSOCIATION { this.associationInSelectItem( $art ); }
1406
1460
  cardinality[ $art ]? TO
1407
- | assoc=COMPOSITION { this.associationInSelectItem( $art ); }
1461
+ | <guard=columnExpr> // arg=singleId
1462
+ assoc=COMPOSITION { this.associationInSelectItem( $art ); }
1408
1463
  cardinality[ $art ]? OF
1409
1464
  )
1410
- card=ONE/MANY? target=simplePath
1465
+ ( <guard=noRepeatedCardinality> card=ONE/MANY )?
1466
+ target=simplePath
1411
1467
  { this.setAssocAndComposition( $art, $assoc, $card, $target ); }
1412
1468
  ON expr=condition { $art.on = $expr; }
1413
1469
  )
@@ -1471,7 +1527,7 @@ valuePath returns[ default expr = { path: [] } ] locals[ pathItem ]
1471
1527
  ( argumentsAndFilter[ $pathItem ] )?
1472
1528
  (<altRuleStart>)
1473
1529
  (
1474
- <cond=isDotForPath> '.'
1530
+ <guard=isDotForPath> '.'
1475
1531
  Id_all['ref'] { $expr.path.push( $pathItem = this.identAst() ); }
1476
1532
  ( argumentsAndFilter[ $pathItem ] { $pathItem = null; } )?
1477
1533
  )*
@@ -1557,7 +1613,7 @@ expression returns[ default expr = {} ]
1557
1613
  | <prec=10, postfix=once> IS { $expr = this.applyOpToken( $expr ); }
1558
1614
  ( NOT { this.pushXprToken( $expr ); } )?
1559
1615
  NULL { this.pushXprToken( $expr ); }
1560
- | ( <arg=10, cond=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1616
+ | ( <arg=10, guard=isNegatedRelation> NOT { $expr = this.applyOpToken( $expr ); }
1561
1617
  // TODO: condition, because there might be NOT NULL after DEFAULT expression
1562
1618
  | <prec=10, postfix=once>
1563
1619
  { $expr = { op: { val: 'ixpr', location: this.la().location }, args: [ $expr ] }; }
@@ -1587,7 +1643,7 @@ expressionOrQueryParens returns[ default expr ]
1587
1643
  ( expression[ ...$ ]<atAltStart, prepare=queryOnLeft, arg=expr>
1588
1644
  continueExpressionslist[ ...$ ]?
1589
1645
  | continueExpressionslist[ ...$ ] <prepare=queryOnLeft, arg=expr>
1590
- | <cond=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1646
+ | <guard=queryOnLeft> queryExpression[ ...$ ]<atAltStart>
1591
1647
  )?
1592
1648
  | expression[ ...$ ] <prepare=queryOnLeft, arg=expr>
1593
1649
  continueExpressionslist[ ...$ ]?
@@ -1674,7 +1730,7 @@ options{ minTokensMatched = 1 }
1674
1730
  // TODO: if we want perfect CC and error recovery, `isNamedArg` would work
1675
1731
  // like keyword prediction and also do something similar to the (future)
1676
1732
  // "identifier confirmation prediction" (consider argument `(par ‡ : 42)`
1677
- <cond=isNamedArg> id=Id_all['paramname']
1733
+ <guard=isNamedArg> id=Id_all['paramname']
1678
1734
  (
1679
1735
  ':' { $pathStep.args = this.createDict( $open ); $pathStep.$syntax = ':'; }
1680
1736
  expr=expression { this.addNamedArg( $pathStep, $id, $expr ); }
@@ -1815,7 +1871,7 @@ literalValue returns[ default expr = {} ]
1815
1871
  :
1816
1872
  // TODO: remove from this rule (not in enum! `String enum { foo = #bar }`) ?
1817
1873
  '#' { this.reportUnexpectedSpace(); }
1818
- Id['enumref']
1874
+ Id_all['enumref']
1819
1875
  { $expr = { literal: 'enum', sym: this.identAst() } }
1820
1876
  |
1821
1877
  NULL
@@ -1882,7 +1938,7 @@ annoAssignBase[ art ] locals[ value = {} ]
1882
1938
  @finally{ this.assignAnnotation( $art, $value, $name || {} ); }
1883
1939
  :
1884
1940
  name=annoNamePath
1885
- ( <cond=annoInSameLine> ':' value=annoValue )?
1941
+ ( <guard=annoInSameLine> ':' value=annoValue )?
1886
1942
  ;
1887
1943
 
1888
1944
  annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
@@ -1897,7 +1953,7 @@ annoNamePath[ category = 'anno' ] returns[ default name = new XsnName() ]
1897
1953
  Id_all[ $category ] { $name.path.push( this.identAstWithPrefix( $at ) ); }
1898
1954
  )
1899
1955
  )*
1900
- ( <cond=annoInSameLine> annoPathVariant[ $name ] )?
1956
+ ( <guard=annoInSameLine> annoPathVariant[ $name ] )?
1901
1957
  ;
1902
1958
 
1903
1959
  annoPath[ nameOrRef, category = 'annoref' ]
@@ -1955,7 +2011,7 @@ annoValue returns[ default value = {} ]
1955
2011
  }
1956
2012
  (
1957
2013
  // TODO: complain about empty loop if top-level as before
1958
- // TOOL TODO → allow `<cond=…> '}'` below where the condition rejects `}`
2014
+ // TOOL TODO → allow `<guard=…> '}'` below where the condition rejects `}`
1959
2015
  // after `{` if top-level
1960
2016
  sub=annoStructValue
1961
2017
  {
@@ -1965,7 +2021,7 @@ annoValue returns[ default value = {} ]
1965
2021
  ( ',' | <exitLoop> )
1966
2022
  )*
1967
2023
  // TODO TOOL: allow:
1968
- // ( <cond=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
2024
+ // ( <guard=arrayAnno, …> '}' | <error> ) // TODO TOOL - workaround:
1969
2025
  { this.ec( 'arrayAnno', 'orNotEmpty' ); } '}'
1970
2026
  // Do NOT use <prepare=afterBrace> here!
1971
2027
  |
@@ -1975,7 +2031,7 @@ annoValue returns[ default value = {} ]
1975
2031
  (
1976
2032
  ( sub=annoValue { $value.val.push( $sub ) }
1977
2033
  |
1978
- <cond=arrayAnno, arg=ellipsis> ellipsis='...'
2034
+ <guard=arrayAnno, arg=ellipsis> ellipsis='...'
1979
2035
  ( UP TO upTo=annoValue
1980
2036
  { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location, upTo: $upTo } ); }
1981
2037
  | { $value.val.push( { literal: 'token', val: '...', location: $ellipsis.location } ); }
@@ -1987,7 +2043,7 @@ annoValue returns[ default value = {} ]
1987
2043
  )
1988
2044
  ( ',' | <exitLoop> )
1989
2045
  )*
1990
- // TODO TOOL: allow ( <cond=arrayAnno, arg=bracket> ']' )
2046
+ // TODO TOOL: allow ( <guard=arrayAnno, arg=bracket> ']' )
1991
2047
  { this.ec( 'arrayAnno', 'bracket' ); }<always>
1992
2048
  ']'
1993
2049
  |
@@ -95,7 +95,7 @@ class Lexer {
95
95
  tokenIndex: parser.tokens.length + parser.docComments.length + parser.comments.length,
96
96
  };
97
97
  let keyword;
98
- if (typeof type !== 'function' ||
98
+ if (typeof type !== 'function' || // eslint-disable-next-line sonarjs/no-nested-assignment
99
99
  ([ type, text, keyword ] = type( text, this, parser, pos )) && type) {
100
100
  parser.tokens.push( {
101
101
  __proto__: Token.prototype,
@@ -200,7 +200,8 @@ function string( text, lexer, parser, beg ) {
200
200
  // TODO: set parsedAs to 0 → no further error if string is not expected?
201
201
  prefix = null; // no combination with date/time/…
202
202
  }
203
- adaptEndLocation( lexer, (rulesRegexp.lastIndex = lastIndex || lexer.input.length) );
203
+ rulesRegexp.lastIndex = lastIndex || lexer.input.length;
204
+ adaptEndLocation( lexer, rulesRegexp.lastIndex );
204
205
 
205
206
  if (!prefix)
206
207
  return [ 'String', lexer.input.substring( beg, rulesRegexp.lastIndex ), keyword ];
@@ -233,7 +234,8 @@ function ident( text, lexer, parser, beg ) {
233
234
  keyword = 0;
234
235
  // TODO: set parsedAs to 0 → no further error if string is not expected?
235
236
  }
236
- adaptEndLocation( lexer, (rulesRegexp.lastIndex = lastIndex || lexer.input.length) );
237
+ rulesRegexp.lastIndex = lastIndex || lexer.input.length;
238
+ adaptEndLocation( lexer, rulesRegexp.lastIndex );
237
239
  return [ 'Id', lexer.input.substring( beg, rulesRegexp.lastIndex ), keyword ];
238
240
  }
239
241
 
@@ -21,7 +21,7 @@ function parseCdl( source, filename = '<undefined>.cds',
21
21
  options = {}, messageFunctions = null,
22
22
  rule = 'cdl' ) {
23
23
  const rulespec = rules[rule];
24
- if (!options.newParser)
24
+ if (!options.newParser) // TODO: (options.newParser === false)
25
25
  return parseWithAntlr( source, filename, options, messageFunctions, rulespec );
26
26
  const { CdlParser } = gen;
27
27
  if (CdlParser.tracingParser) // tracing → direct console output of message